sw/qa/extras/ooxmlexport/data/clearing-break.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport17.cxx        |   13 +++++++
 sw/source/filter/ww8/attributeoutputbase.hxx      |    3 +
 sw/source/filter/ww8/docxattributeoutput.cxx      |   36 ++++++++++++++++++++++
 sw/source/filter/ww8/docxattributeoutput.hxx      |    8 ++++
 sw/source/filter/ww8/rtfattributeoutput.cxx       |    2 +
 sw/source/filter/ww8/rtfattributeoutput.hxx       |    2 +
 sw/source/filter/ww8/ww8atr.cxx                   |    5 +++
 sw/source/filter/ww8/ww8attributeoutput.hxx       |    2 +
 9 files changed, 71 insertions(+)

New commits:
commit 280ab11ee40c95d2513e6f23ec5b08a083ecc1c5
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed Mar 9 08:32:24 2022 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Wed Mar 9 09:30:13 2022 +0100

    sw clearing breaks: add DOCX export
    
    Need to do this in two passes, because a clearing line break is a text
    attribute, but the DOCX markup is not a run property, so can only write
    it once the run properties are finished.
    
    Change-Id: I74e94dbd02ca4e6ceee0439c5eafd3c3bbe2264b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131231
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/extras/ooxmlexport/data/clearing-break.docx 
b/sw/qa/extras/ooxmlexport/data/clearing-break.docx
new file mode 100644
index 000000000000..453a4c2b83d1
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/clearing-break.docx 
differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
index 5de93edd3226..7905091de82f 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
@@ -75,6 +75,19 @@ CPPUNIT_TEST_FIXTURE(Test, testParaStyleNumLevel)
     assertXPath(pXmlDoc, 
"/w:styles/w:style[@w:styleId='Mystyle']/w:pPr/w:numPr/w:ilvl", "val", "1");
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testClearingBreak)
+{
+    // Given a document with a clearing break, when saving to DOCX:
+    loadAndSave("clearing-break.docx");
+
+    // Then make sure that the clearing break is not lost:
+    xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
+    // Without the accompanying fix in place, this test would have failed with:
+    // - XPath '/w:document/w:body/w:p/w:r/w:br' number of nodes is incorrect
+    // i.e. first the clearing break was turned into a plain break, then it 
was completely lost.
+    assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:r/w:br", "clear", "all");
+}
+
 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 1694b9335cb5..8461f2e1510c 100644
--- a/sw/source/filter/ww8/attributeoutputbase.hxx
+++ b/sw/source/filter/ww8/attributeoutputbase.hxx
@@ -507,6 +507,9 @@ protected:
     /// Sfx item RES_TXTATR_FTN
     virtual void TextFootnote_Impl( const SwFormatFootnote& ) = 0;
 
+    /// RES_TXTATR_LINEBREAK, i.e. clearing breaks.
+    virtual void TextLineBreak(const SwFormatLineBreak&) = 0;
+
     /// Sfx item RES_PARATR_LINESPACING
     void ParaLineSpacing( const SvxLineSpacingItem& );
 
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index a08a16919e8d..6f3601ff0815 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -3097,6 +3097,8 @@ void DocxAttributeOutput::EndRunProperties( const 
SwRedlineData* pRedlineData )
     // write footnotes/endnotes if we have any
     FootnoteEndnoteReference();
 
+    WriteLineBreak();
+
     // merge the properties _before_ the run text (strictly speaking, just
     // after the start of the run)
     m_pSerializer->mergeTopMarks(Tag_StartRunProperties, 
sax_fastparser::MergeMarks::PREPEND);
@@ -7186,6 +7188,40 @@ void DocxAttributeOutput::SectionRtlGutter(const 
SfxBoolItem& rRtlGutter)
     m_pSerializer->singleElementNS(XML_w, XML_rtlGutter);
 }
 
+void DocxAttributeOutput::TextLineBreak(const SwFormatLineBreak& rLineBreak)
+{
+    m_oLineBreakClear = rLineBreak.GetValue();
+}
+
+void DocxAttributeOutput::WriteLineBreak()
+{
+    if (!m_oLineBreakClear.has_value())
+    {
+        return;
+    }
+
+    rtl::Reference<FastAttributeList> pAttr = 
FastSerializerHelper::createAttrList();
+    pAttr->add(FSNS(XML_w, XML_type), "textWrapping");
+    switch (*m_oLineBreakClear)
+    {
+        case SwLineBreakClear::NONE:
+            pAttr->add(FSNS(XML_w, XML_clear), "none");
+            break;
+        case SwLineBreakClear::LEFT:
+            pAttr->add(FSNS(XML_w, XML_clear), "left");
+            break;
+        case SwLineBreakClear::RIGHT:
+            pAttr->add(FSNS(XML_w, XML_clear), "right");
+            break;
+        case SwLineBreakClear::ALL:
+            pAttr->add(FSNS(XML_w, XML_clear), "all");
+            break;
+    }
+    m_oLineBreakClear.reset();
+
+    m_pSerializer->singleElementNS(XML_w, XML_br, pAttr);
+}
+
 void DocxAttributeOutput::SectionLineNumbering( sal_uLong nRestartNo, const 
SwLineNumberInfo& rLnNumInfo )
 {
     rtl::Reference<FastAttributeList> pAttr = 
FastSerializerHelper::createAttrList();
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx 
b/sw/source/filter/ww8/docxattributeoutput.hxx
index cac04054f542..934247ab533d 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -51,6 +51,7 @@
 class SwGrfNode;
 class SdrObject;
 enum class SvxBoxItemLine;
+enum class SwLineBreakClear;
 
 namespace docx { class FootnotesList; }
 namespace oox::drawingml { class DrawingML; }
@@ -717,6 +718,11 @@ protected:
 
     void SectionRtlGutter( const SfxBoolItem& rRtlGutter) override;
 
+    void TextLineBreak(const SwFormatLineBreak& rLineBreak) override;
+
+    /// Writes a clearing line break at the end of run properties, if there 
are any.
+    void WriteLineBreak();
+
     /// Reference to the export, where to get the data from
     DocxExport &m_rExport;
 
@@ -1022,6 +1028,8 @@ private:
 
     std::vector<std::map<SvxBoxItemLine, css::table::BorderLine2>> 
m_aTableStyleConfs;
 
+    std::optional<SwLineBreakClear> m_oLineBreakClear;
+
 public:
     DocxAttributeOutput( DocxExport &rExport, const 
::sax_fastparser::FSHelperPtr& pSerializer, oox::drawingml::DrawingML* 
pDrawingML );
 
diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx 
b/sw/source/filter/ww8/rtfattributeoutput.cxx
index 153669189ed6..925b8ddbf016 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.cxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.cxx
@@ -4397,4 +4397,6 @@ void RtfAttributeOutput::SectionRtlGutter(const 
SfxBoolItem& rRtlGutter)
     m_rExport.Strm().WriteCharPtr(LO_STRING_SVTOOLS_RTF_RTLGUTTER);
 }
 
+void RtfAttributeOutput::TextLineBreak(const SwFormatLineBreak& 
/*rLineBreak*/) {}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/rtfattributeoutput.hxx 
b/sw/source/filter/ww8/rtfattributeoutput.hxx
index c07f3e525501..aedef264b7d2 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.hxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.hxx
@@ -462,6 +462,8 @@ protected:
 
     void SectionRtlGutter(const SfxBoolItem& rRtlGutter) override;
 
+    void TextLineBreak(const SwFormatLineBreak& rLineBreak) override;
+
 private:
     /// Reference to the export, where to get the data from
     RtfExport& m_rExport;
diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx
index fcadda82f7c8..b9ec65d4cfc9 100644
--- a/sw/source/filter/ww8/ww8atr.cxx
+++ b/sw/source/filter/ww8/ww8atr.cxx
@@ -4185,6 +4185,8 @@ void WW8AttributeOutput::SectionRtlGutter(const 
SfxBoolItem& rRtlGutter)
     m_rWW8Export.pO->push_back(1);
 }
 
+void WW8AttributeOutput::TextLineBreak(const SwFormatLineBreak& 
/*rLineBreak*/) {}
+
 void WW8AttributeOutput::FormatULSpace( const SvxULSpaceItem& rUL )
 {
     // Flys are still missing ( see RTF )
@@ -5590,6 +5592,9 @@ void AttributeOutputBase::OutputItem( const SfxPoolItem& 
rHt )
         case RES_RTL_GUTTER:
             SectionRtlGutter(static_cast<const SfxBoolItem&>(rHt));
             break;
+        case RES_TXTATR_LINEBREAK:
+            TextLineBreak(static_cast<const SwFormatLineBreak&>(rHt));
+            break;
 
         default:
             SAL_INFO("sw.ww8", "Unhandled SfxPoolItem with id " << rHt.Which() 
);
diff --git a/sw/source/filter/ww8/ww8attributeoutput.hxx 
b/sw/source/filter/ww8/ww8attributeoutput.hxx
index 9bd1b425d39e..f459a7bf8a9c 100644
--- a/sw/source/filter/ww8/ww8attributeoutput.hxx
+++ b/sw/source/filter/ww8/ww8attributeoutput.hxx
@@ -436,6 +436,8 @@ protected:
 
     void SectionRtlGutter( const SfxBoolItem& rRtlGutter) override;
 
+    void TextLineBreak(const SwFormatLineBreak& rLineBreak) override;
+
     /// Reference to the export, where to get the data from
     WW8Export &m_rWW8Export;
 

Reply via email to