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;