sw/qa/extras/rtfexport/data/tdf167569.rtf   |    9 ++++
 sw/qa/extras/rtfexport/rtfexport8.cxx       |   51 ++++++++++++++++++++++++++++
 sw/source/filter/ww8/rtfattributeoutput.cxx |   44 +++++++++++++++++++++++-
 sw/source/filter/ww8/rtfattributeoutput.hxx |    2 +
 4 files changed, 104 insertions(+), 2 deletions(-)

New commits:
commit 7f4868348c14b305fcd75744e1e3544d0d3a5d61
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Sat Jul 19 14:26:48 2025 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Tue Jul 22 22:39:52 2025 +0200

    tdf#167569: RTF: Export paragraph mark properties
    
    In commits:
    
        5ba30f588d6e41a13d68b1461345fca7a7ca61ac
        tdf#64222 sw: better DOCX import/export of paragraph marker formatting
        2019-09-17
    
        6e1c8bcec511444d2d51c5c5143be56d1900e5e6
        tdf#150613 sw: better DOC import of paragraph marker formatting
        2022-09-20
    
        6249858a8972aef077e0249bd93cfe8f01bce4d6
        sw: ODT import/export of DOCX's paragraph marker formatting
        2022-12-19
    
        a25eda715591cfa96136bcfd95360156516239d1
        tdf#131386 writerfilter: RTF import paragraph mark formatting
        2023-01-13
    
    import and export of paragraph marker (as defined by Word and its file 
formats)
    was implemented, except for export to RTF. This change implements the 
latter.
    
    Change-Id: I2208a416377be42d2e6c5d5042cb92bc8109b4c5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188084
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/extras/rtfexport/data/tdf167569.rtf 
b/sw/qa/extras/rtfexport/data/tdf167569.rtf
new file mode 100644
index 000000000000..b82e4fd2d3d9
--- /dev/null
+++ b/sw/qa/extras/rtfexport/data/tdf167569.rtf
@@ -0,0 +1,9 @@
+{ tf1
+{onttbl{0romancharset0prq2 Liberation Serif;}}
+{\*\defchp s24}
+{\*\listtable{\list\listtemplateid1{\listlevel\levelnfc23\leveljc0\levelfollow0{\leveltext
 \'01\u183 ?;}0}\listid1}}
+{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}}
+{\*\pnseclvl1\pnucrm\pnqc\pnstart1\pnindent720\pnhang {\pntxta .}}
+{\listtext\pard\plain s16 \u183\'b7   ab}\pard i-360\li720 i0\sa60\ls1 
in0\lin720 {s12 AAA\par}
+{\listtext\pard\plain s16 \u183\'b7   ab}\pard i-360\li720 i0\sa60\ls1 
in0\lin720 {s12 BBB\par}
+\pard \par}
\ No newline at end of file
diff --git a/sw/qa/extras/rtfexport/rtfexport8.cxx 
b/sw/qa/extras/rtfexport/rtfexport8.cxx
index b75d44282c17..ff05ce5c4fcd 100644
--- a/sw/qa/extras/rtfexport/rtfexport8.cxx
+++ b/sw/qa/extras/rtfexport/rtfexport8.cxx
@@ -941,6 +941,57 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf167512)
     }
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf167569)
+{
+    // Given an RTF having default text size of 12 pt, with a list, which 
elements' markers get
+    // their size of 6 pt from paragraph marker formatting:
+    createSwDoc("tdf167569.rtf");
+
+    {
+        comphelper::SequenceAsHashMap markerProperties;
+        markerProperties << 
getProperty<uno::Sequence<beans::NamedValue>>(getParagraph(1),
+                                                                          
u"ListAutoFormat"_ustr);
+        CPPUNIT_ASSERT_EQUAL(6.0f, 
markerProperties[u"CharHeight"_ustr].get<float>());
+
+        markerProperties.clear();
+        markerProperties << 
getProperty<uno::Sequence<beans::NamedValue>>(getParagraph(2),
+                                                                          
u"ListAutoFormat"_ustr);
+        CPPUNIT_ASSERT_EQUAL(6.0f, 
markerProperties[u"CharHeight"_ustr].get<float>());
+
+        xmlDocUniquePtr pLayout = parseLayoutDump();
+        auto lineHeight = getXPath(pLayout, "//txt[1]['pass 
1']/infos/prtBounds", "height");
+        CPPUNIT_ASSERT_LESS(sal_Int32(150), lineHeight.toInt32()); // expected 
value is around 140
+        lineHeight = getXPath(pLayout, "//txt[2]['pass 1']/infos/prtBounds", 
"height");
+        CPPUNIT_ASSERT_LESS(sal_Int32(150), lineHeight.toInt32());
+    }
+
+    // After round-tripping the RTF, the marker properties must not get lost; 
previously, the
+    // bullets became large, which additionally increased overall line height.
+    saveAndReload(mpFilter);
+
+    {
+        comphelper::SequenceAsHashMap markerProperties;
+        // Before the export was implemented, this failed with
+        // - the property is of unexpected type or void: ListAutoFormat
+        // meaning that the paragraph marker property was lost
+        markerProperties << 
getProperty<uno::Sequence<beans::NamedValue>>(getParagraph(1),
+                                                                          
u"ListAutoFormat"_ustr);
+        CPPUNIT_ASSERT_EQUAL(6.0f, 
markerProperties[u"CharHeight"_ustr].get<float>());
+
+        markerProperties.clear();
+        markerProperties << 
getProperty<uno::Sequence<beans::NamedValue>>(getParagraph(2),
+                                                                          
u"ListAutoFormat"_ustr);
+        CPPUNIT_ASSERT_EQUAL(6.0f, 
markerProperties[u"CharHeight"_ustr].get<float>());
+
+        xmlDocUniquePtr pLayout = parseLayoutDump();
+        auto lineHeight = getXPath(pLayout, "//txt[1]['pass 
2']/infos/prtBounds", "height");
+        // Before the export was implemented, these were 280
+        CPPUNIT_ASSERT_LESS(sal_Int32(150), lineHeight.toInt32()); // expected 
value is around 140
+        lineHeight = getXPath(pLayout, "//txt[2]['pass 2']/infos/prtBounds", 
"height");
+        CPPUNIT_ASSERT_LESS(sal_Int32(150), lineHeight.toInt32());
+    }
+}
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx 
b/sw/source/filter/ww8/rtfattributeoutput.cxx
index 82643081d739..68b758924ab6 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.cxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.cxx
@@ -91,6 +91,7 @@
 #include <oox/mathml/imexport.hxx>
 #include <com/sun/star/i18n/ScriptType.hpp>
 #include <svl/grabbagitem.hxx>
+#include <svl/itemiter.hxx>
 #include <frmatr.hxx>
 #include <swtable.hxx>
 #include <formatflysplit.hxx>
@@ -310,10 +311,13 @@ void RtfAttributeOutput::EndParagraph(
         // RTF_PAR at the end of the footnote or clipboard, would cause an 
additional empty paragraph.
         if (!bLastPara)
         {
+            if (!m_aParagraphMarkerProperties.isEmpty())
+                aParagraph->append("{" + m_aParagraphMarkerProperties);
             aParagraph->append(OOO_STRING_SVTOOLS_RTF_PAR);
-            aParagraph->append(' ');
+            aParagraph->append(m_aParagraphMarkerProperties.isEmpty() ? ' ' : 
'}');
         }
     }
+    m_aParagraphMarkerProperties.clear();
     if (m_nColBreakNeeded)
     {
         aParagraph->append(OOO_STRING_SVTOOLS_RTF_COLUMN);
@@ -392,8 +396,29 @@ void RtfAttributeOutput::StartParagraphProperties()
         m_aSectionHeaders.append(aPar);
 }
 
+/// Outputs an item set, that contains the formatting of the paragraph marker.
+/// Similar to respective function in 
sw/source/filter/ww8/docxattributeoutput.cxx.
+static void lcl_writeParagraphMarkerProperties(RtfAttributeOutput& 
rAttributeOutput,
+                                               const SfxItemSet& 
rParagraphMarkerProperties)
+{
+    for (SfxItemIter it(rParagraphMarkerProperties); !it.IsAtEnd(); 
it.NextItem())
+    {
+        const auto nWhichId = it.GetCurWhich();
+        if (isCHRATR(nWhichId) || nWhichId == RES_TXTATR_CHARFMT)
+        {
+            rAttributeOutput.OutputItem(*it.GetCurItem());
+        }
+        else if (nWhichId == RES_TXTATR_AUTOFMT)
+        {
+            const SwFormatAutoFormat& rAutoFormat
+                = it.GetCurItem()->StaticWhichCast(RES_TXTATR_AUTOFMT);
+            lcl_writeParagraphMarkerProperties(rAttributeOutput, 
*rAutoFormat.GetStyleHandle());
+        }
+    }
+}
+
 void RtfAttributeOutput::EndParagraphProperties(
-    const SfxItemSet& /*rParagraphMarkerProperties*/, const SwRedlineData* 
/*pRedlineData*/,
+    const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* 
/*pRedlineData*/,
     const SwRedlineData* /*pRedlineParagraphMarkerDeleted*/,
     const SwRedlineData* /*pRedlineParagraphMarkerInserted*/)
 {
@@ -401,6 +426,21 @@ void RtfAttributeOutput::EndParagraphProperties(
     // Otherwise associate properties in the paragraph style are ruined.
     const OString aProperties = m_aStyles.makeStringAndClear();
     m_rExport.Strm().WriteOString(aProperties);
+
+    // Backup other styles strings, to avoid modification by marker properties
+    OString aStylesAssocHich(m_aStylesAssocHich.makeStringAndClear());
+    OString aStylesAssocDbch(m_aStylesAssocDbch.makeStringAndClear());
+    OString aStylesAssocRtlch(m_aStylesAssocRtlch.makeStringAndClear());
+    OString aStylesAssocLtrch(m_aStylesAssocLtrch.makeStringAndClear());
+
+    lcl_writeParagraphMarkerProperties(*this, rParagraphMarkerProperties);
+    m_aParagraphMarkerProperties = m_aStyles.makeStringAndClear();
+
+    // Restore these
+    m_aStylesAssocHich = aStylesAssocHich;
+    m_aStylesAssocDbch = aStylesAssocDbch;
+    m_aStylesAssocRtlch = aStylesAssocRtlch;
+    m_aStylesAssocLtrch = aStylesAssocLtrch;
 }
 
 void RtfAttributeOutput::StartRun(const SwRedlineData* pRedlineData, sal_Int32 
/*nPos*/,
diff --git a/sw/source/filter/ww8/rtfattributeoutput.hxx 
b/sw/source/filter/ww8/rtfattributeoutput.hxx
index 6f9e2b2d107b..654ee2ab576c 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.hxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.hxx
@@ -669,6 +669,8 @@ private:
 
     editeng::WordPageMargins m_aPageMargins;
 
+    OString m_aParagraphMarkerProperties;
+
 public:
     explicit RtfAttributeOutput(RtfExport& rExport);
 

Reply via email to