chart2/qa/extras/chart2export3.cxx                          |  138 +++++++++++-
 chart2/qa/extras/data/ods/tdf39052.ods                      |binary
 chart2/source/controller/chartapiwrapper/TitleWrapper.cxx   |   46 +---
 chart2/source/controller/main/ChartController_Tools.cxx     |    6 
 chart2/source/controller/main/ControllerCommandDispatch.cxx |    3 
 chart2/source/model/main/Title.cxx                          |    3 
 oox/source/export/chartexport.cxx                           |   12 -
 xmloff/source/chart/SchXMLAxisContext.cxx                   |   22 -
 xmloff/source/chart/SchXMLChartContext.cxx                  |   39 ---
 xmloff/source/chart/SchXMLChartContext.hxx                  |    6 
 xmloff/source/chart/SchXMLExport.cxx                        |   77 +++---
 xmloff/source/chart/SchXMLParagraphContext.cxx              |  131 +++++++++++
 xmloff/source/chart/SchXMLParagraphContext.hxx              |   44 +++
 xmloff/source/chart/SchXMLTools.cxx                         |   92 ++++++++
 xmloff/source/chart/SchXMLTools.hxx                         |    4 
 xmloff/source/chart/transporttypes.hxx                      |    2 
 16 files changed, 496 insertions(+), 129 deletions(-)

New commits:
commit 0ddf1d301e8e6e8a406ad170417e12d2af84ed43
Author:     Balazs Varga <balazs.varga.ext...@allotropia.de>
AuthorDate: Wed May 8 17:22:24 2024 +0200
Commit:     Thorsten Behrens <thorsten.behr...@allotropia.de>
CommitDate: Thu May 9 21:47:42 2024 +0200

    tdf#58038 - chart: make available format characters toolbar for
    
    text boxes or shapes *inside* charts (as created with the Drawing toolbar).
    
    follow-up commit: 4f994cec388377cc5c2bddb804bd92eb4cd7dc8d
    (tdf#39052 - Chart: make characters formatable in editable chart textshapes)
    
    Change-Id: Iccc5ee350ea0a37d8bda9652e09d3d61339f1d71
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/167366
    Tested-by: Jenkins
    Reviewed-by: Balazs Varga <balazs.varga.ext...@allotropia.de>

diff --git a/chart2/source/controller/main/ChartController_Tools.cxx 
b/chart2/source/controller/main/ChartController_Tools.cxx
index 67ceaa1764c7..6363970553db 100644
--- a/chart2/source/controller/main/ChartController_Tools.cxx
+++ b/chart2/source/controller/main/ChartController_Tools.cxx
@@ -591,9 +591,9 @@ bool ChartController::isShapeContext() const
 
 bool ChartController::IsTextEdit() const
 {
-    // only Title objects are editable textshapes
-    return m_aSelection.isTitleObjectSelected() &&
-        m_pDrawViewWrapper && m_pDrawViewWrapper->IsTextEdit();
+    // only Title objects and additional shapes are editable textshapes in 
chart
+    return m_pDrawViewWrapper && m_pDrawViewWrapper->IsTextEdit() &&
+        (m_aSelection.isTitleObjectSelected() || 
m_aSelection.isAdditionalShapeSelected());
 }
 
 void ChartController::impl_ClearSelection()
diff --git a/chart2/source/controller/main/ControllerCommandDispatch.cxx 
b/chart2/source/controller/main/ControllerCommandDispatch.cxx
index cf305ea66d64..95cf433f3e04 100644
--- a/chart2/source/controller/main/ControllerCommandDispatch.cxx
+++ b/chart2/source/controller/main/ControllerCommandDispatch.cxx
@@ -601,7 +601,8 @@ void ControllerCommandDispatch::updateCommandAvailability()
     // format objects
     bool bFormatObjectAvailable = bIsWritable && bControllerStateIsValid && 
m_apControllerState->bIsFormateableObjectSelected;
     m_aCommandAvailability[ ".uno:FormatSelection" ] = bFormatObjectAvailable 
&& !bIsTextEdit;
-    m_aCommandAvailability[ ".uno:FontDialog" ] = bFormatObjectAvailable && 
bIsTextEdit;
+    m_aCommandAvailability[ ".uno:FontDialog" ] = (bShapeContext ? 
isShapeControllerCommandAvailable(".uno:FontDialog") :
+                                                   bFormatObjectAvailable) && 
bIsTextEdit;
     m_aCommandAvailability[ ".uno:FormatAxis" ] = bFormatObjectAvailable;
     m_aCommandAvailability[ ".uno:FormatTitle" ] = bFormatObjectAvailable && 
!bIsTextEdit;
     m_aCommandAvailability[ ".uno:FormatDataSeries" ] = bFormatObjectAvailable;
commit bb16f920b5e16ca7d2601190a7e2b534a2dfa1a5
Author:     Balazs Varga <balazs.varga.ext...@allotropia.de>
AuthorDate: Mon Apr 15 13:39:03 2024 +0200
Commit:     Thorsten Behrens <thorsten.behr...@allotropia.de>
CommitDate: Thu May 9 21:41:41 2024 +0200

    tdf#160517 - chart odf: import/export formatted chart titles
    
    (main, sub, axis titles) texts properly to/from odf format.
    
    Fix odf export of formatted chart titles. The exported data structure
    will look like:
    
    <chart:title svg:x="3.304cm" svg:y="0.285cm" chart:style-name="ch2">
        <text:p>
            <text:span text:style-name="T1">This</text:span>
            <text:span text:style-name="T2"> is</text:span>
            .
            .
            .
            <text:span text:style-name="T3">3</text:span>
            <text:span text:style-name="T2"> a </text:span>
        </text:p>
    </chart:title>
    
    Fix import of formatted chart titles. Put the properties and related texts
    into the chart2::XFormattedString2 uno objects.
    
    Follow-up commit of:
    55e9a27afd2d6a13cf76b39641bf121c3ec4b45c
    Related: tdf#39052 - chart ooxml: export formatted chart titles
    
    4f994cec388377cc5c2bddb804bd92eb4cd7dc8d
    tdf#39052 - Chart: make characters formatable in editable chart textshapes
    
    --
    TODO: chart data point / dataseries labels are handled differently
    since those are not editable objects, but that is a completily different
    issue.
    --
    
    Change-Id: I1842f2c69c132bdf578bb2d354f451cc9d49c63c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166122
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>
    Tested-by: Jenkins
    Reviewed-by: Balazs Varga <balazs.varga.ext...@allotropia.de>

diff --git a/chart2/qa/extras/chart2export3.cxx 
b/chart2/qa/extras/chart2export3.cxx
index 13f41f56198f..fcc9a80954cc 100644
--- a/chart2/qa/extras/chart2export3.cxx
+++ b/chart2/qa/extras/chart2export3.cxx
@@ -18,6 +18,9 @@
 #include <com/sun/star/drawing/FillStyle.hpp>
 #include <com/sun/star/chart2/DataPointLabel.hpp>
 #include <com/sun/star/chart/DataLabelPlacement.hpp>
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/awt/FontUnderline.hpp>
 
 using uno::Reference;
 using beans::XPropertySet;
@@ -440,11 +443,17 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, testChartSubTitle)
     xmlDocUniquePtr pXmlDoc = parseExport("xl/charts/chart1.xml");
     CPPUNIT_ASSERT(pXmlDoc);
     // test properties of subtitle
+    // paragraph props
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:pPr/a:defRPr"_ostr, "sz"_ostr, 
"1100");
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:pPr/a:defRPr"_ostr, "b"_ostr, 
"1");
-    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:pPr/a:defRPr/a:solidFill/a:srgbClr"_ostr,
 "val"_ostr, "00a933");
+    // run props
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:rPr"_ostr, "sz"_ostr, 
"1100");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:rPr"_ostr, "b"_ostr, "1");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:rPr/a:solidFill/a:srgbClr"_ostr,
 "val"_ostr, "00a933");
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:rPr/a:latin"_ostr, 
"typeface"_ostr, "Times New Roman");
+    // text
     assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:t"_ostr, "It is a 
Subtitle");
+    // shape props
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:spPr/a:solidFill/a:srgbClr"_ostr, "val"_ostr, 
"b2b2b2");
 }
 
@@ -455,13 +464,19 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, 
testChartMainWithSubTitle)
     xmlDocUniquePtr pXmlDoc = parseExport("xl/charts/chart1.xml");
     CPPUNIT_ASSERT(pXmlDoc);
     // test properties of title
+    // paragraph props
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:pPr/a:defRPr"_ostr, 
"sz"_ostr, "1300");
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:pPr/a:defRPr"_ostr, 
"b"_ostr, "0");
-    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:pPr/a:defRPr"_ostr, 
"i"_ostr, "1");
-    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:pPr/a:defRPr/a:solidFill/a:srgbClr"_ostr,
 "val"_ostr, "f10d0c");
+    // run props
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r/a:rPr"_ostr, "sz"_ostr, 
"1300");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r/a:rPr"_ostr, "b"_ostr, 
"0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r/a:rPr"_ostr, "i"_ostr, 
"1");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r/a:rPr/a:solidFill/a:srgbClr"_ostr,
 "val"_ostr, "f10d0c");
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r/a:rPr/a:latin"_ostr, 
"typeface"_ostr, "Arial");
+    // text
     assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r/a:t"_ostr, "It is a 
Maintitle");
     assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r/a:t"_ostr, "It is a 
Subtitle");
+    // shape props
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:spPr/a:solidFill/a:srgbClr"_ostr, "val"_ostr, 
"81d41a");
 }
 
@@ -608,8 +623,8 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, 
testTitleCharacterPropertiesXLSX)
     xmlDocUniquePtr pXmlDoc = parseExport("xl/charts/chart1.xml");
     CPPUNIT_ASSERT(pXmlDoc);
 
-    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:pPr/a:defRPr"_ostr, "sz"_ostr, 
"2400");
-    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:pPr/a:defRPr"_ostr, "b"_ostr, 
"1");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:pPr/a:defRPr"_ostr, "sz"_ostr, 
"1300");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:pPr/a:defRPr"_ostr, "b"_ostr, 
"0");
 
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:rPr"_ostr, "sz"_ostr, 
"2400");
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:rPr"_ostr, "b"_ostr, "1");
@@ -820,6 +835,119 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, 
testFormattedChartTitles)
     assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[4]/a:t"_ostr, "title");
 }
 
+namespace {
+
+void checkCharacterProps(Reference<beans::XPropertySet> const & xTitleProp)
+{
+    Sequence< uno::Reference< chart2::XFormattedString > > xFormattedSubTitle;
+    CPPUNIT_ASSERT(xTitleProp->getPropertyValue("FormattedStrings") >>= 
xFormattedSubTitle);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(10), xFormattedSubTitle.getLength());
+    // check texts
+    std::vector<OUString> aValues = { "This", " is", "3", " a ", "custom", " 
erte1
", "2dfgd ch", "ar", "t ", "title" };
+    for (sal_Int32 i = 0; i < xFormattedSubTitle.getLength(); i++)
+    {
+        const OUString aText = 
xFormattedSubTitle.getConstArray()[i]->getString();
+        CPPUNIT_ASSERT_EQUAL(aValues[i], aText);
+        Reference< beans::XPropertySet > 
xRunPropSet(xFormattedSubTitle.getConstArray()[i], uno::UNO_QUERY);
+        // common props
+        uno::Any aAny = xRunPropSet->getPropertyValue("CharFontName");
+        CPPUNIT_ASSERT_EQUAL(uno::Any(OUString("Aptos Narrow")), aAny);
+        // unique props
+        if (aText == aValues[0])
+        {
+            aAny = xRunPropSet->getPropertyValue("CharWeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(awt::FontWeight::BOLD), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharHeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(14.0f), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharColor");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(Color(0xff0000)), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharEscapement");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(short(0)), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharEscapementHeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(short(100)), aAny);
+        }
+        else if (aText == aValues[1] || aText == aValues[3] || aText == 
aValues[5] ||
+            aText == aValues[6] || aText == aValues[8])
+        {
+            aAny = xRunPropSet->getPropertyValue("CharWeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(awt::FontWeight::NORMAL), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharHeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(14.0f), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharColor");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(Color(0x595959)), aAny);
+        }
+        else if (aText == aValues[2])
+        {
+            aAny = xRunPropSet->getPropertyValue("CharWeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(awt::FontWeight::NORMAL), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharHeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(14.0f), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharColor");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(Color(0x595959)), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharEscapement");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(short(30)), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharEscapementHeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(short(58)), aAny);
+        }
+        else if (aText == aValues[4])
+        {
+            aAny = xRunPropSet->getPropertyValue("CharWeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(awt::FontWeight::NORMAL), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharHeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(20.0f), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharColor");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(Color(0x4ea72e)), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharPosture");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(awt::FontSlant_ITALIC), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharUnderline");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(awt::FontUnderline::SINGLE), aAny);
+        }
+        else if (aText == aValues[7])
+        {
+            aAny = xRunPropSet->getPropertyValue("CharWeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(awt::FontWeight::BOLD), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharHeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(14.0f), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharColor");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(Color(0x595959)), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharPosture");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(awt::FontSlant_NONE), aAny);
+        }
+        else // aText == aValues[9]
+        {
+            aAny = xRunPropSet->getPropertyValue("CharWeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(awt::FontWeight::NORMAL), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharHeight");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(14.0f), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharColor");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(Color(0x595959)), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharPosture");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(awt::FontSlant_ITALIC), aAny);
+            aAny = xRunPropSet->getPropertyValue("CharOverline");
+            CPPUNIT_ASSERT_EQUAL(uno::Any(awt::FontUnderline::NONE), aAny);
+        }
+    }
+}
+
+}
+
+CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, testODSFormattedChartTitles)
+{
+    // The document contains a line chart with "Between tick marks" X axis 
position.
+    loadFromFile(u"ods/tdf39052.ods");
+    // Check formatted strings after export.
+    saveAndReload("calc8");
+
+    Reference<chart2::XChartDocument> xChart2Doc = getChartDocFromSheet(0, 
mxComponent);
+    CPPUNIT_ASSERT(xChart2Doc.is());
+    Reference< chart::XChartDocument > xChartDoc(xChart2Doc, uno::UNO_QUERY);
+    CPPUNIT_ASSERT(xChartDoc.is());
+    uno::Reference< beans::XPropertySet > xTitleProp(xChartDoc->getTitle(), 
uno::UNO_QUERY);
+    CPPUNIT_ASSERT(xTitleProp.is());
+
+    checkCharacterProps(xTitleProp);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/qa/extras/data/ods/tdf39052.ods 
b/chart2/qa/extras/data/ods/tdf39052.ods
new file mode 100644
index 000000000000..68089195507e
Binary files /dev/null and b/chart2/qa/extras/data/ods/tdf39052.ods differ
diff --git a/chart2/source/controller/chartapiwrapper/TitleWrapper.cxx 
b/chart2/source/controller/chartapiwrapper/TitleWrapper.cxx
index b0367cb0f478..fb94a5d74c8a 100644
--- a/chart2/source/controller/chartapiwrapper/TitleWrapper.cxx
+++ b/chart2/source/controller/chartapiwrapper/TitleWrapper.cxx
@@ -143,16 +143,7 @@ Any WrappedTitleFormStringsProperty::getPropertyValue( 
const Reference< beans::X
     if (xTitle.is())
     {
         const Sequence< Reference< chart2::XFormattedString > > 
aStrings(xTitle->getText());
-
-        OUStringBuffer aBuf;
-        for (Reference< chart2::XFormattedString > const& formattedStr : 
aStrings)
-        {
-            aBuf.append(formattedStr->getString());
-        }
-        if (!aBuf.makeStringAndClear().isEmpty())
-        {
-            aRet <<= aStrings;
-        }
+        aRet <<= aStrings;
     }
     return aRet;
 }
@@ -325,27 +316,12 @@ void SAL_CALL TitleWrapper::removeEventListener(
     m_aEventListenerContainer.removeInterface( g, aListener );
 }
 
-Reference< beans::XPropertySet > TitleWrapper::getFirstCharacterPropertySet()
-{
-    Reference< beans::XPropertySet > xProp;
-
-    Reference< chart2::XTitle > xTitle( getTitleObject() );
-    if( xTitle.is())
-    {
-        Sequence< Reference< chart2::XFormattedString > > aStrings( 
xTitle->getText());
-        if( aStrings.hasElements() )
-            xProp.set( aStrings[0], uno::UNO_QUERY );
-    }
-
-    return xProp;
-}
-
 void TitleWrapper::getFastCharacterPropertyValue( sal_Int32 nHandle, Any& 
rValue )
 {
     OSL_ASSERT( FAST_PROPERTY_ID_START_CHAR_PROP <= nHandle &&
                 nHandle < CharacterProperties::FAST_PROPERTY_ID_END_CHAR_PROP 
);
 
-    Reference< beans::XPropertySet > xProp = getFirstCharacterPropertySet();
+    Reference< beans::XPropertySet > xProp = getInnerPropertySet();
     Reference< beans::XFastPropertySet > xFastProp( xProp, uno::UNO_QUERY );
     if(xProp.is())
     {
@@ -385,6 +361,16 @@ void TitleWrapper::setFastCharacterPropertyValue(
         else if( xFastPropertySet.is() )
             xFastPropertySet->setFastPropertyValue( nHandle, rValue );
     }
+
+    Reference< beans::XPropertySet > xInnerProp = getInnerPropertySet();
+    Reference< beans::XFastPropertySet > xFastInnerProp( xInnerProp, 
uno::UNO_QUERY );
+    if (xInnerProp.is())
+    {
+        if (pWrappedProperty)
+            pWrappedProperty->setPropertyValue(rValue, xInnerProp);
+        else if (xFastInnerProp.is())
+            xFastInnerProp->setFastPropertyValue(nHandle, rValue);
+    }
 }
 
 // WrappedPropertySet
@@ -418,7 +404,7 @@ beans::PropertyState SAL_CALL 
TitleWrapper::getPropertyState( const OUString& rP
     sal_Int32 nHandle = getInfoHelper().getHandleByName( rPropertyName );
     if( CharacterProperties::IsCharacterPropertyHandle( nHandle ) )
     {
-        Reference< beans::XPropertyState > xPropState( 
getFirstCharacterPropertySet(), uno::UNO_QUERY );
+        Reference< beans::XPropertyState > xPropState( getInnerPropertySet(), 
uno::UNO_QUERY);
         if( xPropState.is() )
         {
             const WrappedProperty* pWrappedProperty = getWrappedProperty( 
rPropertyName );
@@ -451,7 +437,7 @@ Any SAL_CALL TitleWrapper::getPropertyDefault( const 
OUString& rPropertyName )
     sal_Int32 nHandle = getInfoHelper().getHandleByName( rPropertyName );
     if( CharacterProperties::IsCharacterPropertyHandle( nHandle ) )
     {
-        Reference< beans::XPropertyState > xPropState( 
getFirstCharacterPropertySet(), uno::UNO_QUERY );
+        Reference< beans::XPropertyState > xPropState( getInnerPropertySet(), 
uno::UNO_QUERY );
         if( xPropState.is() )
         {
             const WrappedProperty* pWrappedProperty = getWrappedProperty( 
rPropertyName );
@@ -472,7 +458,7 @@ void SAL_CALL TitleWrapper::addPropertyChangeListener( 
const OUString& rProperty
     sal_Int32 nHandle = getInfoHelper().getHandleByName( rPropertyName );
     if( CharacterProperties::IsCharacterPropertyHandle( nHandle ) )
     {
-        Reference< beans::XPropertySet > xPropSet = 
getFirstCharacterPropertySet();
+        Reference< beans::XPropertySet > xPropSet = getInnerPropertySet();
         if( xPropSet.is() )
             xPropSet->addPropertyChangeListener( rPropertyName, xListener );
     }
@@ -484,7 +470,7 @@ void SAL_CALL TitleWrapper::removePropertyChangeListener( 
const OUString& rPrope
     sal_Int32 nHandle = getInfoHelper().getHandleByName( rPropertyName );
     if( CharacterProperties::IsCharacterPropertyHandle( nHandle ) )
     {
-        Reference< beans::XPropertySet > xPropSet = 
getFirstCharacterPropertySet();
+        Reference< beans::XPropertySet > xPropSet = getInnerPropertySet();
         if( xPropSet.is() )
             xPropSet->removePropertyChangeListener( rPropertyName, xListener );
     }
diff --git a/chart2/source/model/main/Title.cxx 
b/chart2/source/model/main/Title.cxx
index 6833684e9bf2..10551ce6e318 100644
--- a/chart2/source/model/main/Title.cxx
+++ b/chart2/source/model/main/Title.cxx
@@ -18,6 +18,7 @@
  */
 
 #include <Title.hxx>
+#include <CharacterProperties.hxx>
 #include <LinePropertiesHelper.hxx>
 #include <FillProperties.hxx>
 #include <CloneHelper.hxx>
@@ -145,6 +146,7 @@ const ::chart::tPropertyValueMap& StaticTitleDefaults()
         {
             ::chart::tPropertyValueMap aTmp;
 
+            ::chart::CharacterProperties::AddDefaultsToMap( aTmp );
             ::chart::LinePropertiesHelper::AddDefaultsToMap( aTmp );
             ::chart::FillProperties::AddDefaultsToMap( aTmp );
 
@@ -178,6 +180,7 @@ const ::chart::tPropertyValueMap& StaticTitleDefaults()
         {
             std::vector< css::beans::Property > aProperties;
             lcl_AddPropertiesToVector( aProperties );
+            ::chart::CharacterProperties::AddPropertiesToVector( aProperties );
             ::chart::LinePropertiesHelper::AddPropertiesToVector( aProperties 
);
             ::chart::FillProperties::AddPropertiesToVector( aProperties );
 
diff --git a/oox/source/export/chartexport.cxx 
b/oox/source/export/chartexport.cxx
index 00161cde7e4a..6fb1ae804ee7 100644
--- a/oox/source/export/chartexport.cxx
+++ b/oox/source/export/chartexport.cxx
@@ -1154,13 +1154,9 @@ void ChartExport::exportChart( const Reference< 
css::chart::XChartDocument >& xC
     Reference< beans::XPropertySet > xPropSubTitle( xChartDoc->getSubTitle(), 
UNO_QUERY );
     if( xPropSubTitle.is())
     {
-        try
-        {
+        OUString aSubTitle;
+        if ((xPropSubTitle->getPropertyValue("String") >>= aSubTitle) && 
!aSubTitle.isEmpty())
             xPropSubTitle->getPropertyValue("FormattedStrings") >>= 
xFormattedSubTitle;
-        }
-        catch( beans::UnknownPropertyException & )
-        {
-        }
     }
 
     // chart element
@@ -1451,7 +1447,9 @@ void ChartExport::exportTitle( const Reference< XShape >& 
xShape,
     Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
     if( xPropSet.is())
     {
-        xPropSet->getPropertyValue("FormattedStrings") >>= xFormattedTitle;
+        OUString aTitle;
+        if ((xPropSet->getPropertyValue("String") >>= aTitle) && 
!aTitle.isEmpty())
+            xPropSet->getPropertyValue("FormattedStrings") >>= xFormattedTitle;
     }
 
     // tdf#101322: add subtitle to title
diff --git a/xmloff/source/chart/SchXMLAxisContext.cxx 
b/xmloff/source/chart/SchXMLAxisContext.cxx
index fd106f72fa69..c39d13f69100 100644
--- a/xmloff/source/chart/SchXMLAxisContext.cxx
+++ b/xmloff/source/chart/SchXMLAxisContext.cxx
@@ -553,26 +553,18 @@ void SchXMLAxisContext::CreateAxis()
 
 void SchXMLAxisContext::SetAxisTitle()
 {
-    if( m_aCurrentAxis.aTitle.isEmpty() )
+    if( m_aCurrentAxis.maTitle.empty() )
         return;
 
     Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, 
m_xDiagram ) );
     if( !xAxis.is() )
         return;
 
-    Reference< beans::XPropertySet > xTitleProp( xAxis->getAxisTitle() );
-    if( xTitleProp.is() )
-    {
-        try
-        {
-            // TODO: ODF import for formatted chart titles
-            xTitleProp->setPropertyValue("String", 
uno::Any(m_aCurrentAxis.aTitle) );
-        }
-        catch( beans::UnknownPropertyException & )
-        {
-            SAL_INFO("xmloff.chart", "Property String for Title not available" 
);
-        }
-    }
+    if (m_aCurrentAxis.maTitle.back().first.isEmpty() &&
+        m_aCurrentAxis.maTitle.back().second == OUStringChar(u'\x0D'))
+        m_aCurrentAxis.maTitle.pop_back(); // remove last end of paragraph 
break
+
+    SchXMLTools::importFormattedText(GetImport(), m_aCurrentAxis.maTitle, 
xAxis->getAxisTitle());
 }
 
 css::uno::Reference< css::xml::sax::XFastContextHandler > 
SchXMLAxisContext::createFastChildContext(
@@ -585,7 +577,7 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > 
SchXMLAxisContext::cre
         {
             Reference< drawing::XShape > xTitleShape = getTitleShape();
             return new SchXMLTitleContext( m_rImportHelper, GetImport(),
-                                               m_aCurrentAxis.aTitle,
+                                               m_aCurrentAxis.maTitle,
                                                xTitleShape );
         }
         break;
diff --git a/xmloff/source/chart/SchXMLChartContext.cxx 
b/xmloff/source/chart/SchXMLChartContext.cxx
index 6d1350add627..ab800c5f5199 100644
--- a/xmloff/source/chart/SchXMLChartContext.cxx
+++ b/xmloff/source/chart/SchXMLChartContext.cxx
@@ -720,37 +720,16 @@ void SchXMLChartContext::endFastElement(sal_Int32 )
 
     if( xProp.is())
     {
-        if( !maMainTitle.isEmpty())
+        if( !maMainTitle.empty())
         {
-            uno::Reference< beans::XPropertySet > xTitleProp( 
xDoc->getTitle(), uno::UNO_QUERY );
-            if( xTitleProp.is())
-            {
-                try
-                {
-                    // TODO: ODF import for formatted chart titles
-                    xTitleProp->setPropertyValue("String", 
uno::Any(maMainTitle) );
-                }
-                catch(const beans::UnknownPropertyException&)
-                {
-                    SAL_WARN("xmloff.chart", "Property String for Title not 
available" );
-                }
-            }
+            uno::Reference< beans::XPropertySet > xTitleProp(xDoc->getTitle(), 
uno::UNO_QUERY);
+            SchXMLTools::importFormattedText(GetImport(), maMainTitle, 
xTitleProp);
         }
-        if( !maSubTitle.isEmpty())
+
+        if( !maSubTitle.empty())
         {
-            uno::Reference< beans::XPropertySet > xTitleProp( 
xDoc->getSubTitle(), uno::UNO_QUERY );
-            if( xTitleProp.is())
-            {
-                try
-                {
-                    // TODO: ODF import for formatted chart titles
-                    xTitleProp->setPropertyValue("String", 
uno::Any(maSubTitle) );
-                }
-                catch(const beans::UnknownPropertyException&)
-                {
-                    SAL_WARN("xmloff.chart", "Property String for Title not 
available" );
-                }
-            }
+            uno::Reference< beans::XPropertySet > 
xTitleProp(xDoc->getSubTitle(), uno::UNO_QUERY);
+            SchXMLTools::importFormattedText(GetImport(), maSubTitle, 
xTitleProp);
         }
     }
 
@@ -1165,7 +1144,7 @@ void SchXMLChartContext::InitChart(
 }
 
 SchXMLTitleContext::SchXMLTitleContext( SchXMLImportHelper& rImpHelper, 
SvXMLImport& rImport,
-                                        OUString& rTitle,
+                                        std::vector<std::pair<OUString, 
OUString>>& rTitle,
                                         uno::Reference< drawing::XShape > 
xTitleShape ) :
         SvXMLImportContext( rImport ),
         mrImportHelper( rImpHelper ),
@@ -1231,7 +1210,7 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > 
SchXMLTitleContext::cr
     if( nElement == XML_ELEMENT(TEXT, XML_P) ||
         nElement == XML_ELEMENT(LO_EXT, XML_P) )
     {
-        pContext = new SchXMLParagraphContext( GetImport(), mrTitle );
+        pContext = new SchXMLTitleParaContext(GetImport(), mrTitle);
     }
     else
         XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
diff --git a/xmloff/source/chart/SchXMLChartContext.hxx 
b/xmloff/source/chart/SchXMLChartContext.hxx
index 40ba13e0177c..6b52b61b6d7c 100644
--- a/xmloff/source/chart/SchXMLChartContext.hxx
+++ b/xmloff/source/chart/SchXMLChartContext.hxx
@@ -89,7 +89,7 @@ private:
     SchXMLTable maTable;
     SchXMLImportHelper& mrImportHelper;
 
-    OUString maMainTitle, maSubTitle;
+    std::vector<std::pair<OUString, OUString>> maMainTitle, maSubTitle;
     OUString m_aXLinkHRefAttributeToIndicateDataProvider;
     bool m_bHasRangeAtPlotArea;
     bool m_bHasTableElement;
@@ -127,14 +127,14 @@ class SchXMLTitleContext : public SvXMLImportContext
 {
 private:
     SchXMLImportHelper& mrImportHelper;
-    OUString& mrTitle;
+    std::vector<std::pair<OUString, OUString>>& mrTitle;
     css::uno::Reference< css::drawing::XShape > mxTitleShape;
     OUString msAutoStyleName;
 
 public:
     SchXMLTitleContext( SchXMLImportHelper& rImpHelper,
                         SvXMLImport& rImport,
-                        OUString& rTitle,
+                        std::vector<std::pair<OUString, OUString>>& rTitle,
                         css::uno::Reference< css::drawing::XShape > 
xTitleShape );
     virtual ~SchXMLTitleContext() override;
 
diff --git a/xmloff/source/chart/SchXMLExport.cxx 
b/xmloff/source/chart/SchXMLExport.cxx
index 804891006faa..e3b1c3ebb1ef 100644
--- a/xmloff/source/chart/SchXMLExport.cxx
+++ b/xmloff/source/chart/SchXMLExport.cxx
@@ -207,6 +207,8 @@ public:
     ::std::queue< OUString > maAutoStyleNameQueue;
     void CollectAutoStyle(
         std::vector< XMLPropertyState >&& aStates );
+    void CollectAutoTextStyle(
+        const css::uno::Reference< css::beans::XPropertySet >& xTitlePropSet );
     void AddAutoStyleAttribute(
         const std::vector< XMLPropertyState >& aStates );
 
@@ -275,6 +277,7 @@ public:
     void addSize( const css::uno::Reference< css::drawing::XShape >& xShape );
     /// exports a string as a paragraph element
     void exportText( const OUString& rText );
+    void exportFormattedText( const css::uno::Reference< beans::XPropertySet 
>& xTitleProps );
 
 public:
     SvXMLExport& mrExport;
@@ -1314,15 +1317,14 @@ void SchXMLExportHelper_Impl::parseDocument( Reference< 
chart::XChartDocument >
     if( bHasMainTitle )
     {
         // get property states for autostyles
-        if( mxExpPropMapper.is())
+        Reference< drawing::XShape > xShape = rChartDoc->getTitle();
+        Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY);
+        if( mxExpPropMapper.is() && xPropSet.is())
         {
-            Reference< beans::XPropertySet > xPropSet( rChartDoc->getTitle(), 
uno::UNO_QUERY );
-            if( xPropSet.is())
-                aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
+            aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
         }
         if( bExportContent )
         {
-            Reference< drawing::XShape > xShape = rChartDoc->getTitle();
             if( xShape.is())    // && "hasTitleBeenMoved"
                 addPosition( xShape );
 
@@ -1333,19 +1335,12 @@ void SchXMLExportHelper_Impl::parseDocument( Reference< 
chart::XChartDocument >
             SvXMLElementExport aElTitle( mrExport, XML_NAMESPACE_CHART, 
XML_TITLE, true, true );
 
             // content (text:p)
-            Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY 
);
-            if( xPropSet.is())
-            {
-                // TODO: ODF export for formatted chart titles
-                Any aAny( xPropSet->getPropertyValue( "String" ));
-                OUString aText;
-                aAny >>= aText;
-                exportText( aText );
-            }
+            exportFormattedText(xPropSet);
         }
         else    // autostyles
         {
             CollectAutoStyle( std::move(aPropertyStates) );
+            CollectAutoTextStyle( xPropSet );
         }
         // remove property states for autostyles
         aPropertyStates.clear();
@@ -1355,16 +1350,15 @@ void SchXMLExportHelper_Impl::parseDocument( Reference< 
chart::XChartDocument >
     if( bHasSubTitle )
     {
         // get property states for autostyles
-        if( mxExpPropMapper.is())
+        Reference< drawing::XShape > xShape = rChartDoc->getSubTitle();
+        Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY);
+        if( mxExpPropMapper.is() && xPropSet.is())
         {
-            Reference< beans::XPropertySet > xPropSet( 
rChartDoc->getSubTitle(), uno::UNO_QUERY );
-            if( xPropSet.is())
-                aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
+            aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
         }
 
         if( bExportContent )
         {
-            Reference< drawing::XShape > xShape = rChartDoc->getSubTitle();
             if( xShape.is())
                 addPosition( xShape );
 
@@ -1375,19 +1369,12 @@ void SchXMLExportHelper_Impl::parseDocument( Reference< 
chart::XChartDocument >
             SvXMLElementExport aElSubTitle( mrExport, XML_NAMESPACE_CHART, 
XML_SUBTITLE, true, true );
 
             // content (text:p)
-            Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY 
);
-            if( xPropSet.is())
-            {
-                // TODO: ODF import for formatted chart titles
-                Any aAny( xPropSet->getPropertyValue( "String" ));
-                OUString aText;
-                aAny >>= aText;
-                exportText( aText );
-            }
+            exportFormattedText(xPropSet);
         }
         else    // autostyles
         {
             CollectAutoStyle( std::move(aPropertyStates) );
+            CollectAutoTextStyle(xPropSet);
         }
         // remove property states for autostyles
         aPropertyStates.clear();
@@ -2258,11 +2245,6 @@ void SchXMLExportHelper_Impl::exportAxisTitle( const 
Reference< beans::XProperty
     std::vector<XMLPropertyState> aPropertyStates = 
mxExpPropMapper->Filter(mrExport, rTitleProps);
     if( bExportContent )
     {
-        // TODO: ODF import for formatted chart titles
-        OUString aText;
-        Any aAny( rTitleProps->getPropertyValue( "String" ));
-        aAny >>= aText;
-
         Reference< drawing::XShape > xShape( rTitleProps, uno::UNO_QUERY );
         if( xShape.is())
             addPosition( xShape );
@@ -2271,11 +2253,12 @@ void SchXMLExportHelper_Impl::exportAxisTitle( const 
Reference< beans::XProperty
         SvXMLElementExport aTitle( mrExport, XML_NAMESPACE_CHART, XML_TITLE, 
true, true );
 
         // paragraph containing title
-        exportText( aText );
+        exportFormattedText( rTitleProps );
     }
     else
     {
         CollectAutoStyle( std::move(aPropertyStates) );
+        CollectAutoTextStyle( rTitleProps );
     }
     aPropertyStates.clear();
 }
@@ -3819,6 +3802,27 @@ void SchXMLExportHelper_Impl::CollectAutoStyle( 
std::vector< XMLPropertyState >&
         maAutoStyleNameQueue.push( mrAutoStylePool.Add( 
XmlStyleFamily::SCH_CHART_ID, std::move(aStates) ));
 }
 
+void SchXMLExportHelper_Impl::CollectAutoTextStyle( const css::uno::Reference< 
beans::XPropertySet >& xTitlePropSet )
+{
+    if (xTitlePropSet.is())
+    {
+        Sequence< uno::Reference< chart2::XFormattedString > > xFormattedTitle;
+
+        OUString aTitle;
+        if ((xTitlePropSet->getPropertyValue("String") >>= aTitle) && 
!aTitle.isEmpty())
+            xTitlePropSet->getPropertyValue("FormattedStrings") >>= 
xFormattedTitle;
+
+        if (xFormattedTitle.hasElements())
+        {
+            for (const uno::Reference<chart2::XFormattedString>& rxFS : 
xFormattedTitle)
+            {
+                Reference< beans::XPropertySet > xRunPropSet(rxFS, 
uno::UNO_QUERY);
+                
mrExport.GetTextParagraphExport()->Add(XmlStyleFamily::TEXT_TEXT, xRunPropSet);
+            }
+        }
+    }
+}
+
 void SchXMLExportHelper_Impl::AddAutoStyleAttribute( const std::vector< 
XMLPropertyState >& aStates )
 {
     if( !aStates.empty() )
@@ -3835,6 +3839,11 @@ void SchXMLExportHelper_Impl::exportText( const 
OUString& rText )
     SchXMLTools::exportText( mrExport, rText, false/*bConvertTabsLFs*/ );
 }
 
+void SchXMLExportHelper_Impl::exportFormattedText( const css::uno::Reference< 
beans::XPropertySet >& xTitleProps )
+{
+    SchXMLTools::exportFormattedText( mrExport, xTitleProps );
+}
+
 
 SchXMLExport::SchXMLExport(const Reference<uno::XComponentContext>& xContext,
                            OUString const& implementationName, 
SvXMLExportFlags nExportFlags)
diff --git a/xmloff/source/chart/SchXMLParagraphContext.cxx 
b/xmloff/source/chart/SchXMLParagraphContext.cxx
index 84e22c5a5c71..32e2a9899789 100644
--- a/xmloff/source/chart/SchXMLParagraphContext.cxx
+++ b/xmloff/source/chart/SchXMLParagraphContext.cxx
@@ -104,4 +104,135 @@ void SchXMLParagraphContext::characters( const OUString& 
rChars )
     maBuffer.append( rChars );
 }
 
+SchXMLTitleParaContext::SchXMLTitleParaContext( SvXMLImport& rImport,
+                                                
std::vector<std::pair<OUString, OUString>>& rParaText,
+                                                OUString * pOutId /* = 0 */ ) :
+    SvXMLImportContext( rImport ),
+    mrParaText( rParaText ),
+    mpId( pOutId )
+{
+}
+
+SchXMLTitleParaContext::~SchXMLTitleParaContext()
+{}
+
+void SchXMLTitleParaContext::startFastElement(
+   sal_Int32 /*nElement*/,
+   const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+    // remember the id. It is used for storing the original cell range string 
in
+    // a local table (cached data)
+    if( !mpId )
+        return;
+
+    bool bHaveXmlId( false );
+
+    for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+    {
+        switch(aIter.getToken())
+        {
+            case XML_ELEMENT(TEXT, XML_STYLE_NAME):
+                maStyleName = aIter.toString();
+                break;
+            case XML_ELEMENT(XML, XML_ID):
+                (*mpId) = aIter.toString();
+                bHaveXmlId = true;
+                break;
+            case XML_ELEMENT(TEXT, XML_ID):
+            {   // text:id shall be ignored if xml:id exists
+                if (!bHaveXmlId)
+                {
+                    (*mpId) = aIter.toString();
+                }
+                break;
+            }
+            default:
+                XMLOFF_WARN_UNKNOWN("xmloff", aIter);
+        }
+    }
+}
+
+void SchXMLTitleParaContext::endFastElement(sal_Int32 )
+{
+    if (!maBuffer.isEmpty())
+        mrParaText.push_back(std::make_pair(maStyleName, 
maBuffer.makeStringAndClear()));
+}
+
+void SchXMLTitleParaContext::characters(const OUString& rChars)
+{
+    maBuffer.append(rChars);
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > 
SchXMLTitleParaContext::createFastChildContext(
+    sal_Int32 nElement,
+    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
+{
+    if( nElement == XML_ELEMENT(TEXT, XML_SPAN) )
+    {
+        if (!maBuffer.isEmpty())
+            mrParaText.push_back(std::make_pair(maStyleName, 
maBuffer.makeStringAndClear()));
+
+        return new SchXMLTitleSpanContext(GetImport(), mrParaText, xAttrList);
+    }
+    else if( nElement == XML_ELEMENT(TEXT, XML_TAB_STOP) )
+    {
+        maBuffer.append( u'\x0009');  // tabulator
+    }
+    else if( nElement == XML_ELEMENT(TEXT, XML_LINE_BREAK) )
+    {
+        maBuffer.append( u'\x000A');  // linefeed
+    }
+    else
+        XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
+
+    return nullptr;
+}
+
+SchXMLTitleSpanContext::SchXMLTitleSpanContext(SvXMLImport& rImport, 
std::vector<std::pair<OUString, OUString>>& rSpanTexts,
+    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) 
:
+    SvXMLImportContext(rImport),
+    mrSpanTexts(rSpanTexts)
+{
+    for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+    {
+        if( aIter.getToken() == XML_ELEMENT(TEXT, XML_STYLE_NAME) )
+        {
+            maStyleName = aIter.toString();
+            break;
+        }
+    }
+}
+
+SchXMLTitleSpanContext::~SchXMLTitleSpanContext()
+{}
+
+void SchXMLTitleSpanContext::characters(const OUString& rChars)
+{
+    maCharBuffer.append(rChars);
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > 
SchXMLTitleSpanContext::createFastChildContext(
+    sal_Int32 nElement,
+    const css::uno::Reference< css::xml::sax::XFastAttributeList >& 
/*xAttrList*/)
+{
+    if( nElement == XML_ELEMENT(TEXT, XML_TAB_STOP) )
+    {
+        maCharBuffer.append( u'\x0009');  // tabulator
+    }
+    else if( nElement == XML_ELEMENT(TEXT, XML_LINE_BREAK) )
+    {
+        maCharBuffer.append( u'\x000A');  // linefeed
+    }
+    else
+        XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
+
+    return nullptr;
+}
+
+void SchXMLTitleSpanContext::endFastElement(sal_Int32)
+{
+    if (!maCharBuffer.isEmpty())
+        mrSpanTexts.push_back(std::make_pair(maStyleName, 
maCharBuffer.makeStringAndClear()));
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmloff/source/chart/SchXMLParagraphContext.hxx 
b/xmloff/source/chart/SchXMLParagraphContext.hxx
index 65e90522e860..ba2231bd5d24 100644
--- a/xmloff/source/chart/SchXMLParagraphContext.hxx
+++ b/xmloff/source/chart/SchXMLParagraphContext.hxx
@@ -50,4 +50,48 @@ public:
         const css::uno::Reference< css::xml::sax::XFastAttributeList >& 
AttrList ) override;
 };
 
+class SchXMLTitleParaContext : public SvXMLImportContext
+{
+private:
+    std::vector<std::pair<OUString, OUString>>& mrParaText;
+    OUString* mpId;
+    OUStringBuffer maBuffer;
+    OUString maStyleName;
+
+public:
+    SchXMLTitleParaContext( SvXMLImport& rImport,
+                            std::vector<std::pair<OUString, OUString>>& 
rParaText,
+                            OUString * pOutId = nullptr );
+    virtual ~SchXMLTitleParaContext() override;
+
+    virtual void SAL_CALL startFastElement(
+        sal_Int32 nElement,
+        const css::uno::Reference< css::xml::sax::XFastAttributeList >& 
xAttrList ) override;
+    virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+    virtual void SAL_CALL characters( const OUString& rChars ) override;
+
+    virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL 
createFastChildContext(
+        sal_Int32 nElement,
+        const css::uno::Reference< css::xml::sax::XFastAttributeList >& 
AttrList ) override;
+};
+
+class SchXMLTitleSpanContext : public SvXMLImportContext
+{
+private:
+    std::vector<std::pair<OUString, OUString>>& mrSpanTexts;
+    OUStringBuffer maCharBuffer;
+    OUString maStyleName;
+public:
+    SchXMLTitleSpanContext( SvXMLImport& rImport, 
std::vector<std::pair<OUString, OUString>>& rSpanTexts,
+        const css::uno::Reference< css::xml::sax::XFastAttributeList >& 
xAttrList);
+    virtual ~SchXMLTitleSpanContext() override;
+
+    virtual void SAL_CALL characters( const OUString& rChars ) override;
+    virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+    virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL 
createFastChildContext(
+        sal_Int32 nElement,
+        const css::uno::Reference< css::xml::sax::XFastAttributeList >& 
/*AttrList*/) override;
+};
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmloff/source/chart/SchXMLTools.cxx 
b/xmloff/source/chart/SchXMLTools.cxx
index 120e361f84f0..ad012dc9cffe 100644
--- a/xmloff/source/chart/SchXMLTools.cxx
+++ b/xmloff/source/chart/SchXMLTools.cxx
@@ -26,6 +26,7 @@
 #include <xmloff/prstylei.hxx>
 #include <xmloff/xmlprmap.hxx>
 #include <xmloff/xmlexp.hxx>
+#include <xmloff/xmlimp.hxx>
 #include <xmloff/xmlnamespace.hxx>
 #include <xmloff/xmlmetai.hxx>
 #include <xmloff/maptype.hxx>
@@ -37,6 +38,7 @@
 #include <com/sun/star/chart2/data/XDataReceiver.hpp>
 #include <com/sun/star/chart2/data/XRangeXMLConversion.hpp>
 #include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
+#include <com/sun/star/chart2/FormattedString.hpp>
 #include <com/sun/star/chart2/XChartDocument.hpp>
 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
 #include <com/sun/star/container/XChild.hpp>
@@ -47,6 +49,7 @@
 
 #include <comphelper/processfactory.hxx>
 #include <comphelper/diagnose_ex.hxx>
+#include <comphelper/sequence.hxx>
 #include <sal/log.hxx>
 #include <o3tl/string_view.hxx>
 #include <algorithm>
@@ -600,6 +603,95 @@ void exportText( SvXMLExport& rExport, const OUString& 
rText, bool bConvertTabsL
     }
 }
 
+void exportFormattedText( SvXMLExport& rExport, const uno::Reference< 
beans::XPropertySet >& xTitleProps )
+{
+    Sequence< uno::Reference< chart2::XFormattedString > > xFormattedTitle;
+    if (xTitleProps.is())
+    {
+        OUString aTitle;
+        if ((xTitleProps->getPropertyValue("String") >>= aTitle) && 
!aTitle.isEmpty())
+            xTitleProps->getPropertyValue("FormattedStrings") >>= 
xFormattedTitle;
+    }
+
+    if (xFormattedTitle.hasElements())
+    {
+        SvXMLElementExport aElemP(rExport, XML_NAMESPACE_TEXT, XML_P, true, 
false);
+        for (const uno::Reference<chart2::XFormattedString>& rxFS : 
xFormattedTitle)
+        {
+            Reference< beans::XPropertySet > xRunPropSet(rxFS, uno::UNO_QUERY);
+            bool bIsUICharStyle, bHasAutoStyle = false;
+            OUString sStyle = 
rExport.GetTextParagraphExport()->FindTextStyle(xRunPropSet, bIsUICharStyle, 
bHasAutoStyle);
+            if (!sStyle.isEmpty())
+            {
+                rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_STYLE_NAME,
+                    rExport.EncodeStyleName(sStyle));
+            }
+
+            SvXMLElementExport aSpan(rExport, !sStyle.isEmpty(), 
XML_NAMESPACE_TEXT, XML_SPAN, true, false);
+            rExport.GetDocHandler()->characters(rxFS->getString());
+        }
+    }
+}
+
+void importFormattedText( SvXMLImport& rImport, const 
std::vector<std::pair<OUString, OUString>>& rTitle,
+    const uno::Reference< beans::XPropertySet >& xTitleProp )
+{
+    if (!xTitleProp.is())
+        return;
+
+    std::vector< Reference< chart2::XFormattedString > > aStringVec;
+    Sequence< uno::Reference< chart2::XFormattedString > > xFullTextTitle;
+    Reference < beans::XPropertySet > xFullTextTitleProps;
+
+    try
+    {
+        if ((xTitleProp->getPropertyValue("FormattedStrings") >>= 
xFullTextTitle) &&
+            xFullTextTitle.hasElements())
+        {
+            // these are the properies from the textshape object - needs to 
apply them
+            // to all the string parts firstly - (necessarry for backward 
compatibility)
+            xFullTextTitleProps.set(xFullTextTitle.getArray()[0], 
uno::UNO_QUERY);
+        }
+
+        for (auto aRIt = std::begin(rTitle); aRIt != std::end(rTitle); ++aRIt)
+        {
+            Reference< chart2::XFormattedString2 > xNewFmtStr;
+            xNewFmtStr = 
chart2::FormattedString::create(rImport.GetComponentContext());
+
+            if (xFullTextTitleProps.is())
+            {
+                // these are the properies from the textshape object - needs 
to apply them
+                // to all the string parts firstly
+                uno::Reference< beans::XPropertySetInfo > xInfo = 
xNewFmtStr->getPropertySetInfo();
+                for (const beans::Property& rProp : 
xFullTextTitleProps->getPropertySetInfo()->getProperties())
+                {
+                    if (xInfo.is() && xInfo->hasPropertyByName(rProp.Name))
+                    {
+                        const uno::Any aValue = 
xFullTextTitleProps->getPropertyValue(rProp.Name);
+                        xNewFmtStr->setPropertyValue(rProp.Name, aValue);
+                    }
+                }
+            }
+
+            if (auto pStyle = 
rImport.GetTextImport()->FindAutoCharStyle(aRIt->first))
+            {
+                pStyle->FillPropertySet(xNewFmtStr);
+            }
+
+            xNewFmtStr->setString(aRIt->second);
+            aStringVec.emplace_back(xNewFmtStr);
+        }
+
+        uno::Sequence< Reference< chart2::XFormattedString > > aStringSeq =
+            comphelper::containerToSequence(aStringVec);
+        xTitleProp->setPropertyValue("FormattedStrings", uno::Any(aStringSeq));
+    }
+    catch (const beans::UnknownPropertyException&)
+    {
+        SAL_WARN("xmloff.chart", "Property FormattedStrings for Title not 
available");
+    }
+}
+
 void exportRangeToSomewhere( SvXMLExport& rExport, const OUString& rValue )
 {
     //with issue #i366# and CWS chart20 ranges for error bars were introduced
diff --git a/xmloff/source/chart/SchXMLTools.hxx 
b/xmloff/source/chart/SchXMLTools.hxx
index 994308a19bc3..364c894c5634 100644
--- a/xmloff/source/chart/SchXMLTools.hxx
+++ b/xmloff/source/chart/SchXMLTools.hxx
@@ -38,6 +38,7 @@ namespace com::sun::star {
 class XMLPropStyleContext;
 class SvXMLStylesContext;
 class SvXMLExport;
+class SvXMLImport;
 
 namespace SchXMLTools
 {
@@ -98,6 +99,9 @@ namespace SchXMLTools
     css::uno::Any getPropertyFromContext( std::u16string_view rPropertyName, 
const XMLPropStyleContext * pPropStyleContext, const SvXMLStylesContext* 
pStylesCtxt );
 
     void exportText( SvXMLExport& rExport, const OUString& rText, bool 
bConvertTabsLFs );
+    void exportFormattedText( SvXMLExport& rExport, const css::uno::Reference< 
css::beans::XPropertySet >& xTitleProps );
+    void importFormattedText( SvXMLImport& rImport, const 
std::vector<std::pair<OUString, OUString>>& rTitle,
+        const css::uno::Reference< css::beans::XPropertySet >& xTitleProp);
 
     void exportRangeToSomewhere( SvXMLExport& rExport, const OUString& rValue 
);
 
diff --git a/xmloff/source/chart/transporttypes.hxx 
b/xmloff/source/chart/transporttypes.hxx
index 81d5de8cdb8a..b312af71cf38 100644
--- a/xmloff/source/chart/transporttypes.hxx
+++ b/xmloff/source/chart/transporttypes.hxx
@@ -103,7 +103,7 @@ struct SchXMLAxis
     enum SchXMLAxisDimension eDimension;
     sal_Int8 nAxisIndex;//0->primary axis; 1->secondary axis
     OUString aName;
-    OUString aTitle;
+    std::vector<std::pair<OUString, OUString>> maTitle;
     bool bHasCategories;
 
     SchXMLAxis() : eDimension( SCH_XML_AXIS_UNDEF ), nAxisIndex( 0 ), 
bHasCategories( false ) {}

Reply via email to