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();
