include/xmloff/txtimp.hxx                                   |    3 
 schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng |   12 
 sw/inc/unoprnms.hxx                                         |    1 
 sw/source/core/unocore/unoredline.cxx                       |  168 ++++++++++++
 sw/source/filter/xml/XMLRedlineImportHelper.cxx             |   21 +
 sw/source/filter/xml/XMLRedlineImportHelper.hxx             |    3 
 sw/source/filter/xml/xmltexti.cxx                           |    5 
 sw/source/filter/xml/xmltexti.hxx                           |    3 
 xmloff/qa/unit/data/redline-format-char-props.docx          |binary
 xmloff/qa/unit/data/redline-format-char-props.odt           |binary
 xmloff/qa/unit/text.cxx                                     |   48 +++
 xmloff/source/text/XMLChangedRegionImportContext.cxx        |   32 ++
 xmloff/source/text/XMLChangedRegionImportContext.hxx        |    2 
 xmloff/source/text/XMLRedlineExport.cxx                     |   27 +
 xmloff/source/text/txtimp.cxx                               |    3 
 15 files changed, 318 insertions(+), 10 deletions(-)

New commits:
commit 3b504086e2446d566749638882b4f73c7eeb1e3f
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon Aug 18 08:38:09 2025 +0200
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Thu Aug 21 00:08:27 2025 +0200

    tdf#167761 sw format redline, char props: implement ODF import
    
    Load the bugdoc, save as ODF, open the ODF, revert the format redline,
    the font size should go from 36pt (new direct format) to 24pt (old
    direct format), but it goes to 12pt (doc default).
    
    What happens is that the format redline specifies the old direct format
    via <text:format-change loext:style-name="..."> (referring to an
    automatic character style), but this was not handled on import.
    
    Fix the problem by:
    1) Handling the new attribute in
       XMLChangedRegionImportContext::createFastChildContext()
    2) Looking up the already imported autostyle for the style name in
       XMLChangedRegionImportContext::SetChangeInfo()
    3) Creating a matching SwRedlineExtraData_FormatColl in
       XMLRedlineImportHelper::InsertIntoDocument() when the format redline
       is created in sw/ core.
    
    Note that this means we have two autostyle names at play, the first is
    T<number> and the second is effectively a pointer address as a string,
    like 3cb7b270; FindAutoCharStyle() mapping from the first to the second.
    Unfortunately both are just string references to the already imported
    autostyle.
    
    Change-Id: Ib0f18bad223f4320afe5aaa11ea713e610f6c029
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189845
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189909

diff --git a/include/xmloff/txtimp.hxx b/include/xmloff/txtimp.hxx
index e97d0dfac7b3..cb9b44145049 100644
--- a/include/xmloff/txtimp.hxx
+++ b/include/xmloff/txtimp.hxx
@@ -382,7 +382,8 @@ public:
             /// move id, to find other parts (moveFrom/MoveTo)
             const OUString& rMoveId,
             /// merge last paras
-            bool bMergeLastParagraph);
+            bool bMergeLastParagraph,
+            const OUString& rAutoStyleName);
 
     virtual css::uno::Reference< css::text::XTextCursor> RedlineCreateText(
             /// needed to get the document
diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.cxx 
b/sw/source/filter/xml/XMLRedlineImportHelper.cxx
index 5eac6501fe25..faece01160a8 100644
--- a/sw/source/filter/xml/XMLRedlineImportHelper.cxx
+++ b/sw/source/filter/xml/XMLRedlineImportHelper.cxx
@@ -43,6 +43,7 @@
 #include <o3tl/any.hxx>
 #include <xmloff/xmltoken.hxx>
 #include <vcl/svapp.hxx>
+#include <istyleaccess.hxx>
 
 using namespace ::com::sun::star;
 using namespace ::com::sun::star::uno;
@@ -190,6 +191,7 @@ public:
     util::DateTime aDateTime;       // change DateTime
     OUString sMovedID;              // change move id string
     bool bMergeLastParagraph;   // the SwRangeRedline::IsDelLastPara flag
+    OUString m_aAutoStyleName;
 
     // each position can may be either empty, an XTextRange, or an SwNodeIndex
 
@@ -371,7 +373,8 @@ void XMLRedlineImportHelper::Add(
     const OUString& rComment,
     const util::DateTime& rDateTime,
     const OUString& rMovedID,
-    bool bMergeLastPara)
+    bool bMergeLastPara,
+    const OUString& rAutoStyleName)
 {
     // we need to do the following:
     // 1) parse type string
@@ -410,6 +413,7 @@ void XMLRedlineImportHelper::Add(
     pInfo->aDateTime = rDateTime;
     pInfo->sMovedID = rMovedID;
     pInfo->bMergeLastParagraph = bMergeLastPara;
+    pInfo->m_aAutoStyleName = rAutoStyleName;
 
     //reserve MoveID so it won't be reused by others
     if (!rMovedID.isEmpty())
@@ -789,6 +793,21 @@ void 
XMLRedlineImportHelper::InsertIntoDocument(RedlineInfo* pRedlineInfo)
                 SAL_WARN("sw", "Recursive change tracking");
         }
 
+        // Create the redline's extra data if we have a matching autostyle.
+        if (!pRedlineInfo->m_aAutoStyleName.isEmpty())
+        {
+            IStyleAccess& rStyleAccess = pDoc->GetIStyleAccess();
+            std::shared_ptr<SfxItemSet> pAutoStyle = rStyleAccess.getByName(
+                pRedlineInfo->m_aAutoStyleName, IStyleAccess::AUTO_STYLE_CHAR);
+            if (pAutoStyle)
+            {
+                sal_uInt16 nPoolFormatId = USHRT_MAX;
+                SwRedlineExtraData_FormatColl aExtraData(UIName(u""_ustr), 
nPoolFormatId, pAutoStyle);
+                // aExtraData is copied here.
+                pRedline->SetExtraData(&aExtraData);
+            }
+        }
+
         // set redline mode (without doing the associated book-keeping)
         
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::On);
         pDoc->getIDocumentRedlineAccess().AppendRedline(pRedline, false);
diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.hxx 
b/sw/source/filter/xml/XMLRedlineImportHelper.hxx
index 63d34d405f62..17228eaed57b 100644
--- a/sw/source/filter/xml/XMLRedlineImportHelper.hxx
+++ b/sw/source/filter/xml/XMLRedlineImportHelper.hxx
@@ -81,7 +81,8 @@ public:
         const OUString& rComment,    // redline comment
         const css::util::DateTime& rDateTime, // date+time
         const OUString& rMovedID,     // redline move id
-        bool bMergeLastParagraph);      // merge last paragraph?
+        bool bMergeLastParagraph, // merge last paragraph?
+        const OUString& rAutoStyleName);
 
     // create a text section for the redline, and return an
     // XText/XTextCursor that may be used to write into it.
diff --git a/sw/source/filter/xml/xmltexti.cxx 
b/sw/source/filter/xml/xmltexti.cxx
index 0005c2f0712c..1baa124fb6df 100644
--- a/sw/source/filter/xml/xmltexti.cxx
+++ b/sw/source/filter/xml/xmltexti.cxx
@@ -925,13 +925,14 @@ void SwXMLTextImportHelper::RedlineAdd(
     const OUString& rComment,
     const util::DateTime& rDateTime,
     const OUString& rMovedID,
-    bool bMergeLastPara)
+    bool bMergeLastPara,
+    const OUString& rAutoStyleName)
 {
     // create redline helper on demand
     OSL_ENSURE(nullptr != m_pRedlineHelper, "helper should have been created 
in constructor");
     if (nullptr != m_pRedlineHelper)
         m_pRedlineHelper->Add(rType, rId, rAuthor, rComment, rDateTime, 
rMovedID,
-                            bMergeLastPara);
+                            bMergeLastPara, rAutoStyleName);
 }
 
 uno::Reference<XTextCursor> SwXMLTextImportHelper::RedlineCreateText(
diff --git a/sw/source/filter/xml/xmltexti.hxx 
b/sw/source/filter/xml/xmltexti.hxx
index 886ce4c7ac1c..ccb06a53ac22 100644
--- a/sw/source/filter/xml/xmltexti.hxx
+++ b/sw/source/filter/xml/xmltexti.hxx
@@ -91,7 +91,8 @@ public:
         const OUString& rComment,    /// redline comment
         const css::util::DateTime& rDateTime,  /// date+time
         const OUString& rMovedID,    /// redline move id, to find 
moveFrom/MoveTo parts
-        bool bMergeLastPara) override;           /// merge last paragraph
+        bool bMergeLastPara,         /// merge last paragraph
+        const OUString& rAutoStyleName) override;
     virtual css::uno::Reference<css::text::XTextCursor> RedlineCreateText(
             css::uno::Reference<css::text::XTextCursor> & rOldCursor, /// 
needed to get the document
             const OUString& rId) override;    /// ID used to RedlineAdd() call
diff --git a/xmloff/qa/unit/data/redline-format-char-props.odt 
b/xmloff/qa/unit/data/redline-format-char-props.odt
new file mode 100644
index 000000000000..3d6a63c9f622
Binary files /dev/null and b/xmloff/qa/unit/data/redline-format-char-props.odt 
differ
diff --git a/xmloff/qa/unit/text.cxx b/xmloff/qa/unit/text.cxx
index 798e311be507..a6a90f0587c9 100644
--- a/xmloff/qa/unit/text.cxx
+++ b/xmloff/qa/unit/text.cxx
@@ -21,6 +21,7 @@
 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
 #include <com/sun/star/text/XTextFramesSupplier.hpp>
+#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
 
 #include <comphelper/propertysequence.hxx>
 #include <comphelper/propertyvalue.hxx>
@@ -1307,7 +1308,7 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, 
testDeleteThenFormatOdtExport)
     assertXPath(pXmlDoc, 
"//text:tracked-changes/text:changed-region[2]/text:deletion/text:p");
 }
 
-CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testRedlineFormatCharProps)
+CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testRedlineFormatCharPropsExport)
 {
     // Given a document with a format redline, the redline contains the old 
char props:
     loadFromFile(u"redline-format-char-props.docx");
@@ -1326,6 +1327,32 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, 
testRedlineFormatCharProps)
     CPPUNIT_ASSERT(!aStyleName.isEmpty());
 }
 
+CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testRedlineFormatCharPropsImport)
+{
+    // Given a document with a format redline, the redline contains the old 
char props:
+    loadFromFile(u"redline-format-char-props.odt");
+
+    // When rejecting the format redline:
+    dispatchCommand(mxComponent, u".uno:NextTrackedChange"_ustr, {});
+    dispatchCommand(mxComponent, u".uno:RejectTrackedChange"_ustr, {});
+
+    // Then make sure the old direct format is restored, not the doc default:
+    uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
+    CPPUNIT_ASSERT(xModel.is());
+    uno::Reference<text::XTextViewCursorSupplier> 
xController(xModel->getCurrentController(),
+                                                              uno::UNO_QUERY);
+    CPPUNIT_ASSERT(xController.is());
+    uno::Reference<beans::XPropertySet> 
xViewCursor(xController->getViewCursor(), uno::UNO_QUERY);
+    CPPUNIT_ASSERT(xViewCursor.is());
+    float fCharheight{};
+    xViewCursor->getPropertyValue(u"CharHeight"_ustr) >>= fCharheight;
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 24
+    // - Actual  : 12
+    // i.e. the font size was the doc default, not the old direct format.
+    CPPUNIT_ASSERT_EQUAL(24.f, fCharheight);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmloff/source/text/XMLChangedRegionImportContext.cxx 
b/xmloff/source/text/XMLChangedRegionImportContext.cxx
index 9b0ecbc4b866..b29980528ff2 100644
--- a/xmloff/source/text/XMLChangedRegionImportContext.cxx
+++ b/xmloff/source/text/XMLChangedRegionImportContext.cxx
@@ -29,6 +29,7 @@
 #include <xmloff/xmlimp.hxx>
 #include <xmloff/xmlnamespace.hxx>
 #include <xmloff/xmltoken.hxx>
+#include <xmloff/prstylei.hxx>
 
 
 using namespace ::xmloff::token;
@@ -85,7 +86,7 @@ void XMLChangedRegionImportContext::startFastElement(
 }
 
 css::uno::Reference< css::xml::sax::XFastContextHandler > 
XMLChangedRegionImportContext::createFastChildContext(
-    sal_Int32 nElement, const css::uno::Reference< 
css::xml::sax::XFastAttributeList >&  )
+    sal_Int32 nElement, const css::uno::Reference< 
css::xml::sax::XFastAttributeList >& xAttrList )
 {
     SvXMLImportContextRef xContext;
 
@@ -96,6 +97,19 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > 
XMLChangedRegionImport
         nElement == XML_ELEMENT(TEXT, XML_DELETION) ||
         nElement == XML_ELEMENT(TEXT, XML_FORMAT_CHANGE) )
     {
+        // Parse attributes, e.g. <text:format-change loext:style-name="...">.
+        for (const auto& rAttribute : 
sax_fastparser::castToFastAttributeList(xAttrList))
+        {
+            switch (rAttribute.getToken())
+            {
+                case XML_ELEMENT(LO_EXT, XML_STYLE_NAME):
+                    m_aAutoStyleName = rAttribute.toString();
+                    break;
+                default:
+                    XMLOFF_WARN_UNKNOWN("xmloff", rAttribute);
+            }
+        }
+
         // create XMLChangeElementImportContext for all kinds of changes
         xContext = new XMLChangeElementImportContext(
             GetImport(),
@@ -141,11 +155,25 @@ void XMLChangedRegionImportContext::SetChangeInfo(
     std::u16string_view rDate,
     const OUString& rMovedID)
 {
+    // If the format redline has an autostyle, look up the internal 'auto 
name'.
+    OUString aAutoName;
+    if (!m_aAutoStyleName.isEmpty())
+    {
+        rtl::Reference<XMLTextImportHelper> xHelper = 
GetImport().GetTextImport();
+        // Map the XML-level automatic style name (e.g. T1) to an 'auto name' 
that exists in the
+        // char auto style pool (e.g. 3cb7b270).
+        XMLPropStyleContext* pStyle = 
xHelper->FindAutoCharStyle(m_aAutoStyleName);
+        if (pStyle)
+        {
+            pStyle->GetAutoName() >>= aAutoName;
+        }
+    }
+
     util::DateTime aDateTime;
     if (::sax::Converter::parseDateTime(aDateTime, rDate))
     {
         GetImport().GetTextImport()->RedlineAdd(
-            rType, sID, rAuthor, rComment, aDateTime, rMovedID, 
bMergeLastPara);
+            rType, sID, rAuthor, rComment, aDateTime, rMovedID, 
bMergeLastPara, aAutoName);
     }
 }
 
diff --git a/xmloff/source/text/XMLChangedRegionImportContext.hxx 
b/xmloff/source/text/XMLChangedRegionImportContext.hxx
index 6a4c924c095f..e12a9298597f 100644
--- a/xmloff/source/text/XMLChangedRegionImportContext.hxx
+++ b/xmloff/source/text/XMLChangedRegionImportContext.hxx
@@ -46,6 +46,8 @@ class XMLChangedRegionImportContext : public 
SvXMLImportContext
     /// merge-last-paragraph flag
     bool bMergeLastPara;
 
+    OUString m_aAutoStyleName;
+
 public:
 
 
diff --git a/xmloff/source/text/txtimp.cxx b/xmloff/source/text/txtimp.cxx
index 3357c3e66527..4415fde16a25 100644
--- a/xmloff/source/text/txtimp.cxx
+++ b/xmloff/source/text/txtimp.cxx
@@ -2328,7 +2328,8 @@ void XMLTextImportHelper::RedlineAdd( const OUString& 
/*rType*/,
                                       const OUString& /*rComment*/,
                                       const util::DateTime& /*rDateTime*/,
                                       const OUString& /*rMovedID*/,
-                                      bool /*bMergeLastPara*/)
+                                      bool /*bMergeLastPara*/,
+                                      const OUString& /*rAutoStyleName*/)
 {
     // dummy implementation: do nothing
 }
commit a9a945547b26376b15d8dd3865d2dd54e576608a
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Aug 15 08:25:46 2025 +0200
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Thu Aug 21 00:08:17 2025 +0200

    tdf#167761 sw format redline: implement ODF export
    
    Load the bugdoc, revert the format redline, the font size should go from
    36pt (new direct format) to 24pt (old direct format), but it goes to
    12pt (doc default).
    
    What happens is that we have working DOCX import to store this in
    SwRedlineExtraData_FormatColl's item set, but the ODF import/export is
    missing.
    
    Add the ODT export by:
    1) Adding a new SwXRedlineAutoStyle that exposes the item set, assuming
       it contains character properties, and create this in
       SwXRedlinePortion::GetPropertyValue() if the RedlineAutoFormat property
       is requested.
    2) In XMLRedlineExport::ExportChangeAutoStyle(), write this item set as
       an autostyle.
    3) In XMLRedlineExport::ExportChangedRegion(), refer to this autostyle.
    
    Note that similar to delete redlines, the format redline contains the
    old formatting, the new formatting is directly in the document.
    
    Change-Id: Iedd7416e5eefc4814199d60c4a6b68dbb76136e2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189672
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins
    Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189908

diff --git a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng 
b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng
index 6144cfebf93b..9c5e1f80a63d 100644
--- a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng
@@ -4146,4 +4146,16 @@ 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
       </rng:optional>
     </rng:element>
   </rng:define>
+
+  <!-- TODO(vmiklos) no proposal for style on format changes -->
+  <rng:define name="text-changed-region-content" combine="choice">
+    <rng:element name="text:format-change">
+      <rng:optional>
+        <rng:attribute name="loext:style-name">
+          <rng:ref name="styleNameRef"/>
+        </rng:attribute>
+      </rng:optional>
+      <rng:ref name="office-change-info"/>
+    </rng:element>
+  </rng:define>
 </rng:grammar>
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index d343006249db..c275e4fcdb5a 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -605,6 +605,7 @@ inline constexpr OUString UNO_NAME_REDLINE_DESCRIPTION = 
u"RedlineDescription"_u
 inline constexpr OUString UNO_NAME_REDLINE_TYPE = u"RedlineType"_ustr;
 inline constexpr OUString UNO_NAME_REDLINE_SUCCESSOR_DATA = 
u"RedlineSuccessorData"_ustr;
 inline constexpr OUString UNO_NAME_REDLINE_IDENTIFIER = 
u"RedlineIdentifier"_ustr;
+inline constexpr OUString UNO_NAME_REDLINE_AUTO_FORMAT = 
u"RedlineAutoFormat"_ustr;
 inline constexpr OUString UNO_NAME_IS_IN_HEADER_FOOTER = 
u"IsInHeaderFooter"_ustr;
 inline constexpr OUString UNO_NAME_START_REDLINE = u"StartRedline"_ustr;
 inline constexpr OUString UNO_NAME_END_REDLINE = u"EndRedline"_ustr;
diff --git a/sw/source/core/unocore/unoredline.cxx 
b/sw/source/core/unocore/unoredline.cxx
index 5ddd540c2bc6..b25d3d358408 100644
--- a/sw/source/core/unocore/unoredline.cxx
+++ b/sw/source/core/unocore/unoredline.cxx
@@ -86,6 +86,170 @@ uno::Sequence<beans::PropertyValue> 
GetSuccessorProperties(const SwRangeRedline&
     return uno::Sequence<beans::PropertyValue>(5);
 }
 
+/// Presents character properties in an item set as a beans::XPropertySet.
+class SwXRedlineAutoStyle final
+    : public cppu::WeakImplHelper<beans::XPropertySet, beans::XPropertyState>
+{
+    std::shared_ptr<SfxItemSet> m_pItemSet;
+
+public:
+    SwXRedlineAutoStyle(const std::shared_ptr<SfxItemSet>& pItemSet);
+    ~SwXRedlineAutoStyle() override;
+
+    // XPropertySet
+    uno::Reference<beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() 
override;
+    void SAL_CALL setPropertyValue(const OUString& rPropertyName,
+            const uno::Any& rValue) override;
+    uno::Any SAL_CALL getPropertyValue(const OUString& rPropertyName) override;
+    void SAL_CALL addPropertyChangeListener(
+        const OUString& rPropertyName,
+        const uno::Reference<beans::XPropertyChangeListener>& xListener) 
override;
+    void SAL_CALL removePropertyChangeListener(
+        const OUString& rPropertyName,
+        const uno::Reference<beans::XPropertyChangeListener>& xListener) 
override;
+    void SAL_CALL addVetoableChangeListener(
+        const OUString& rPropertyName,
+        const uno::Reference<beans::XVetoableChangeListener>& xListener) 
override;
+    void SAL_CALL removeVetoableChangeListener(
+        const OUString& rPropertyName,
+        const uno::Reference<beans::XVetoableChangeListener>& xListener) 
override;
+
+    // XPropertyState
+    beans::PropertyState SAL_CALL getPropertyState(const OUString& 
PropertyName) override;
+    uno::Sequence<beans::PropertyState>
+        SAL_CALL getPropertyStates(const uno::Sequence<OUString>& 
aPropertyName) override;
+    void SAL_CALL setPropertyToDefault(const OUString& PropertyName) override;
+    uno::Any SAL_CALL getPropertyDefault(const OUString& aPropertyName) 
override;
+};
+
+SwXRedlineAutoStyle::SwXRedlineAutoStyle(const std::shared_ptr<SfxItemSet>& 
pItemSet)
+    : m_pItemSet(pItemSet)
+{
+}
+
+SwXRedlineAutoStyle::~SwXRedlineAutoStyle() = default;
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL 
SwXRedlineAutoStyle::getPropertySetInfo()
+{
+    SolarMutexGuard aGuard;
+
+    static uno::Reference<beans::XPropertySetInfo> xRet
+        = 
aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE)->getPropertySetInfo();
+    return xRet;
+}
+
+void SAL_CALL SwXRedlineAutoStyle::setPropertyValue(const OUString& 
rPropertyName,
+        const uno::Any& rValue)
+{
+    SolarMutexGuard aGuard;
+
+    const SfxItemPropertySet* pPropertySet = 
aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE);
+    pPropertySet->setPropertyValue(rPropertyName, rValue, *m_pItemSet);
+}
+
+uno::Any SAL_CALL SwXRedlineAutoStyle::getPropertyValue(const OUString& 
rPropertyName)
+{
+    SolarMutexGuard aGuard;
+
+    const SfxItemPropertySet* pPropertySet = 
aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE);
+    return pPropertySet->getPropertyValue(rPropertyName, *m_pItemSet);
+}
+
+void SAL_CALL SwXRedlineAutoStyle::addPropertyChangeListener(
+    const OUString& /*rPropertyName*/,
+    const uno::Reference<beans::XPropertyChangeListener>& /*xListener*/)
+{
+    SAL_WARN("sw.uno", "SwXRedlineAutoStyle::addPropertyChangeListener: not 
implemented");
+}
+
+void SAL_CALL SwXRedlineAutoStyle::removePropertyChangeListener(
+    const OUString& /*rPropertyName*/,
+    const uno::Reference<beans::XPropertyChangeListener>& /*xListener*/)
+{
+    SAL_WARN("sw.uno", "SwXRedlineAutoStyle::removePropertyChangeListener: not 
implemented");
+}
+
+void SAL_CALL SwXRedlineAutoStyle::addVetoableChangeListener(
+    const OUString& /*rPropertyName*/,
+    const uno::Reference<beans::XVetoableChangeListener>& /*xListener*/)
+{
+    SAL_WARN("sw.uno", "SwXRedlineAutoStyle::addVetoableChangeListener: not 
implemented");
+}
+
+void SAL_CALL SwXRedlineAutoStyle::removeVetoableChangeListener(
+    const OUString& /*rPropertyName*/,
+    const uno::Reference<beans::XVetoableChangeListener>& /*xListener*/)
+{
+    SAL_WARN("sw.uno", "SwXRedlineAutoStyle::removeVetoableChangeListener: not 
implemented");
+}
+
+beans::PropertyState SwXRedlineAutoStyle::getPropertyState(const OUString& 
rPropertyName)
+{
+    SolarMutexGuard aGuard;
+
+    const SfxItemPropertySet* pPropertySet = 
aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE);
+    return pPropertySet->getPropertyState(rPropertyName, *m_pItemSet);
+}
+
+uno::Sequence< beans::PropertyState > SwXRedlineAutoStyle::getPropertyStates(
+        const uno::Sequence<OUString>& rPropertyNames)
+{
+    SolarMutexGuard aGuard;
+
+    const SfxItemPropertySet* pPropertySet = 
aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE);
+    const OUString* pNames = rPropertyNames.getConstArray();
+    uno::Sequence<beans::PropertyState> aRet(rPropertyNames.getLength());
+    beans::PropertyState* pStates = aRet.getArray();
+    const SfxItemPropertyMap& rMap = pPropertySet->getPropertyMap();
+    for (sal_Int32 i = 0, nEnd = rPropertyNames.getLength(); i < nEnd; i++)
+    {
+        const SfxItemPropertyMapEntry* pEntry = rMap.getByName(pNames[i]);
+        if (!pEntry)
+        {
+            throw beans::UnknownPropertyException("Unknown property: " + 
pNames[i]);
+        }
+
+        pStates[i] = SfxItemPropertySet::getPropertyState(*pEntry, 
*m_pItemSet);
+    }
+    return aRet;
+}
+
+void SwXRedlineAutoStyle::setPropertyToDefault(const OUString& 
/*rPropertyName*/)
+{
+    SAL_WARN("sw.uno", "SwXRedlineAutoStyle::setPropertyToDefault: not 
implemented");
+}
+
+uno::Any SwXRedlineAutoStyle::getPropertyDefault(const OUString& 
/*rPropertyName*/)
+{
+    SAL_WARN("sw.uno", "SwXRedlineAutoStyle::getPropertyDefault: not 
implemented");
+    return uno::Any();
+}
+
+/// If this format redline has old direct formatting, return it as an 
autostyle.
+uno::Reference<beans::XPropertySet> GetRedlineAutoFormat(const SwRangeRedline& 
rRedline)
+{
+    if (rRedline.GetType() != RedlineType::Format)
+    {
+        return {};
+    }
+
+    const SwRedlineExtraData* pExtraData = 
rRedline.GetRedlineData().GetExtraData();
+    auto pFormattingExtraData = dynamic_cast<const 
SwRedlineExtraData_FormatColl*>(pExtraData);
+    if (!pFormattingExtraData)
+    {
+        return {};
+    }
+
+    std::shared_ptr<SfxItemSet> pItemSet = pFormattingExtraData->GetItemSet();
+    if (!pItemSet)
+    {
+        return {};
+    }
+
+    uno::Reference<beans::XPropertySet> xAutoStyle(new 
SwXRedlineAutoStyle(pItemSet));
+    return xAutoStyle;
+}
+
 std::optional<uno::Any> GetRedlinePortionPropertyValue(std::u16string_view 
rPropertyName,
                                                        const SwRangeRedline& 
rRedline)
 {
@@ -131,6 +295,10 @@ std::optional<uno::Any> 
GetRedlinePortionPropertyValue(std::u16string_view rProp
     {
         aRet <<= !rRedline.IsDelLastPara();
     }
+    else if (rPropertyName == UNO_NAME_REDLINE_AUTO_FORMAT)
+    {
+        aRet <<= GetRedlineAutoFormat(rRedline);
+    }
     else
     {
         return {}; // Property name unknown; the caller decides when to throw
diff --git a/xmloff/qa/unit/data/redline-format-char-props.docx 
b/xmloff/qa/unit/data/redline-format-char-props.docx
new file mode 100644
index 000000000000..dfcf74ebc04e
Binary files /dev/null and b/xmloff/qa/unit/data/redline-format-char-props.docx 
differ
diff --git a/xmloff/qa/unit/text.cxx b/xmloff/qa/unit/text.cxx
index ce11001b12cd..798e311be507 100644
--- a/xmloff/qa/unit/text.cxx
+++ b/xmloff/qa/unit/text.cxx
@@ -1291,7 +1291,7 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, 
testDeleteThenFormatOdtExport)
     // Given a document that has a delete redline, and then a format redline 
on top of it:
     loadFromFile(u"del-then-format.docx");
 
-    // When exporting that to DOCX:
+    // When exporting that to ODT:
     save(u"writer8"_ustr);
 
     // Then make sure <text:changed-region> not only has the 
<text:format-change> child, but also an
@@ -1307,6 +1307,25 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, 
testDeleteThenFormatOdtExport)
     assertXPath(pXmlDoc, 
"//text:tracked-changes/text:changed-region[2]/text:deletion/text:p");
 }
 
+CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testRedlineFormatCharProps)
+{
+    // Given a document with a format redline, the redline contains the old 
char props:
+    loadFromFile(u"redline-format-char-props.docx");
+
+    // When exporting that to ODT:
+    save(u"writer8"_ustr);
+
+    // Then make sure <text:format-change> refers to an autostyle that 
describes those old char
+    // props:
+    xmlDocUniquePtr pXmlDoc = parseExport(u"content.xml"_ustr);
+    assertXPath(pXmlDoc, 
"//text:tracked-changes/text:changed-region/text:format-change");
+    // Without the accompanying fix in place, this test would have failed with:
+    // - XPath '//text:tracked-changes/text:changed-region/text:format-change' 
no attribute 'style-name' exist
+    OUString aStyleName = getXPath(
+        pXmlDoc, 
"//text:tracked-changes/text:changed-region/text:format-change", "style-name");
+    CPPUNIT_ASSERT(!aStyleName.isEmpty());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmloff/source/text/XMLRedlineExport.cxx 
b/xmloff/source/text/XMLRedlineExport.cxx
index 34e3ae5c5264..4357e5c11ffb 100644
--- a/xmloff/source/text/XMLRedlineExport.cxx
+++ b/xmloff/source/text/XMLRedlineExport.cxx
@@ -261,6 +261,16 @@ void XMLRedlineExport::ExportChangeAutoStyle(
         // export the auto styles
         rExport.GetTextParagraphExport()->collectTextAutoStyles(xText);
     }
+
+    // See if the format redline has an autostyle for old direct formatting: 
if so, export that as
+    // an autostyle.
+    aAny = rPropSet->getPropertyValue(u"RedlineAutoFormat"_ustr);
+    uno::Reference<beans::XPropertySet> xAutoFormat;
+    aAny >>= xAutoFormat;
+    if (xAutoFormat.is())
+    {
+        rExport.GetTextParagraphExport()->Add(XmlStyleFamily::TEXT_TEXT, 
xAutoFormat);
+    }
 }
 
 void XMLRedlineExport::ExportChangesListAutoStyles()
@@ -353,6 +363,23 @@ void XMLRedlineExport::ExportChangedRegion(
         aAny = rPropSet->getPropertyValue(u"RedlineType"_ustr);
         OUString sType;
         aAny >>= sType;
+
+        // See if the format redline has an autostyle for old direct 
formatting: if so, refer to the
+        // already exported autostyle.
+        aAny = rPropSet->getPropertyValue(u"RedlineAutoFormat"_ustr);
+        uno::Reference<beans::XPropertySet> xAutoStyle;
+        aAny >>= xAutoStyle;
+        if (xAutoStyle.is())
+        {
+            bool bIsUICharStyle;
+            bool bHasAutoStyle;
+            OUString sStyle = 
rExport.GetTextParagraphExport()->FindTextStyle(xAutoStyle, bIsUICharStyle, 
bHasAutoStyle);
+            if (!sStyle.isEmpty())
+            {
+                rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_STYLE_NAME, 
sStyle);
+            }
+        }
+
         SvXMLElementExport aChange(rExport, XML_NAMESPACE_TEXT,
                                    ConvertTypeName(sType), true, true);
 

Reply via email to