sw/qa/extras/rtfexport/data/tdf162342.rtf            |    7 ++
 sw/qa/extras/rtfexport/rtfexport8.cxx                |   64 +++++++++++++++++++
 sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx |   34 ++++++++--
 3 files changed, 101 insertions(+), 4 deletions(-)

New commits:
commit 7877bfc1356ca38d294772e9d88c429b3c613a04
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Mon Jul 28 01:29:18 2025 +0500
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Jul 28 13:26:44 2025 +0200

    tdf#162342: workaround out-of-order context stack pop
    
    Turns out, the context stack is populated in one order, but emptied in
    another. Commit e63a75e00dcd797b0bd061add3c72dcb7b353007 (tdf#158586
    writerfilter: RTF import: fix \page \sect \skbnone, 2024-02-01) added
    yet another case, which resulted in m_pTopContext being empty at the
    moment when section properties should have been applied (see "needed
    for page properties" comment in DomainMapper::sprmWithProps; that code
    wasn't reached because of (!rContext) check above).
    
    But that isn't the only case; I tried to assert in non-RTF cases, and
    it fired in `make check`, too.
    
    So here is an UGLY HACK. I repair the stack when it's popped out of
    order. This pleads for a proper fix, but the effort will be far from
    trivial.
    
    Change-Id: I1a5c646188f3da4842d103b8edeb11b801fea830
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188450
    Reviewed-by: Michael Stahl <michael.st...@collabora.com>
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>

diff --git a/sw/qa/extras/rtfexport/data/tdf162342.rtf 
b/sw/qa/extras/rtfexport/data/tdf162342.rtf
new file mode 100644
index 000000000000..e04fdc6aeb96
--- /dev/null
+++ b/sw/qa/extras/rtfexport/data/tdf162342.rtf
@@ -0,0 +1,7 @@
+{ tf1
+\margl720 \margr360 \margt720 \margb720 \paperw6120 \paperh7920
+First
+\page
+Second
+\page
+Third}
\ No newline at end of file
diff --git a/sw/qa/extras/rtfexport/rtfexport8.cxx 
b/sw/qa/extras/rtfexport/rtfexport8.cxx
index 3337ce0b38d9..5fbfb0128fd4 100644
--- a/sw/qa/extras/rtfexport/rtfexport8.cxx
+++ b/sw/qa/extras/rtfexport/rtfexport8.cxx
@@ -1040,6 +1040,70 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf167569_2)
     }
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf162342)
+{
+    // Given an RTF with some page settings, and two page breaks:
+    createSwDoc("tdf162342.rtf");
+
+    {
+        xmlDocUniquePtr pLayout = parseLayoutDump();
+        assertXPath(pLayout, "//page['pass 1']", 3);
+
+        // Without the fix, this failed with
+        // - Expected: 6120
+        // - Actual  : 12240
+        // i.e., the page geometry was ignored.
+        assertXPath(pLayout, "//page[1]['pass 1']/infos/bounds", "width", 
u"6120");
+        assertXPath(pLayout, "//page[1]['pass 1']/infos/bounds", "height", 
u"7937"); // Not 7920?
+        assertXPath(pLayout, "//page[1]['pass 1']/infos/prtBounds", "left", 
u"720");
+        assertXPath(pLayout, "//page[1]['pass 1']/infos/prtBounds", "top", 
u"720");
+        assertXPath(pLayout, "//page[1]['pass 1']/infos/prtBounds", "width", 
u"5040");
+        assertXPath(pLayout, "//page[1]['pass 1']/infos/prtBounds", "height", 
u"6497");
+
+        assertXPath(pLayout, "//page[2]['pass 1']/infos/bounds", "width", 
u"6120");
+        assertXPath(pLayout, "//page[2]['pass 1']/infos/bounds", "height", 
u"7937");
+        assertXPath(pLayout, "//page[2]['pass 1']/infos/prtBounds", "left", 
u"720");
+        assertXPath(pLayout, "//page[2]['pass 1']/infos/prtBounds", "top", 
u"720");
+        assertXPath(pLayout, "//page[2]['pass 1']/infos/prtBounds", "width", 
u"5040");
+        assertXPath(pLayout, "//page[2]['pass 1']/infos/prtBounds", "height", 
u"6497");
+
+        assertXPath(pLayout, "//page[3]['pass 1']/infos/bounds", "width", 
u"6120");
+        assertXPath(pLayout, "//page[3]['pass 1']/infos/bounds", "height", 
u"7937");
+        assertXPath(pLayout, "//page[3]['pass 1']/infos/prtBounds", "left", 
u"720");
+        assertXPath(pLayout, "//page[3]['pass 1']/infos/prtBounds", "top", 
u"720");
+        assertXPath(pLayout, "//page[3]['pass 1']/infos/prtBounds", "width", 
u"5040");
+        assertXPath(pLayout, "//page[3]['pass 1']/infos/prtBounds", "height", 
u"6497");
+    }
+
+    saveAndReload(mpFilter);
+
+    {
+        xmlDocUniquePtr pLayout = parseLayoutDump();
+        assertXPath(pLayout, "//page['pass 2']", 3);
+
+        assertXPath(pLayout, "//page[1]['pass 2']/infos/bounds", "width", 
u"6120");
+        assertXPath(pLayout, "//page[1]['pass 2']/infos/bounds", "height", 
u"7937");
+        assertXPath(pLayout, "//page[1]['pass 2']/infos/prtBounds", "left", 
u"720");
+        assertXPath(pLayout, "//page[1]['pass 2']/infos/prtBounds", "top", 
u"720");
+        assertXPath(pLayout, "//page[1]['pass 2']/infos/prtBounds", "width", 
u"5040");
+        assertXPath(pLayout, "//page[1]['pass 2']/infos/prtBounds", "height", 
u"6497");
+
+        assertXPath(pLayout, "//page[2]['pass 2']/infos/bounds", "width", 
u"6120");
+        assertXPath(pLayout, "//page[2]['pass 2']/infos/bounds", "height", 
u"7937");
+        assertXPath(pLayout, "//page[2]['pass 2']/infos/prtBounds", "left", 
u"720");
+        assertXPath(pLayout, "//page[2]['pass 2']/infos/prtBounds", "top", 
u"720");
+        assertXPath(pLayout, "//page[2]['pass 2']/infos/prtBounds", "width", 
u"5040");
+        assertXPath(pLayout, "//page[2]['pass 2']/infos/prtBounds", "height", 
u"6497");
+
+        assertXPath(pLayout, "//page[3]['pass 2']/infos/bounds", "width", 
u"6120");
+        assertXPath(pLayout, "//page[3]['pass 2']/infos/bounds", "height", 
u"7937");
+        assertXPath(pLayout, "//page[3]['pass 2']/infos/prtBounds", "left", 
u"720");
+        assertXPath(pLayout, "//page[3]['pass 2']/infos/prtBounds", "top", 
u"720");
+        assertXPath(pLayout, "//page[3]['pass 2']/infos/prtBounds", "width", 
u"5040");
+        assertXPath(pLayout, "//page[3]['pass 2']/infos/prtBounds", "height", 
u"6497");
+    }
+}
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx 
b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
index fc9e14f6a22b..93bac6b106d2 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
@@ -1368,13 +1368,39 @@ void    DomainMapper_Impl::PopProperties(ContextType 
eId)
     }
 
     m_aPropertyStacks[eId].pop();
+    {
+        // !!!FIXME!!!
+        // We sometimes pop out of order. Just try to comment out this fixing 
block, keeping the
+        // assert; and run `make check`. Sigh! In that case, popping from 
m_aContextStack would
+        // make it (and m_pTopContext) out-of-sync. This must be an abort; but 
let's try to fix
+        // the stack instead, as a temporary (?) measure. I don't replace the 
stack with a more
+        // easy-to-manipulate container: I don't optimize for invalid case.
+        // !!!FIXME!!!
+        SAL_WARN_IF(m_aContextStack.top() != eId, "writerfilter.dmapper", "pop 
out of order");
+        std::stack<ContextType> tmp;
+        while (m_aContextStack.top() != eId)
+        {
+            tmp.push(m_aContextStack.top());
+            m_aContextStack.pop();
+        }
+        m_aContextStack.pop();
+        while (!tmp.empty())
+        {
+            m_aContextStack.push(tmp.top());
+            tmp.pop();
+        }
+        m_aContextStack.push(eId); // to pop below
+    }
+    assert(m_aContextStack.top() == eId);
     m_aContextStack.pop();
-    if(!m_aContextStack.empty() && 
!m_aPropertyStacks[m_aContextStack.top()].empty())
-
-            m_pTopContext = m_aPropertyStacks[m_aContextStack.top()].top();
+    if (!m_aContextStack.empty() && 
!m_aPropertyStacks[m_aContextStack.top()].empty())
+    {
+        m_pTopContext = m_aPropertyStacks[m_aContextStack.top()].top();
+    }
     else
     {
-        // OSL_ENSURE(eId == CONTEXT_SECTION, "this should happen at a section 
context end");
+        SAL_WARN_IF(eId != CONTEXT_SECTION, "writerfilter.dmapper",
+                    "this should happen at a section context end");
         m_pTopContext.clear();
     }
 }

Reply via email to