sw/CppunitTest_sw_filter_ww8.mk | 75 ++++++++++ sw/Module_sw.mk | 1 sw/qa/extras/ooxmlexport/ooxmlexport17.cxx | 25 ++- sw/qa/extras/ooxmlexport/ooxmlexport5.cxx | 2 sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx | 6 sw/qa/filter/ww8/ww8.cxx | 63 ++++++++ sw/source/filter/ww8/attributeoutputbase.hxx | 2 sw/source/filter/ww8/docxattributeoutput.cxx | 21 ++ sw/source/filter/ww8/docxattributeoutput.hxx | 2 sw/source/filter/ww8/wrtw8nds.cxx | 2 writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx | 30 ++++ writerfilter/qa/cppunittests/dmapper/data/sdt-run-plain-text.docx |binary writerfilter/source/dmapper/DomainMapper.cxx | 20 ++ writerfilter/source/dmapper/DomainMapper_Impl.cxx | 5 writerfilter/source/dmapper/SdtHelper.cxx | 1 writerfilter/source/dmapper/SdtHelper.hxx | 4 16 files changed, 243 insertions(+), 16 deletions(-)
New commits: commit 8c0ab5555c373a2aeb44b71d04ae80c67e704ec8 Author: Miklos Vajna <[email protected]> AuthorDate: Mon Jul 25 09:13:03 2022 +0200 Commit: Miklos Vajna <[email protected]> CommitDate: Wed Jul 27 13:38:30 2022 +0200 sw content controls, plain text: add DOCX import - the core of this is the writerfilter/ change to call PopSdt() for SdtControlType::plainText, which maps inline plain text SDTs to Writer content controls, not to input fields - disable the grab-bag in this case, otherwise we would run duplicated <w:sdt> elements on export - fix CppunitTest_sw_ooxmlexport7's testSdtAndShapeOverlapping by postponing the SDT start in DocxAttributeOutput::WriteContentControlStart() in case a shape is anchored at the same position as the SDT start: if the shape should start inside the content control, then it should be anchored after the dummy character - reduce the debug output in VMLExport::Commit(), which could write control characters to the terminal on test failure, potentially breaking it (requiring a 'reset' to recover) - fix CppunitTest_sw_ooxmlexport5's testSdt2Run: now we merge two runs inside a plain text content control into a single one, and there is no problem with that, so adapt the test instead - fix CppunitTest_sw_ooxmlexport17's testTdf148361: plain text inline SDT is now a content control, not a field - fix CppunitTest_sw_ooxmlfieldexport's testfdo82492: explicitly assert that there is 1 text run inside the SDT and there is a shape after it (outside). Also extend DocxAttributeOutput::EndContentControl(), so it ends the content control at the correct, earlier position in case it's followed by an as-char shape - fix CppunitTest_sw_ooxmlfieldexport's testfdo82123: again assert that the SDT has 1 run with text, and there is a drawing after the SDT - fix CppunitTest_sw_ooxmlfieldexport's testTdf104823: this revealed that some more complex logic is needed to support data bindings, so exclude text-with-databinding from the scope of this commit and continue to map those to input fields for now - fix CppunitTest_sw_ooxmlfieldexport's testFdo81945: this had a similar problem as as-char shapes, but this time a new SDT is starting right after a previous SDT. Adapt DocxAttributeOutput::EndContentControl() accordingly, though perhaps this should be generalized later, so we always close SDTs in the previous run, unless this is the last run, or something similar (cherry picked from commit 9700c1b2170ad04453a361ed5647937833ac3c18) Conflicts: oox/source/export/vmlexport.cxx Change-Id: Ifaf581be884a683de6c8b932008a03ba43734b75 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137494 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx index 9f8c4c0b5c5d..2414c1b2ad38 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx @@ -397,15 +397,26 @@ DECLARE_OOXMLEXPORT_TEST(testTdf123642_BookmarkAtDocEnd, "tdf123642.docx") DECLARE_OOXMLEXPORT_TEST(testTdf148361, "tdf148361.docx") { - // Refresh fields and ensure cross-reference to numbered para is okay - uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); - uno::Reference<container::XEnumerationAccess> xFieldsAccess(xTextFieldsSupplier->getTextFields()); + if (mbExported) + { + // Block SDT is turned into run SDT on export, so the next import will have this as content + // control, not as a field. + OUString aActual = getParagraph(1)->getString(); + // This was "itadmin". + CPPUNIT_ASSERT_EQUAL(OUString("itadmin"), aActual); + } + else + { + // Refresh fields and ensure cross-reference to numbered para is okay + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XEnumerationAccess> xFieldsAccess(xTextFieldsSupplier->getTextFields()); - uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); - CPPUNIT_ASSERT(xFields->hasMoreElements()); + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + CPPUNIT_ASSERT(xFields->hasMoreElements()); - uno::Reference<text::XTextField> xTextField1(xFields->nextElement(), uno::UNO_QUERY); - CPPUNIT_ASSERT_EQUAL(OUString("itadmin"), xTextField1->getPresentation(false)); + uno::Reference<text::XTextField> xTextField1(xFields->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("itadmin"), xTextField1->getPresentation(false)); + } OUString aActual = getParagraph(2)->getString(); // This was "itadmin". diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx index 17fe49c8f8aa..04c9d7e7d927 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx @@ -1105,7 +1105,7 @@ CPPUNIT_TEST_FIXTURE(Test, testSdt2Run) xmlDocUniquePtr pXmlDoc = parseExport(); // The problem was that <w:sdt> was closed after "first", not after "second", so the second assert failed. - assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:sdt/w:sdtContent/w:r", 1); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:p/w:sdt/w:sdtContent/w:r/w:t", "firstsecond"); // Make sure the third portion is still outside <w:sdt>. assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[1]/w:r/w:t", "third"); } diff --git a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx index ea07a681343c..98a5b6ab6a15 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx @@ -541,7 +541,8 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testfdo82123, "fdo82123.docx") xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); // make sure there is only one run inside first SDT after RT as in the Original file. - assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr/w:tc[2]/w:p/w:sdt[1]/w:sdtContent/w:r",1); + assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr/w:tc[2]/w:p/w:sdt[1]/w:sdtContent/w:r/w:t", 1); + assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr/w:tc[2]/w:p/w:r/w:drawing", 1); } DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testSdtBeforeField, "sdt-before-field.docx") @@ -563,7 +564,8 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testfdo82492, "fdo82492.docx") xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); // make sure there is only one run inside first SDT after RT as in the Original file. - assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:sdt[1]/w:sdtContent/w:r",1); + assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:sdt[1]/w:sdtContent/w:r/w:t", 1); + assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:r/mc:AlternateContent", 1); } DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testSdtHeader, "sdt-header.docx") diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx b/sw/source/filter/ww8/attributeoutputbase.hxx index a9331c9b628b..35ac50057786 100644 --- a/sw/source/filter/ww8/attributeoutputbase.hxx +++ b/sw/source/filter/ww8/attributeoutputbase.hxx @@ -373,7 +373,7 @@ public: virtual void StartContentControl(const SwFormatContentControl& /*rFormatContentControl*/) {} /// Output content control end. - virtual void EndContentControl() {} + virtual void EndContentControl( const SwTextNode& /*rNode*/, sal_Int32 /*nPos*/ ) {} protected: diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index e0cd2b1318a6..84189eaf5e81 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -368,9 +368,16 @@ void DocxAttributeOutput::StartContentControl(const SwFormatContentControl& rFor m_pContentControl = rFormatContentControl.GetContentControl(); } -void DocxAttributeOutput::EndContentControl() +void DocxAttributeOutput::EndContentControl(const SwTextNode& rNode, sal_Int32 nPos) { - ++m_nCloseContentControlInThisRun; + if (rNode.GetTextAttrForCharAt(nPos, RES_TXTATR_FLYCNT) || rNode.GetTextAttrForCharAt(nPos, RES_TXTATR_CONTENTCONTROL)) + { + ++m_nCloseContentControlInPreviousRun; + } + else + { + ++m_nCloseContentControlInThisRun; + } } static void checkAndWriteFloatingTables(DocxAttributeOutput& rDocxAttributeOutput) @@ -2336,6 +2343,11 @@ void DocxAttributeOutput::WriteContentControlStart() return; } + if (m_bAnchorLinkedToNode) + { + return; + } + m_pSerializer->startElementNS(XML_w, XML_sdt); m_pSerializer->startElementNS(XML_w, XML_sdtPr); if (!m_pContentControl->GetPlaceholderDocPart().isEmpty()) diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx index a264b879fd9b..def5a955f363 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -409,7 +409,7 @@ public: void StartContentControl(const SwFormatContentControl& rFormatContentControl) override; /// See AttributeOutputBase::EndContentControl(). - void EndContentControl() override; + void EndContentControl( const SwTextNode& rNode, sal_Int32 nPos ) override; private: /// Initialize the structures where we are going to collect some of the paragraph properties. diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx index 805ab17a7464..e980efa238b7 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -1416,7 +1416,7 @@ int SwWW8AttrIter::OutAttrWithRange(const SwTextNode& rNode, sal_Int32 nPos) pEnd = pHt->End(); if (nPos == *pEnd && nPos != pHt->GetStart()) { - m_rExport.AttrOutput().EndContentControl(); + m_rExport.AttrOutput().EndContentControl(rNode, nPos); --nRet; } break; diff --git a/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx b/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx index b2e7f1058f88..6b568619785e 100644 --- a/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx +++ b/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx @@ -88,6 +88,36 @@ CPPUNIT_TEST_FIXTURE(Test, testSdtRunRichText) CPPUNIT_ASSERT_EQUAL(24.f, fCharheight); } +CPPUNIT_TEST_FIXTURE(Test, testSdtRunPlainText) +{ + // Given a document with a plain text inline/run SDT: + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "sdt-run-plain-text.docx"; + + // When loading the document: + getComponent() = loadFromDesktop(aURL); + + // Then make sure that the text inside the SDT is not rich: + 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<container::XEnumerationAccess> xPara(xParaEnum->nextElement(), uno::UNO_QUERY); + uno::Reference<container::XEnumeration> xPortionEnum = xPara->createEnumeration(); + uno::Reference<beans::XPropertySet> xPortion(xPortionEnum->nextElement(), uno::UNO_QUERY); + OUString aTextPortionType; + xPortion->getPropertyValue("TextPortionType") >>= aTextPortionType; + // Without the accompanying fix in place, this test would have failed with: + // - Expected: ContentControl + // - Actual : TextField + // i.e. the SDT was imported as a text field, not as a content control. + CPPUNIT_ASSERT_EQUAL(OUString("ContentControl"), aTextPortionType); + uno::Reference<beans::XPropertySet> xContentControl; + xPortion->getPropertyValue("ContentControl") >>= xContentControl; + bool bPlainText{}; + xContentControl->getPropertyValue("PlainText") >>= bPlainText; + CPPUNIT_ASSERT(bPlainText); +} + CPPUNIT_TEST_FIXTURE(Test, testSdtRunCheckbox) { // Given a document with a checkbox inline/run SDT: diff --git a/writerfilter/qa/cppunittests/dmapper/data/sdt-run-plain-text.docx b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-plain-text.docx new file mode 100644 index 000000000000..127d81fd966b Binary files /dev/null and b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-plain-text.docx differ diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx index 33929cdddf1c..93e0b4f1bd0e 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -1070,6 +1070,7 @@ void DomainMapper::lcl_attribute(Id nName, Value & val) break; case NS_ooxml::LN_CT_SdtBlock_sdtContent: case NS_ooxml::LN_CT_SdtRun_sdtContent: + m_pImpl->m_pSdtHelper->SetSdtType(nName); if (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::unknown) { // Still not determined content type? and it is even not unsupported? Then it is plain text field @@ -1110,6 +1111,15 @@ void DomainMapper::lcl_attribute(Id nName, Value & val) default: break; } + + if (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::plainText) + { + // The plain text && data binding case needs more work before it can be enabled. + if (m_pImpl->m_pSdtHelper->GetDataBindingPrefixMapping().isEmpty()) + { + m_pImpl->PopSdt(); + } + } } m_pImpl->SetSdt(false); @@ -2751,6 +2761,14 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) case NS_ooxml::LN_CT_SdtPr_text: { m_pImpl->m_pSdtHelper->setControlType(SdtControlType::plainText); + if (m_pImpl->m_pSdtHelper->GetSdtType() == NS_ooxml::LN_CT_SdtRun_sdtContent) + { + if (m_pImpl->m_pSdtHelper->GetDataBindingPrefixMapping().isEmpty()) + { + m_pImpl->m_pSdtHelper->getInteropGrabBagAndClear(); + break; + } + } enableInteropGrabBag("ooxml:CT_SdtPr_text"); writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps(); if (pProperties) @@ -3739,7 +3757,7 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len) return; } } - else if (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::plainText) + else if ((m_pImpl->m_pSdtHelper->GetSdtType() != NS_ooxml::LN_CT_SdtRun_sdtContent || !m_pImpl->m_pSdtHelper->GetDataBindingPrefixMapping().isEmpty()) && m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::plainText) { m_pImpl->m_pSdtHelper->getSdtTexts().append(sText); if (bNewLine) diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index a1c280a9869a..0cf793f480b0 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -973,6 +973,11 @@ void DomainMapper_Impl::PopSdt() uno::Any(m_pSdtHelper->getDate().makeStringAndClear())); } + if (m_pSdtHelper->getControlType() == SdtControlType::plainText) + { + xContentControlProps->setPropertyValue("PlainText", uno::Any(true)); + } + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); m_pSdtHelper->clear(); diff --git a/writerfilter/source/dmapper/SdtHelper.cxx b/writerfilter/source/dmapper/SdtHelper.cxx index a3bd08ec807a..013581a038c8 100644 --- a/writerfilter/source/dmapper/SdtHelper.cxx +++ b/writerfilter/source/dmapper/SdtHelper.cxx @@ -445,6 +445,7 @@ void SdtHelper::clear() m_aDropDownItems.clear(); m_aDropDownDisplayTexts.clear(); setControlType(SdtControlType::unknown); + m_nSdtType = 0; m_sDataBindingPrefixMapping.clear(); m_sDataBindingXPath.clear(); m_sDataBindingStoreItemID.clear(); diff --git a/writerfilter/source/dmapper/SdtHelper.hxx b/writerfilter/source/dmapper/SdtHelper.hxx index 3d616067211e..a986da304df0 100644 --- a/writerfilter/source/dmapper/SdtHelper.hxx +++ b/writerfilter/source/dmapper/SdtHelper.hxx @@ -66,6 +66,7 @@ class SdtHelper final : public virtual SvRefBase std::vector<OUString> m_aDropDownDisplayTexts; /// Type of sdt control SdtControlType m_aControlType; + sal_uInt32 m_nSdtType = 0; /// Pieces of the default text -- currently used only by the dropdown control. OUStringBuffer m_aSdtTexts; /// Date ISO string contained in the w:date element, used by the date control. @@ -169,6 +170,9 @@ public: SdtControlType getControlType() { return m_aControlType; } void setControlType(SdtControlType aType) { m_aControlType = aType; } + void SetSdtType(sal_uInt32 nSdtType) { m_nSdtType = nSdtType; } + sal_uInt32 GetSdtType() const { return m_nSdtType; } + /// Create drop-down control from w:sdt's w:dropDownList. void createDropDownControl(); /// Create date control from w:sdt's w:date. commit 1fdfda440ae62ca40e757cf14af238d18b9b550a Author: Miklos Vajna <[email protected]> AuthorDate: Mon Jul 25 08:10:07 2022 +0200 Commit: Miklos Vajna <[email protected]> CommitDate: Wed Jul 27 13:38:15 2022 +0200 sw content controls, plain text: add DOCX export Map the PlainText UNO property to: <w:sdtPr> <w:text/> </w:sdtPr> (cherry picked from commit a6e5726f186bf9d2a0ea91169649504c7396c539) Conflicts: sw/qa/filter/ww8/ww8.cxx Change-Id: I57f365fcfb3a80acb74aa932432a8ae8f3acc92b Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137493 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/sw/CppunitTest_sw_filter_ww8.mk b/sw/CppunitTest_sw_filter_ww8.mk new file mode 100644 index 000000000000..0452776478df --- /dev/null +++ b/sw/CppunitTest_sw_filter_ww8.mk @@ -0,0 +1,75 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +#************************************************************************* +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +#************************************************************************* + +$(eval $(call gb_CppunitTest_CppunitTest,sw_filter_ww8)) + +$(eval $(call gb_CppunitTest_use_common_precompiled_header,sw_filter_ww8)) + +$(eval $(call gb_CppunitTest_add_exception_objects,sw_filter_ww8, \ + sw/qa/filter/ww8/ww8 \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,sw_filter_ww8, \ + comphelper \ + cppu \ + cppuhelper \ + editeng \ + sal \ + sfx \ + svl \ + svx \ + svxcore \ + sw \ + swqahelper \ + test \ + unotest \ + utl \ + vcl \ + tl \ +)) + +$(eval $(call gb_CppunitTest_use_externals,sw_filter_ww8,\ + boost_headers \ + libxml2 \ +)) + +$(eval $(call gb_CppunitTest_set_include,sw_filter_ww8,\ + -I$(SRCDIR)/sw/inc \ + -I$(SRCDIR)/sw/source/core/inc \ + -I$(SRCDIR)/sw/source/uibase/inc \ + -I$(SRCDIR)/sw/qa/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_CppunitTest_use_api,sw_filter_ww8,\ + udkapi \ + offapi \ + oovbaapi \ +)) + +$(eval $(call gb_CppunitTest_use_ure,sw_filter_ww8)) +$(eval $(call gb_CppunitTest_use_vcl,sw_filter_ww8)) + +$(eval $(call gb_CppunitTest_use_rdb,sw_filter_ww8,services)) + +$(eval $(call gb_CppunitTest_use_custom_headers,sw_filter_ww8,\ + officecfg/registry \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,sw_filter_ww8)) + +$(eval $(call gb_CppunitTest_use_uiconfigs,sw_filter_ww8, \ + modules/swriter \ +)) + +$(eval $(call gb_CppunitTest_use_more_fonts,sw_filter_ww8)) + +# vim: set noet sw=4 ts=4: diff --git a/sw/Module_sw.mk b/sw/Module_sw.mk index ce9a13066a96..e624630187b7 100644 --- a/sw/Module_sw.mk +++ b/sw/Module_sw.mk @@ -142,6 +142,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sw,\ CppunitTest_sw_core_edit \ CppunitTest_sw_uibase_fldui \ CppunitTest_sw_core_attr \ + CppunitTest_sw_filter_ww8 \ )) ifneq ($(DISABLE_GUI),TRUE) diff --git a/sw/qa/filter/ww8/ww8.cxx b/sw/qa/filter/ww8/ww8.cxx new file mode 100644 index 000000000000..d98ff7dae151 --- /dev/null +++ b/sw/qa/filter/ww8/ww8.cxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <swmodeltestbase.hxx> + +#include <com/sun/star/text/XTextDocument.hpp> + +namespace +{ +/** + * Covers sw/source/filter/ww8/ fixes. + * + * Note that these tests are meant to be simple: either load a file and assert some result or build + * a document model with code, export and assert that result. + * + * Keep using the various sw_<format>import/export suites for multiple filter calls inside a single + * test. + */ +class Test : public SwModelTestBase +{ +}; + +CPPUNIT_TEST_FIXTURE(Test, testPlainTextContentControlExport) +{ + // Given a document with a plain text content control around a text portion: + mxComponent = loadFromDesktop("private:factory/swriter"); + uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("PlainText", uno::Any(true)); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // When exporting to DOCX: + save("Office Open XML Text", maTempFile); + mbExported = true; + + // Then make sure the expected markup is used: + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // - XPath '//w:sdt/w:sdtPr/w:text' number of nodes is incorrect + // i.e. the plain text content control was turned into a rich text one on export. + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:text", 1); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index e74ea397c40a..e0cd2b1318a6 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -2428,6 +2428,11 @@ void DocxAttributeOutput::WriteContentControlStart() m_pSerializer->endElementNS(XML_w, XML_date); } + if (m_pContentControl->GetPlainText()) + { + m_pSerializer->singleElementNS(XML_w, XML_text); + } + m_pSerializer->endElementNS(XML_w, XML_sdtPr); m_pSerializer->startElementNS(XML_w, XML_sdtContent); m_pContentControl = nullptr;
