sw/qa/extras/ooxmlexport/ooxmlexport17.cxx | 34 +++++++++++++++++++++++++++ sw/source/filter/ww8/attributeoutputbase.hxx | 6 ++++ sw/source/filter/ww8/docxattributeoutput.cxx | 26 ++++++++++++++++++++ sw/source/filter/ww8/docxattributeoutput.hxx | 10 +++++++ sw/source/filter/ww8/wrtw8nds.cxx | 19 +++++++++++++++ 5 files changed, 95 insertions(+)
New commits: commit b40f9c536c1cefae8404a3f1c0080473151913d2 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Apr 12 08:25:16 2022 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Tue Apr 12 09:06:47 2022 +0200 sw content controls: add initial DOCX export Wrap the text portions inside the content control inside <w:sdtContent> and <w:sdt>. Also map the (so far) single property of it to <w:showingPlcHdr>. This is just initial export for inline text content controls, more properties are to be added in follow-up commits. Change-Id: I21e085496b4c79114b158656c5611aff8ffdd08a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132875 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx index e592e376336e..0c357607be22 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx @@ -13,6 +13,8 @@ #include <com/sun/star/text/XTextField.hpp> #include <com/sun/star/drawing/XShapes.hpp> #include <com/sun/star/util/XRefreshable.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/frame/XStorable.hpp> #include <comphelper/scopeguard.hxx> #include <officecfg/Office/Common.hxx> @@ -125,6 +127,38 @@ CPPUNIT_TEST_FIXTURE(Test, testClearingBreak) assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:r/w:br", "clear", "all"); } +CPPUNIT_TEST_FIXTURE(Test, testContentControlExport) +{ + // Given a document with a content control around one or more text portions: + 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("ShowingPlaceHolder", uno::makeAny(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:showingPlcHdr' number of nodes is incorrect + // i.e. the SDT elements were missing on export. + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:showingPlcHdr", 1); + assertXPath(pXmlDoc, "//w:sdt/w:sdtContent", 1); +} + DECLARE_OOXMLEXPORT_TEST(testTdf137466, "tdf137466.docx") { xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx b/sw/source/filter/ww8/attributeoutputbase.hxx index 8461f2e1510c..a9331c9b628b 100644 --- a/sw/source/filter/ww8/attributeoutputbase.hxx +++ b/sw/source/filter/ww8/attributeoutputbase.hxx @@ -369,6 +369,12 @@ public: const OUString &rNumberingString, const SvxBrushItem* pBrush) = 0; // #i120928 export graphic of bullet + /// Output content control start. + virtual void StartContentControl(const SwFormatContentControl& /*rFormatContentControl*/) {} + + /// Output content control end. + virtual void EndContentControl() {} + protected: static void GetNumberPara( OUString& rStr, const SwField& rField ); diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 1e2d490e653c..47d4117ae052 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -362,6 +362,13 @@ void DocxAttributeOutput::WriteFloatingTable(ww8::Frame const* pParentFrame) m_rExport.SetFloatingTableFrame(nullptr); } +void DocxAttributeOutput::StartContentControl(const SwFormatContentControl& rFormatContentControl) +{ + m_pContentControl = rFormatContentControl.GetContentControl(); +} + +void DocxAttributeOutput::EndContentControl() { ++m_nCloseContentControl; } + static void checkAndWriteFloatingTables(DocxAttributeOutput& rDocxAttributeOutput) { const auto& rExport = rDocxAttributeOutput.GetExport(); @@ -1621,6 +1628,12 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / m_bEndCharSdt = false; } + for (; m_nCloseContentControl > 0; --m_nCloseContentControl) + { + m_pSerializer->endElementNS(XML_w, XML_sdtContent); + m_pSerializer->endElementNS(XML_w, XML_sdt); + } + if ( m_closeHyperlinkInPreviousRun ) { if ( m_startedHyperlink ) @@ -1718,6 +1731,19 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / m_nHyperLinkCount++; } + if (m_pContentControl) + { + m_pSerializer->startElementNS(XML_w, XML_sdt); + m_pSerializer->startElementNS(XML_w, XML_sdtPr); + if (m_pContentControl->GetShowingPlaceHolder()) + { + m_pSerializer->singleElementNS(XML_w, XML_showingPlcHdr); + } + m_pSerializer->endElementNS(XML_w, XML_sdtPr); + m_pSerializer->startElementNS(XML_w, XML_sdtContent); + m_pContentControl = nullptr; + } + // if there is some redlining in the document, output it StartRedline( m_pRedlineData ); diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx index 843fc0450bfb..4b4be1ca6160 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -52,6 +52,7 @@ class SwGrfNode; class SdrObject; enum class SvxBoxItemLine; enum class SwLineBreakClear; +class SwContentControl; namespace docx { class FootnotesList; } namespace oox::drawingml { class DrawingML; } @@ -404,6 +405,12 @@ public: void WriteFloatingTable(ww8::Frame const* pParentFrame); + /// See AttributeOutputBase::StartContentControl(). + void StartContentControl(const SwFormatContentControl& rFormatContentControl) override; + + /// See AttributeOutputBase::EndContentControl(). + void EndContentControl() override; + private: /// Initialize the structures where we are going to collect some of the paragraph properties. /// @@ -780,6 +787,7 @@ private: rtl::Reference<sax_fastparser::FastAttributeList> m_pSectionSpacingAttrList; rtl::Reference<sax_fastparser::FastAttributeList> m_pParagraphSpacingAttrList; rtl::Reference<sax_fastparser::FastAttributeList> m_pHyperlinkAttrList; + const SwContentControl* m_pContentControl = nullptr; /// If the current SDT around runs should be ended before the current run. bool m_bEndCharSdt; /// Attributes of the run color @@ -903,6 +911,8 @@ private: o3tl::sorted_vector<const SwFrameFormat*> m_aFloatingTablesOfParagraph; sal_Int32 m_nTextFrameLevel; + sal_Int32 m_nCloseContentControl = 0; + // close of hyperlink needed bool m_closeHyperlinkInThisRun; bool m_closeHyperlinkInPreviousRun; diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx index 5fd077a634e6..36750bf6005a 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -1399,6 +1399,14 @@ int SwWW8AttrIter::OutAttrWithRange(const SwTextNode& rNode, sal_Int32 nPos) --nRet; } break; + case RES_TXTATR_CONTENTCONTROL: + pEnd = pHt->End(); + if (nPos == *pEnd && nPos != pHt->GetStart()) + { + m_rExport.AttrOutput().EndContentControl(); + --nRet; + } + break; } if (nPos < pHt->GetAnyEnd()) break; // sorted by end @@ -1453,6 +1461,17 @@ int SwWW8AttrIter::OutAttrWithRange(const SwTextNode& rNode, sal_Int32 nPos) --nRet; } break; + case RES_TXTATR_CONTENTCONTROL: + if (nPos == pHt->GetStart()) + { + auto pFormatContentControl + = static_cast<const SwFormatContentControl*>(pItem); + m_rExport.AttrOutput().StartContentControl(*pFormatContentControl); + ++nRet; + } + // We know that the content control is never empty as it has a dummy character + // at least. + break; } if (nPos < pHt->GetStart()) break; // sorted by start