include/xmloff/txtparae.hxx | 6 +- xmloff/CppunitTest_xmloff_text.mk | 1 xmloff/qa/unit/text/data/format-charstyle-direct.docx |binary xmloff/qa/unit/text/txtparae.cxx | 53 ++++++++++++++++++ xmloff/source/text/XMLRedlineExport.cxx | 15 ++++- xmloff/source/text/txtparae.cxx | 19 +++++- 6 files changed, 87 insertions(+), 7 deletions(-)
New commits: commit c625247680cd5737723154b9a73c45e786611b44 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Mon Aug 25 09:30:16 2025 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Tue Aug 26 12:32:30 2025 +0200 tdf#167761 sw format redline, char style+direct: add ODT export The bugdoc contains a format redline that changes the character style from Strong to Quote and the font size from 24 to 36. Exporting to ODT, loading the ODT and rejecting the redline, the font size is set to 24, but the character style is set to default instead of Strong. What happens is that the item set of the format redline contains the necessary SvxFontHeightItem, but contains no SwFormatCharFormat. This is because the ODT export writes no parents for char automatic styles, because char formatting outside the redline table has 2 pool items for such char formatting: a dedicated SwFormatCharFormat for the char style and a separate SwFormatAutoFormat for other char props, so the ODT export "knows" to ignore the autoformat parent and creates this markup: <text:span text:style-name="...named style..."><text:span text:style-name="...automatic style...">t</text:span></text:span> Fix the problem by explicitly exporting the parent style name when we write an automatic style that is referred from the redline table (and not from the body text). This is similar to what e.g. "frame" autostyles already do, which also have parents. The result is this markup: <style:style style:name="..." style:family="text" style:parent-style-name="Strong_20_Emphasis"> <style:text-properties fo:font-size="24pt"/> ... <text:format-change loext:style-name="..."> which contains both the named character style name and the automatic style, describing the direct formatting. The ODT import side still needs fixing. Change-Id: I19a67cf5e2c538cfcfa1b13097241954667d2c89 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190181 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/include/xmloff/txtparae.hxx b/include/xmloff/txtparae.hxx index 6df40e45cd3b..74a07f3042c3 100644 --- a/include/xmloff/txtparae.hxx +++ b/include/xmloff/txtparae.hxx @@ -195,7 +195,8 @@ public: const css::uno::Reference< css::beans::XPropertySet > & rPropSet, bool& rbHasCharStyle, bool& rbHasAutoStyle, - const XMLPropertyState** pAddState = nullptr) const; + const XMLPropertyState** pAddState = nullptr, + const OUString* pParentName = nullptr) const; void exportTextRangeEnumeration( const css::uno::Reference< css::container::XEnumeration > & rRangeEnum, @@ -400,7 +401,8 @@ public: void Add( XmlStyleFamily nFamily, const css::uno::Reference< css::beans::XPropertySet > & rPropSet, - std::span<const XMLPropertyState> aAddStates = {} ); + std::span<const XMLPropertyState> aAddStates = {}, + bool bCheckParent = false ); /// find style name for specified family and parent OUString Find( diff --git a/xmloff/CppunitTest_xmloff_text.mk b/xmloff/CppunitTest_xmloff_text.mk index 73e5dbb8eaaa..47bfd0ab4696 100644 --- a/xmloff/CppunitTest_xmloff_text.mk +++ b/xmloff/CppunitTest_xmloff_text.mk @@ -19,6 +19,7 @@ $(eval $(call gb_CppunitTest_use_externals,xmloff_text,\ $(eval $(call gb_CppunitTest_add_exception_objects,xmloff_text, \ xmloff/qa/unit/text \ xmloff/qa/unit/text/txtprmap \ + xmloff/qa/unit/text/txtparae \ )) $(eval $(call gb_CppunitTest_use_libraries,xmloff_text, \ diff --git a/xmloff/qa/unit/text/data/format-charstyle-direct.docx b/xmloff/qa/unit/text/data/format-charstyle-direct.docx new file mode 100644 index 000000000000..5f973391a7d1 Binary files /dev/null and b/xmloff/qa/unit/text/data/format-charstyle-direct.docx differ diff --git a/xmloff/qa/unit/text/txtparae.cxx b/xmloff/qa/unit/text/txtparae.cxx new file mode 100644 index 000000000000..046a987e31e6 --- /dev/null +++ b/xmloff/qa/unit/text/txtparae.cxx @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <test/unoapixml_test.hxx> + +using namespace com::sun::star; + +namespace +{ +/// Covers xmloff/source/text/txtparae.cxx fixes. +class Test : public UnoApiXmlTest +{ +public: + Test(); +}; + +Test::Test() + : UnoApiXmlTest(u"/xmloff/qa/unit/text/data/"_ustr) +{ +} + +CPPUNIT_TEST_FIXTURE(Test, testRedlineFormatCharStyleDirectExport) +{ + // Given a document that contains a format redline: both the character style and direct + // formatting changes: + loadFromFile(u"format-charstyle-direct.docx"); + + // When exporting the document to ODT: + save(u"writer8"_ustr); + + // Then make sure the named style is not lost in the redline: + xmlDocUniquePtr pXmlDoc = parseExport(u"content.xml"_ustr); + OString aAutoName + = getXPath(pXmlDoc, "//text:tracked-changes/text:changed-region/text:format-change", + "style-name") + .toUtf8(); + // Without the accompanying fix in place, this test would have failed with: + // - XPath '//office:automatic-styles/style:style[@style:name='T2']' no attribute 'parent-style-name' exist + // i.e. the named character style in the format redline was lost. + OUString aParentName = getXPath( + pXmlDoc, "//office:automatic-styles/style:style[@style:name='" + aAutoName + "']", + "parent-style-name"); + CPPUNIT_ASSERT_EQUAL(u"Strong_20_Emphasis"_ustr, aParentName); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/text/XMLRedlineExport.cxx b/xmloff/source/text/XMLRedlineExport.cxx index 9eab647b1662..f85d9671878b 100644 --- a/xmloff/source/text/XMLRedlineExport.cxx +++ b/xmloff/source/text/XMLRedlineExport.cxx @@ -269,7 +269,10 @@ void XMLRedlineExport::ExportChangeAutoStyle( aAny >>= xAutoFormat; if (xAutoFormat.is()) { - rExport.GetTextParagraphExport()->Add(XmlStyleFamily::TEXT_TEXT, xAutoFormat); + // Check for parent style when declaring the automatic style. + rExport.GetTextParagraphExport()->Add(XmlStyleFamily::TEXT_TEXT, xAutoFormat, + /*aAddStates=*/{}, + /*bCheckParent=*/true); } } @@ -373,7 +376,15 @@ void XMLRedlineExport::ExportChangedRegion( { bool bIsUICharStyle; bool bHasAutoStyle; - OUString sStyle = rExport.GetTextParagraphExport()->FindTextStyle(xAutoStyle, bIsUICharStyle, bHasAutoStyle); + OUString aParentName; + uno::Reference<beans::XPropertySetInfo> xPropSetInfo = xAutoStyle->getPropertySetInfo(); + if (xPropSetInfo->hasPropertyByName("CharStyleName")) + { + // Consider parent style when referring to the declared automatic style. + xAutoStyle->getPropertyValue("CharStyleName") >>= aParentName; + } + OUString sStyle = rExport.GetTextParagraphExport()->FindTextStyle( + xAutoStyle, bIsUICharStyle, bHasAutoStyle, /*pAddState=*/nullptr, &aParentName); if (!sStyle.isEmpty()) { rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_STYLE_NAME, rExport.EncodeStyleName(sStyle)); diff --git a/xmloff/source/text/txtparae.cxx b/xmloff/source/text/txtparae.cxx index 3ac362785ad5..fd9e76be62f3 100644 --- a/xmloff/source/text/txtparae.cxx +++ b/xmloff/source/text/txtparae.cxx @@ -653,7 +653,8 @@ void FieldParamExporter::ExportParameter(const OUString& sKey, const OUString& s void XMLTextParagraphExport::Add( XmlStyleFamily nFamily, const Reference < XPropertySet > & rPropSet, - const std::span<const XMLPropertyState> aAddStates ) + const std::span<const XMLPropertyState> aAddStates, + bool bCheckParent ) { rtl::Reference < SvXMLExportPropertyMapper > xPropMapper; switch( nFamily ) @@ -737,6 +738,11 @@ void XMLTextParagraphExport::Add( XmlStyleFamily nFamily, break; case XmlStyleFamily::TEXT_TEXT: { + if (bCheckParent && xPropSetInfo->hasPropertyByName(gsCharStyleName)) + { + rPropSet->getPropertyValue(gsCharStyleName) >>= sParent; + } + // Get parent and remove hyperlinks (they aren't of interest) rtl::Reference< XMLPropertySetMapper > xPM(xPropMapper->getPropertySetMapper()); sal_uInt16 nIgnoreProps = 0; @@ -912,7 +918,8 @@ OUString XMLTextParagraphExport::FindTextStyle( const Reference < XPropertySet > & rPropSet, bool& rbHasCharStyle, bool& rbHasAutoStyle, - const XMLPropertyState** ppAddStates ) const + const XMLPropertyState** ppAddStates, + const OUString* pParentName) const { rtl::Reference < SvXMLExportPropertyMapper > xPropMapper(GetTextPropMapper()); std::vector<XMLPropertyState> aPropStates(xPropMapper->Filter(GetExport(), rPropSet)); @@ -975,9 +982,15 @@ OUString XMLTextParagraphExport::FindTextStyle( aPropStates.erase( aSecondDel ); aPropStates.erase( aFirstDel ); } + OUString aParentName; + if (pParentName) + { + // Format redlines can have an autostyle with a parent. + aParentName = *pParentName; + } sName = GetAutoStylePool().Find( XmlStyleFamily::TEXT_TEXT, - OUString(), // AutoStyles should not have parents! + aParentName, aPropStates ); rbHasAutoStyle = true; }