sw/qa/extras/odfexport/data/colored-sections-separators.fodt |   63 +++++++++++
 sw/qa/extras/odfexport/odfexport4.cxx                        |   56 +++++++++
 xmloff/source/text/XMLTextColumnsExport.cxx                  |    1 
 xmloff/source/text/txtprhdl.cxx                              |   40 ++++++
 4 files changed, 160 insertions(+)

New commits:
commit baf57e584653e7bc1b38a914e584ecc4a6ece264
Author:     Mike Kaganski <[email protected]>
AuthorDate: Wed Oct 15 11:08:21 2025 +0500
Commit:     Mike Kaganski <[email protected]>
CommitDate: Sun Oct 19 11:20:23 2025 +0200

    tdf#164677: separator properties in XMLTextColumnsPropertyHandler::equals
    
    When collecting autostyles, each added autostyle is compared with the
    already added ones. If all the properties match, new autostyle is not
    added, but an existing one is used.
    
    For text columns, the comparison procedure only compared things defined
    in XTextColumns interface, and ignored all properties of TextColumns
    service - so, styles only differing in those properties were considered
    identical, and only the first of them was saved and used for all cases.
    
    This had never been implemented properly; all versions I tested, did the
    same, so I doubt it was a regression. But indeed, it was a bug.
    
    Change-Id: I0042067499a904a66ffb10b4f4ce4115d86e6cfb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192639
    Reviewed-by: Mike Kaganski <[email protected]>
    Tested-by: Jenkins

diff --git a/sw/qa/extras/odfexport/data/colored-sections-separators.fodt 
b/sw/qa/extras/odfexport/data/colored-sections-separators.fodt
new file mode 100644
index 000000000000..640d1bc504e7
--- /dev/null
+++ b/sw/qa/extras/odfexport/data/colored-sections-separators.fodt
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:automatic-styles>
+  <style:style style:name="SectRedThinSolid" style:family="section">
+   <style:section-properties text:dont-balance-text-columns="false" 
style:editable="false">
+    <style:columns fo:column-count="2" fo:column-gap="1cm">
+     <style:column-sep style:width="0.3mm" style:color="#ff0000" 
style:height="100%" style:style="solid"/>
+     <style:column style:rel-width="32767*" fo:start-indent="0pt" 
fo:end-indent="5mm"/>
+     <style:column style:rel-width="32768*" fo:start-indent="5mm" 
fo:end-indent="0pt"/>
+    </style:columns>
+   </style:section-properties>
+  </style:style>
+  <style:style style:name="SectBlueThinSolid" style:family="section">
+   <style:section-properties text:dont-balance-text-columns="false" 
style:editable="false">
+    <style:columns fo:column-count="2" fo:column-gap="1cm">
+     <style:column-sep style:width="0.3mm" style:color="#0000ff" 
style:height="100%" style:style="solid"/>
+     <style:column style:rel-width="32767*" fo:start-indent="0pt" 
fo:end-indent="5mm"/>
+     <style:column style:rel-width="32768*" fo:start-indent="5mm" 
fo:end-indent="0pt"/>
+    </style:columns>
+   </style:section-properties>
+  </style:style>
+  <style:style style:name="SectBlueThickSolid" style:family="section">
+   <style:section-properties text:dont-balance-text-columns="false" 
style:editable="false">
+    <style:columns fo:column-count="2" fo:column-gap="1cm">
+     <style:column-sep style:width="3mm" style:color="#0000ff" 
style:height="100%" style:style="solid"/>
+     <style:column style:rel-width="32767*" fo:start-indent="0pt" 
fo:end-indent="5mm"/>
+     <style:column style:rel-width="32768*" fo:start-indent="5mm" 
fo:end-indent="0pt"/>
+    </style:columns>
+   </style:section-properties>
+  </style:style>
+  <style:style style:name="SectBlueThickDotted" style:family="section">
+   <style:section-properties text:dont-balance-text-columns="false" 
style:editable="false">
+    <style:columns fo:column-count="2" fo:column-gap="1cm">
+     <style:column-sep style:width="3mm" style:color="#0000ff" 
style:height="100%" style:style="dotted"/>
+     <style:column style:rel-width="32767*" fo:start-indent="0pt" 
fo:end-indent="5mm"/>
+     <style:column style:rel-width="32768*" fo:start-indent="5mm" 
fo:end-indent="0pt"/>
+    </style:columns>
+   </style:section-properties>
+  </style:style>
+ </office:automatic-styles>
+ <office:body>
+  <office:text>
+   <text:p>Section 1</text:p>
+   <text:section text:style-name="SectRedThinSolid" 
text:name="SectionRedThinSolid">
+    <text:p>Red thin solid separator</text:p>
+   </text:section>
+   <text:p>Section 2</text:p>
+   <text:section text:style-name="SectBlueThinSolid" 
text:name="SectionBlueThinSolid">
+    <text:p>Blue thin solid separator</text:p>
+   </text:section>
+   <text:p>Section 3</text:p>
+   <text:section text:style-name="SectBlueThickSolid" 
text:name="SectionBlueThickSolid">
+    <text:p>Blue thick solid separator</text:p>
+   </text:section>
+   <text:p>Section 4</text:p>
+   <text:section text:style-name="SectBlueThickDotted" 
text:name="SectionBlueThickDotted">
+    <text:p>Blue thick dotted separator</text:p>
+   </text:section>
+   <text:p/>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/odfexport/odfexport4.cxx 
b/sw/qa/extras/odfexport/odfexport4.cxx
index a978447b0914..d826233c9538 100644
--- a/sw/qa/extras/odfexport/odfexport4.cxx
+++ b/sw/qa/extras/odfexport/odfexport4.cxx
@@ -20,6 +20,7 @@
 #include <com/sun/star/text/XTextField.hpp>
 #include <com/sun/star/text/XTextFieldsSupplier.hpp>
 #include <com/sun/star/text/XTextGraphicObjectsSupplier.hpp>
+#include <com/sun/star/text/XTextSectionsSupplier.hpp>
 #include <com/sun/star/text/XTextTable.hpp>
 #include <com/sun/star/text/XTextTablesSupplier.hpp>
 #include <com/sun/star/util/XRefreshable.hpp>
@@ -1573,6 +1574,61 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf168817)
     assertXPathContent(pXmlDoc, "//text:p/text:ruby/text:ruby-text", u"ついたち");
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf164677)
+{
+    // Several sections, which only differ in separator line properties: test 
that the properties
+    // correctly roundtrip. Before the fix, export code didn't see the 
difference between sections'
+    // properties, and generated a single section autostyle, so all the 
sections got identical
+    // properties after reload:
+    loadAndReload("colored-sections-separators.fodt");
+    auto xTextSections = 
mxComponent.queryThrow<text::XTextSectionsSupplier>()->getTextSections();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), 
xTextSections->getElementNames().getLength());
+
+    // Red thin solid separator
+    auto aSect = xTextSections->getByName(u"SectionRedThinSolid"_ustr);
+    auto xColumn = getProperty<uno::Reference<text::XTextColumns>>(aSect, 
u"TextColumns"_ustr);
+    CPPUNIT_ASSERT(getProperty<bool>(xColumn, u"SeparatorLineIsOn"_ustr));
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0xff0000),
+                         getProperty<sal_Int32>(xColumn, 
u"SeparatorLineColor"_ustr));
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(30),
+                         getProperty<sal_Int32>(xColumn, 
u"SeparatorLineWidth"_ustr));
+    CPPUNIT_ASSERT_EQUAL(text::ColumnSeparatorStyle::SOLID,
+                         getProperty<sal_Int16>(xColumn, 
u"SeparatorLineStyle"_ustr));
+
+    // Blue thin solid separator
+    aSect = xTextSections->getByName(u"SectionBlueThinSolid"_ustr);
+    xColumn = getProperty<uno::Reference<text::XTextColumns>>(aSect, 
u"TextColumns"_ustr);
+    CPPUNIT_ASSERT(getProperty<bool>(xColumn, u"SeparatorLineIsOn"_ustr));
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0x0000ff),
+                         getProperty<sal_Int32>(xColumn, 
u"SeparatorLineColor"_ustr));
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(30),
+                         getProperty<sal_Int32>(xColumn, 
u"SeparatorLineWidth"_ustr));
+    CPPUNIT_ASSERT_EQUAL(text::ColumnSeparatorStyle::SOLID,
+                         getProperty<sal_Int16>(xColumn, 
u"SeparatorLineStyle"_ustr));
+
+    // Blue thick solid separator
+    aSect = xTextSections->getByName(u"SectionBlueThickSolid"_ustr);
+    xColumn = getProperty<uno::Reference<text::XTextColumns>>(aSect, 
u"TextColumns"_ustr);
+    CPPUNIT_ASSERT(getProperty<bool>(xColumn, u"SeparatorLineIsOn"_ustr));
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0x0000ff),
+                         getProperty<sal_Int32>(xColumn, 
u"SeparatorLineColor"_ustr));
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(300),
+                         getProperty<sal_Int32>(xColumn, 
u"SeparatorLineWidth"_ustr));
+    CPPUNIT_ASSERT_EQUAL(text::ColumnSeparatorStyle::SOLID,
+                         getProperty<sal_Int16>(xColumn, 
u"SeparatorLineStyle"_ustr));
+
+    // Blue thick dotted separator
+    aSect = xTextSections->getByName(u"SectionBlueThickDotted"_ustr);
+    xColumn = getProperty<uno::Reference<text::XTextColumns>>(aSect, 
u"TextColumns"_ustr);
+    CPPUNIT_ASSERT(getProperty<bool>(xColumn, u"SeparatorLineIsOn"_ustr));
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0x0000ff),
+                         getProperty<sal_Int32>(xColumn, 
u"SeparatorLineColor"_ustr));
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(300),
+                         getProperty<sal_Int32>(xColumn, 
u"SeparatorLineWidth"_ustr));
+    CPPUNIT_ASSERT_EQUAL(text::ColumnSeparatorStyle::DOTTED,
+                         getProperty<sal_Int16>(xColumn, 
u"SeparatorLineStyle"_ustr));
+}
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/xmloff/source/text/XMLTextColumnsExport.cxx 
b/xmloff/source/text/XMLTextColumnsExport.cxx
index ec80f2ed90cc..3aeb38bd0505 100644
--- a/xmloff/source/text/XMLTextColumnsExport.cxx
+++ b/xmloff/source/text/XMLTextColumnsExport.cxx
@@ -58,6 +58,7 @@ XMLTextColumnsExport::XMLTextColumnsExport( SvXMLExport& rExp 
) :
 {
 }
 
+// Cf. XMLTextColumnsPropertyHandler::equals
 void XMLTextColumnsExport::exportXML( const Any& rAny )
 {
     Reference < XTextColumns > xColumns;
diff --git a/xmloff/source/text/txtprhdl.cxx b/xmloff/source/text/txtprhdl.cxx
index a3e6af3d82fb..7c02820dd5ed 100644
--- a/xmloff/source/text/txtprhdl.cxx
+++ b/xmloff/source/text/txtprhdl.cxx
@@ -24,6 +24,7 @@
 #include <rtl/ustrbuf.hxx>
 #include <sal/log.hxx>
 #include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
 #include <com/sun/star/style/DropCapFormat.hpp>
 #include <com/sun/star/text/FontRelief.hpp>
 #include <com/sun/star/text/WrapTextMode.hpp>
@@ -657,6 +658,7 @@ 
XMLTextColumnsPropertyHandler::~XMLTextColumnsPropertyHandler ()
 {
 }
 
+// Cf. XMLTextColumnsExport::exportXML
 bool XMLTextColumnsPropertyHandler::equals(
         const Any& r1,
         const Any& r2 ) const
@@ -674,6 +676,44 @@ bool XMLTextColumnsPropertyHandler::equals(
           xColumns1->getReferenceValue() != xColumns2->getReferenceValue() )
         return false;
 
+    // Check properties of service TextColumns
+    auto xPropSet1 = r1.query<beans::XPropertySet>();
+    auto xPropSet2 = r2.query<beans::XPropertySet>();
+    if (xPropSet1.is() != xPropSet2.is())
+        return false;
+    if (xPropSet1) // here implies xPropSet2
+    {
+        Any aVal;
+        auto cfPropValues = [&xPropSet1, &xPropSet2, &aVal](const OUString& 
prop)
+        {
+            aVal = xPropSet1->getPropertyValue(prop);
+            return aVal == xPropSet2->getPropertyValue(prop);
+        };
+
+        if (!cfPropValues(u"IsAutomatic"_ustr))
+            return false;
+        if (aVal == true)
+            if (!cfPropValues(u"AutomaticDistance"_ustr))
+                return false;
+
+        if (!cfPropValues(u"SeparatorLineIsOn"_ustr))
+            return false;
+        if (aVal == true)
+        {
+            if (!cfPropValues(u"SeparatorLineWidth"_ustr))
+                return false;
+            if (!cfPropValues(u"SeparatorLineColor"_ustr))
+                return false;
+            if (!cfPropValues(u"SeparatorLineRelativeHeight"_ustr))
+                return false;
+            // Optional? XMLTextColumnsExport::exportXML accesses it without 
precautions.
+            if (!cfPropValues(u"SeparatorLineStyle"_ustr))
+                return false;
+            if (!cfPropValues(u"SeparatorLineVerticalAlignment"_ustr))
+                return false;
+        }
+    }
+
     const Sequence < TextColumn > aColumns1 = xColumns1->getColumns();
     const Sequence < TextColumn > aColumns2 = xColumns2->getColumns();
 

Reply via email to