desktop/source/lib/init.cxx                  |   24 +++++++++++++++++++++---
 include/vcl/ITiledRenderable.hxx             |    9 +++++++++
 sc/inc/docuno.hxx                            |    3 +++
 sc/inc/scextopt.hxx                          |    1 +
 sc/qa/unit/tiledrendering/tiledrendering.cxx |   18 ++++++++++++++++++
 sc/source/filter/excel/xeview.cxx            |    4 +++-
 sc/source/ui/inc/viewdata.hxx                |    5 +++++
 sc/source/ui/unoobj/docuno.cxx               |   13 +++++++++++++
 sc/source/ui/view/viewdata.cxx               |   11 +++++++++++
 xmloff/source/core/xmlexp.cxx                |    7 +++++++
 10 files changed, 91 insertions(+), 4 deletions(-)

New commits:
commit 22dbeb3505af9cf7e7d4a07412616652ade67dcd
Author:     Mohit Marathe <mohit.mara...@collabora.com>
AuthorDate: Thu Jul 31 19:48:12 2025 +0530
Commit:     Pedro Silva <pedro.si...@collabora.com>
CommitDate: Tue Aug 12 13:35:00 2025 +0200

    cool#4250 LOK calc export: use LOK Zoom via ScViewData
    
    Justin Luth's comment on the original patch (to which I made
    some minor tweaks to adapt to the new simplified API):
    
    "The zoom values set in the view properties by LibreOfficeKit
    are technical zoom levels that depend on the current screen DPI.
    They do not reflect the logical zoom level that the user sees.
    This has been especially problematic for high DPI screens,
    where the technical-zoom-level is completely different from
    the human-zoom-level - and thus a weird value is exported.
    
    Instead, export the zoom value that LOK presents to the human.
    
    Since LOK doc_setClientZoom  always calls setZoom for bAll sheets,
    it is sufficient to only have a single export zoom variable.
    
    COOL doesn't have a "View - Page break" view,
    so PageZoomValue is irrelevant.
    Of course, even in desktop mode it seems irrelevant to me...
    
    I'm rather worried about the "IsLOKExport" flag interferring
    with normal, synchronous operations
    during a background/download ODT save.
    Those WriteUserDataSequence are not at all
    limited to file-save operations.
    They just fill in property values
    for 'whatever' wants to capture the view settings.
    So if some other process runs in parallel with the save,
    then it will get the export zoom values,
    rather than the operational zoom values it was expecting.
    
    But I don't see any way around it since xmloff
    just dumps properties as the way that it saves an ODS.
    
    make CppunitTest_sc_tiledrendering \
    CPPUNIT_TEST_NAME=testCellInvalidationDocWithExistingZoom"
    
    Signed-off-by: Mohit Marathe <mohit.mara...@collabora.com>
    Co-authored-by: Justin Luth <justin.l...@collabora.com>
    Change-Id: I54282e4d28dbe489ca0ea7c080036391faa4704c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188674
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@collabora.com>

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index a4b63b1861df..ff66953c032e 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -3897,7 +3897,6 @@ static int doc_saveAs(LibreOfficeKitDocument* pThis, 
const char* sUrl, const cha
             aSaveMediaDescriptor[MediaDescriptor::PROP_INTERACTIONHANDLER] <<= 
uno::Reference<task::XInteractionHandler2>(pInteraction);
         }
 
-
         if (bTakeOwnership)
             xStorable->storeAsURL(aURL, 
aSaveMediaDescriptor.getAsConstPropertyValueList());
         else
@@ -5821,9 +5820,28 @@ static bool doc_renderNextSlideLayer(
     return bDone;
 }
 
-static void doc_setViewOption(LibreOfficeKitDocument* /*pDoc*/, const char* 
/*pOption*/, const char* /*pValue*/)
+static void doc_setViewOption(LibreOfficeKitDocument* pThis, const char* 
pOption, const char* pValue)
 {
-    // placeholder for now
+    comphelper::ProfileZone aZone("doc_setViewOption");
+
+    ITiledRenderable* pDoc = getTiledRenderable(pThis);
+    if (!pDoc)
+    {
+        SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr);
+        return;
+    }
+
+    SolarMutexGuard aGuard;
+    SetLastExceptionMsg();
+
+    const OUString sOption = getUString(pOption);
+    if (sOption == "zoom")
+    {
+        const int nZoom = getUString(pValue).toInt32();
+
+        if (nZoom)
+            pDoc->setExportZoom(nZoom);
+    }
 }
 
 static bool getFromTransferable(
diff --git a/include/vcl/ITiledRenderable.hxx b/include/vcl/ITiledRenderable.hxx
index fa42c077df5f..3e02f1506a0a 100644
--- a/include/vcl/ITiledRenderable.hxx
+++ b/include/vcl/ITiledRenderable.hxx
@@ -257,6 +257,15 @@ public:
                                int /*nTileTwipHeight*/)
     {}
 
+    /**
+     * Provide the zoom level that will used during save/export
+     *
+     * @param nExportZoom - the zoom level as a percent
+     */
+    virtual void setExportZoom(int /*nExportZoom*/)
+    {
+    }
+
     /// @see lok::Document::setClientVisibleArea().
     virtual void setClientVisibleArea(const tools::Rectangle& /*rRectangle*/)
     {
diff --git a/sc/inc/docuno.hxx b/sc/inc/docuno.hxx
index 94b6044b2951..5d62c5a13a35 100644
--- a/sc/inc/docuno.hxx
+++ b/sc/inc/docuno.hxx
@@ -364,6 +364,9 @@ public:
     /// @see vcl::ITiledRenderable::setClientZoom().
     virtual void setClientZoom(int nTilePixelWidth, int nTilePixelHeight, int 
nTileTwipWidth, int nTileTwipHeight) override;
 
+    /// @see vcl::ITiledRenderable::setExportZoom().
+    virtual void setExportZoom(int nExportZoom) override;
+
     /// @see vcl::ITiledRenderable::setOutlineState().
     virtual void setOutlineState(bool bColumn, int nLevel, int nIndex, bool 
bHidden) override;
 
diff --git a/sc/inc/scextopt.hxx b/sc/inc/scextopt.hxx
index 843dfa6ebab0..4a71fdd7dd30 100644
--- a/sc/inc/scextopt.hxx
+++ b/sc/inc/scextopt.hxx
@@ -57,6 +57,7 @@ struct ScExtTabSettings
     Color               maGridColor;        ///< Grid color.
     tools::Long                mnNormalZoom;       ///< Zoom in percent for 
normal view.
     tools::Long                mnPageZoom;         ///< Zoom in percent for 
pagebreak preview.
+    std::optional<sal_uInt16> moExportZoom; ///< Zoom in percent - use when 
NormalZoom isn't accurate
     bool                mbSelected;         ///< true = Sheet is selected.
     bool                mbFrozenPanes;      ///< true = Frozen panes; false = 
Normal splits.
     bool                mbPageMode;         ///< true = Pagebreak mode; false 
= Normal view mode.
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx 
b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index 282bfeee1cf5..90a68ae7d49a 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -18,6 +18,7 @@
 #include <comphelper/processfactory.hxx>
 #include <comphelper/propertysequence.hxx>
 #include <comphelper/servicehelper.hxx>
+#include <comphelper/SetFlagContextHelper.hxx>
 #include <sfx2/dispatch.hxx>
 #include <sfx2/viewfrm.hxx>
 #include <svl/stritem.hxx>
@@ -3105,6 +3106,23 @@ CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, 
testCellInvalidationDocWithExistingZo
     CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aView2.m_aInvalidations[0],
                                                   aView1.m_aInvalidations[0],
                                                   50);
+    // cool#4250: zoom values
+    pModelObj->setExportZoom(150);
+
+    {
+        uno::ContextLayer 
aLayer(comphelper::NewFlagContext(u"IsLOKExport"_ustr));
+        save(u"calc8"_ustr); // .ODS
+    }
+    xmlDocUniquePtr pSettingsXml = parseExport(u"settings.xml"_ustr);
+    // Multi-user export: don't save every user's view into the exported file
+    // assertXPath(pSettingsXml, 
"//config:config-item[@config:name='ViewId']", 1);
+    // Use view's logic (not technical) zoom level for export
+    assertXPathContent(pSettingsXml, 
"//config:config-item[@config:name='ZoomValue'][1]", u"150");
+
+    save(u"Calc Office Open XML"_ustr); // .XLSX
+    xmlDocUniquePtr pSheet1Xml = parseExport(u"xl/worksheets/sheet1.xml"_ustr);
+    // Use view's logic (not technical) zoom level for export
+    assertXPath(pSheet1Xml, "//x:sheetViews/x:sheetView", "zoomScale", u"150");
 }
 
 CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testInputHandlerSyncedZoom)
diff --git a/sc/source/filter/excel/xeview.cxx 
b/sc/source/filter/excel/xeview.cxx
index 5280f234eed8..eb73aa67af96 100644
--- a/sc/source/filter/excel/xeview.cxx
+++ b/sc/source/filter/excel/xeview.cxx
@@ -383,7 +383,9 @@ XclExpTabViewSettings::XclExpTabViewSettings( const 
XclExpRoot& rRoot, SCTAB nSc
 
         // view mode and zoom
         maData.mbPageMode       = (GetBiff() == EXC_BIFF8) && 
rTabSett.mbPageMode;
-        maData.mnNormalZoom     = lclGetXclZoom( rTabSett.mnNormalZoom, 
EXC_WIN2_NORMALZOOM_DEF );
+        const tools::Long nNormalZoom
+            = rTabSett.moExportZoom ? *rTabSett.moExportZoom : 
rTabSett.mnNormalZoom;
+        maData.mnNormalZoom = lclGetXclZoom(nNormalZoom, 
EXC_WIN2_NORMALZOOM_DEF);
         maData.mnPageZoom       = lclGetXclZoom( rTabSett.mnPageZoom, 
EXC_WIN2_PAGEZOOM_DEF );
         maData.mnCurrentZoom    = maData.mbPageMode ? maData.mnPageZoom : 
maData.mnNormalZoom;
     }
diff --git a/sc/source/ui/inc/viewdata.hxx b/sc/source/ui/inc/viewdata.hxx
index 0fccba0d64d1..de7d910c1756 100644
--- a/sc/source/ui/inc/viewdata.hxx
+++ b/sc/source/ui/inc/viewdata.hxx
@@ -290,6 +290,8 @@ private:
     Fraction            aDefZoomY;
     Fraction            aDefPageZoomX;              // zoom in page break 
preview mode
     Fraction            aDefPageZoomY;
+    // If the actual zoom values are for implemention-only purposes, then 
provide a value for export
+    std::optional<sal_uInt16> oExportZoom; // used for all sheets
 
     ScRefType           eRefType;
 
@@ -457,6 +459,9 @@ public:
     const Fraction& GetZoomX() const        { return bPagebreak ? 
pThisTab->aPageZoomX : pThisTab->aZoomX; }
     const Fraction& GetZoomY() const        { return bPagebreak ? 
pThisTab->aPageZoomY : pThisTab->aZoomY; }
 
+    void SetExportZoom(sal_uInt16 nExportZoom) { oExportZoom = nExportZoom; }
+    const std::optional<sal_uInt16>& GetExportZoom() const { return 
oExportZoom; }
+
     void            SetShowGrid( bool bShow );
     bool            GetShowGrid() const { return pThisTab->bShowGrid; }
 
diff --git a/sc/source/ui/unoobj/docuno.cxx b/sc/source/ui/unoobj/docuno.cxx
index 9d38d1b080ec..4f376e9d7041 100644
--- a/sc/source/ui/unoobj/docuno.cxx
+++ b/sc/source/ui/unoobj/docuno.cxx
@@ -1117,6 +1117,19 @@ void ScModelObj::setClientZoom(int nTilePixelWidth_, int 
nTilePixelHeight_, int
         pDrawView->resetGridOffsetsForAllSdrPageViews();
 }
 
+void ScModelObj::setExportZoom(int nExportZoom)
+{
+    ScViewData* pViewData = ScDocShell::GetViewData();
+    if (!pViewData)
+        return;
+
+    if (pViewData->GetZoomType() != SvxZoomType::PERCENT)
+        return;
+
+    assert(nExportZoom > 0 && nExportZoom <= SAL_MAX_UINT16);
+    pViewData->SetExportZoom(nExportZoom);
+}
+
 void ScModelObj::getRowColumnHeaders(const tools::Rectangle& rRectangle, 
tools::JsonWriter& rJsonWriter)
 {
     ScViewData* pViewData = ScDocShell::GetViewData();
diff --git a/sc/source/ui/view/viewdata.cxx b/sc/source/ui/view/viewdata.cxx
index 7fa3e8f0f6ea..a00ba92a0036 100644
--- a/sc/source/ui/view/viewdata.cxx
+++ b/sc/source/ui/view/viewdata.cxx
@@ -64,6 +64,7 @@
 #include <comphelper/flagguard.hxx>
 #include <comphelper/lok.hxx>
 #include <comphelper/processfactory.hxx>
+#include <comphelper/SetFlagContextHelper.hxx>
 #include <comphelper/string.hxx>
 
 #include <vcl/uitest/logger.hxx>
@@ -594,6 +595,11 @@ void ScViewDataTable::WriteUserDataSequence(uno::Sequence 
<beans::PropertyValue>
     sal_Int32 nPageZoomValue = tools::Long(aPageZoomY * 100);
     pSettings[SC_TABLE_ZOOM_TYPE].Name = SC_ZOOMTYPE;
     pSettings[SC_TABLE_ZOOM_TYPE].Value <<= sal_Int16(eZoomType);
+
+    const std::optional<sal_uInt16>& oExportZoom = rViewData.GetExportZoom();
+    if (oExportZoom && comphelper::IsContextFlagActive(u"IsLOKExport"_ustr))
+        nZoomValue = *oExportZoom;
+
     pSettings[SC_TABLE_ZOOM_VALUE].Name = SC_ZOOMVALUE;
     pSettings[SC_TABLE_ZOOM_VALUE].Value <<= nZoomValue;
     pSettings[SC_TABLE_PAGE_VIEW_ZOOM_VALUE].Name = SC_PAGEVIEWZOOMVALUE;
@@ -3561,6 +3567,7 @@ void ScViewData::WriteExtOptions( ScExtDocOptions& 
rDocOpt ) const
             rTabSett.mbPageMode = bPagebreak;
             rTabSett.mnNormalZoom = static_cast< tools::Long >( 
pViewTab->aZoomY * Fraction( 100.0 ) );
             rTabSett.mnPageZoom = static_cast< tools::Long >( 
pViewTab->aPageZoomY * Fraction( 100.0 ) );
+            rTabSett.moExportZoom = GetExportZoom();
         }
     }
 }
@@ -3777,6 +3784,10 @@ void ScViewData::WriteUserDataSequence(uno::Sequence 
<beans::PropertyValue>& rSe
     sal_Int32 nPageZoomValue = tools::Long(pThisTab->aPageZoomY * 100);
     pSettings[SC_ZOOM_TYPE].Name = SC_ZOOMTYPE;
     pSettings[SC_ZOOM_TYPE].Value <<= sal_Int16(pThisTab->eZoomType);
+
+    if (oExportZoom && comphelper::IsContextFlagActive(u"IsLOKExport"_ustr))
+        nZoomValue = *oExportZoom;
+
     pSettings[SC_ZOOM_VALUE].Name = SC_ZOOMVALUE;
     pSettings[SC_ZOOM_VALUE].Value <<= nZoomValue;
     pSettings[SC_PAGE_VIEW_ZOOM_VALUE].Name = SC_PAGEVIEWZOOMVALUE;
diff --git a/xmloff/source/core/xmlexp.cxx b/xmloff/source/core/xmlexp.cxx
index b3f21d60e8ef..66b33ab3840c 100644
--- a/xmloff/source/core/xmlexp.cxx
+++ b/xmloff/source/core/xmlexp.cxx
@@ -45,6 +45,7 @@
 #include <comphelper/processfactory.hxx>
 #include <comphelper/propertysetinfo.hxx>
 #include <comphelper/propertyvalue.hxx>
+#include <comphelper/lok.hxx>
 #include <xmloff/namespacemap.hxx>
 #include <xmloff/xmluconv.hxx>
 #include <xmloff/xmlnamespace.hxx>
@@ -1782,6 +1783,12 @@ void 
SvXMLExport::GetViewSettingsAndViews(uno::Sequence<beans::PropertyValue>& r
     if(!xViewDataSupplier.is())
         return;
 
+    std::optional<css::uno::ContextLayer> oLayer;
+    if (comphelper::LibreOfficeKit::isActive())
+    {
+        oLayer.emplace(comphelper::NewFlagContext(u"IsLOKExport"_ustr));
+    }
+
     uno::Reference<container::XIndexAccess> xIndexAccess;
     xViewDataSupplier->setViewData( xIndexAccess ); // make sure we get a 
newly created sequence
     {

Reply via email to