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

Reply via email to