writerfilter/qa/cppunittests/dmapper/DomainMapper.cxx          |   21 
++++++++++
 writerfilter/qa/cppunittests/dmapper/data/sdt-run-in-para.docx |binary
 writerfilter/source/dmapper/DomainMapper.cxx                   |   10 ++++
 3 files changed, 31 insertions(+)

New commits:
commit bab023ce3b584e815067a207adc7a8aca1f964af
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Tue Jun 28 08:53:15 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Tue Jun 28 09:31:51 2022 +0200

    tdf#149654 sw content controls: fix missing string before inline SDT from 
DOCX
    
    The document had a block SDT and inside that, a run SDT. The content
    that is before the run SDT but already inside the block SDT was lost.
    
    To work incrementally, it was intentional that once commit
    5ee8670f18cb8b1913a23d04590d6a31ac9730de (sw content controls, date: add
    DOCX import, 2022-05-30) changed most of run SDTs to content controls,
    it left block SDTs (and cell/row SDTs, too) unchanged, so they are still
    mapped to fields. What was forgotten is that m_pImpl->m_pSdtHelper in
    DomainMapper is a shared state: once a run SDT starts, it assumes that
    no non-run SDTs are in progress.
    
    Fix the problem by explicitly checking for non-run SDTs before
    PushSdt(), that keeps the separation (content control for run SDT,
    fields for the rest) but fixes the lost content.
    
    This is for plain text SDTs, but other types can be added if necessary.
    
    Change-Id: I46b029a3a945d7416028aa196ac3160e6d96eae8
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136524
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/writerfilter/qa/cppunittests/dmapper/DomainMapper.cxx 
b/writerfilter/qa/cppunittests/dmapper/DomainMapper.cxx
index 88ba238fae29..21d5c84cae4e 100644
--- a/writerfilter/qa/cppunittests/dmapper/DomainMapper.cxx
+++ b/writerfilter/qa/cppunittests/dmapper/DomainMapper.cxx
@@ -74,6 +74,27 @@ CPPUNIT_TEST_FIXTURE(Test, testLargeParaTopMargin)
     // -> wrap around a TextBox), which shifted the triangle shape out of the 
page frame.
     CPPUNIT_ASSERT_EQUAL(nExpected, nParaTopMargin);
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testSdtRunInPara)
+{
+    // Given a document with a block SDT, and inside that some content + a run 
SDT:
+    OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"sdt-run-in-para.docx";
+
+    // When loading that document:
+    getComponent() = loadFromDesktop(aURL);
+
+    // Then make sure the content inside the block SDT but outside the run SDT 
is not lost:
+    uno::Reference<text::XTextDocument> xTextDocument(getComponent(), 
uno::UNO_QUERY);
+    uno::Reference<container::XEnumerationAccess> 
xParaEnumAccess(xTextDocument->getText(),
+                                                                  
uno::UNO_QUERY);
+    uno::Reference<container::XEnumeration> xParaEnum = 
xParaEnumAccess->createEnumeration();
+    uno::Reference<text::XTextRange> xPara(xParaEnum->nextElement(), 
uno::UNO_QUERY);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: first-second
+    // - Actual  : second
+    // i.e. the block-SDT-only string was lost.
+    CPPUNIT_ASSERT_EQUAL(OUString("first-second"), xPara->getString());
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/qa/cppunittests/dmapper/data/sdt-run-in-para.docx 
b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-in-para.docx
new file mode 100644
index 000000000000..863bc9213b5b
Binary files /dev/null and 
b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-in-para.docx differ
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx 
b/writerfilter/source/dmapper/DomainMapper.cxx
index cd89b3d7d7ae..224f8c2aa902 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -1074,6 +1074,16 @@ void DomainMapper::lcl_attribute(Id nName, Value & val)
             }
             if (nName == NS_ooxml::LN_CT_SdtRun_sdtContent)
             {
+                if (m_pImpl->GetSdtStarts().empty() && 
!m_pImpl->m_pSdtHelper->getSdtTexts().isEmpty())
+                {
+                    // A non-inline SDT is already started, first convert that 
to a field and only
+                    // then map the inline SDT to a content control.
+                    if (m_pImpl->m_pSdtHelper->getControlType() == 
SdtControlType::plainText)
+                    {
+                        m_pImpl->m_pSdtHelper->createPlainTextControl();
+                    }
+                }
+
                 
m_pImpl->m_pSdtHelper->setControlType(SdtControlType::richText);
                 m_pImpl->PushSdt();
                 break;

Reply via email to