chart2/qa/extras/chart2export3.cxx                         |   70 ++++++++++-
 chart2/qa/extras/data/xlsx/tdf39052.xlsx                   |binary
 chart2/source/controller/chartapiwrapper/TitleWrapper.cxx  |   65 ++++++++++
 chart2/source/controller/main/ChartController_TextEdit.cxx |   30 ----
 chart2/source/inc/TitleHelper.hxx                          |    2 
 chart2/source/tools/TitleHelper.cxx                        |   31 +++-
 include/oox/export/chartexport.hxx                         |    4 
 oox/source/export/chartexport.cxx                          |   81 +++++++++----
 oox/source/export/drawingml.cxx                            |    3 
 xmloff/source/chart/SchXMLAxisContext.cxx                  |    1 
 xmloff/source/chart/SchXMLChartContext.cxx                 |    2 
 xmloff/source/chart/SchXMLExport.cxx                       |    3 
 12 files changed, 230 insertions(+), 62 deletions(-)

New commits:
commit 55e9a27afd2d6a13cf76b39641bf121c3ec4b45c
Author:     Balazs Varga <balazs.varga.ext...@allotropia.de>
AuthorDate: Fri Mar 29 23:00:50 2024 +0100
Commit:     Balazs Varga <balazs.varga.ext...@allotropia.de>
CommitDate: Tue Apr 2 23:27:26 2024 +0200

    Related: tdf#39052 - chart ooxml: export formatted chart titles
    
    texts properly to ooxml.
    
    Also adding "FormattedStrings" property for title objects
    to simplify the working of character formattings in editable
    chart shapes.
    
    TODO: odf import/export
    
    Change-Id: Ie27b4dee72c24fa6a2a4e2a7db8da7fa50eb8937
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165583
    Tested-by: Jenkins
    Tested-by: Gabor Kelemen <gabor.kelemen.ext...@allotropia.de>
    Reviewed-by: Balazs Varga <balazs.varga.ext...@allotropia.de>

diff --git a/chart2/qa/extras/chart2export3.cxx 
b/chart2/qa/extras/chart2export3.cxx
index e1229b07720c..3d05495eff40 100644
--- a/chart2/qa/extras/chart2export3.cxx
+++ b/chart2/qa/extras/chart2export3.cxx
@@ -447,12 +447,13 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, 
testChartMainWithSubTitle)
     xmlDocUniquePtr pXmlDoc = parseExport("xl/charts/chart1.xml");
     CPPUNIT_ASSERT(pXmlDoc);
     // test properties of title
-    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:pPr/a:defRPr"_ostr, "i"_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, "f10d0c");
-    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:rPr/a:latin"_ostr, 
"typeface"_ostr, "Arial");
-    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:t"_ostr, "It is a Maintitle
It is a Subtitle");
+    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");
+    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");
+    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");
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:spPr/a:solidFill/a:srgbClr"_ostr, "val"_ostr, 
"81d41a");
 }
 
@@ -754,6 +755,63 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, testTdf148142)
     CPPUNIT_ASSERT(!aScaleData2.ShiftedCategoryPosition);
 }
 
+CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, testFormattedChartTitles)
+{
+    loadFromFile(u"xlsx/tdf39052.xlsx");
+    save("Calc Office Open XML");
+    xmlDocUniquePtr pXmlDoc = parseExport("xl/charts/chart1.xml");
+    CPPUNIT_ASSERT(pXmlDoc);
+
+    // Check run level properties [1] - first paragraph
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[1]/a:rPr"_ostr, "b"_ostr, 
"1");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[1]/a:rPr"_ostr, 
"sz"_ostr, "1400");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[1]/a:rPr/a:solidFill/a:srgbClr"_ostr,
 "val"_ostr, "ff0000");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[1]/a:rPr/a:latin"_ostr, 
"typeface"_ostr, "Aptos Narrow");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[1]/a:t"_ostr, "This");
+    // Check run level properties [2]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[2]/a:rPr"_ostr, "b"_ostr, 
"0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[2]/a:rPr"_ostr, 
"sz"_ostr, "1400");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[2]/a:rPr/a:solidFill/a:srgbClr"_ostr,
 "val"_ostr, "595959");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[2]/a:t"_ostr, " is");
+    // Check run level properties [3]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[3]/a:rPr"_ostr, "b"_ostr, 
"0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[3]/a:rPr"_ostr, 
"sz"_ostr, "1400");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[3]/a:rPr"_ostr, 
"baseline"_ostr, "30000");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[3]/a:t"_ostr, "3");
+    // Check run level properties [4]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[4]/a:rPr"_ostr, "b"_ostr, 
"0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[4]/a:rPr"_ostr, 
"sz"_ostr, "1400");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[4]/a:t"_ostr, " a ");
+    // Check run level properties [5]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr"_ostr, "b"_ostr, 
"0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr"_ostr, "i"_ostr, 
"1");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr"_ostr, 
"sz"_ostr, "2000");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr"_ostr, "u"_ostr, 
"sng");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr/a:solidFill/a:srgbClr"_ostr,
 "val"_ostr, "4ea72e");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr/a:uFillTx"_ostr, 
1);
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:t"_ostr, "custom");
+    // Check run level properties [6]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[6]/a:rPr"_ostr, "b"_ostr, 
"0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[6]/a:rPr"_ostr, 
"sz"_ostr, "1400");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[6]/a:t"_ostr, " erte1");
+    // Check run level properties [1] - second paragraph
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[1]/a:rPr"_ostr, "b"_ostr, 
"0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[1]/a:rPr"_ostr, 
"sz"_ostr, "1400");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[1]/a:rPr/a:solidFill/a:srgbClr"_ostr,
 "val"_ostr, "595959");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[1]/a:rPr/a:latin"_ostr, 
"typeface"_ostr, "Aptos Narrow");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[1]/a:t"_ostr, "2dfgd ch");
+    // Check run level properties [2]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[2]/a:rPr"_ostr, "b"_ostr, 
"1");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[2]/a:t"_ostr, "ar");
+    // Check run level properties [3]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[3]/a:rPr"_ostr, "b"_ostr, 
"0");;
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[3]/a:t"_ostr, "t ");
+    // Check run level properties [4]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[4]/a:rPr"_ostr, "b"_ostr, 
"0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[4]/a:rPr"_ostr, 
"strike"_ostr, "sngStrike");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[4]/a:t"_ostr, "title");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/qa/extras/data/xlsx/tdf39052.xlsx 
b/chart2/qa/extras/data/xlsx/tdf39052.xlsx
new file mode 100644
index 000000000000..5b0285bab292
Binary files /dev/null and b/chart2/qa/extras/data/xlsx/tdf39052.xlsx differ
diff --git a/chart2/source/controller/chartapiwrapper/TitleWrapper.cxx 
b/chart2/source/controller/chartapiwrapper/TitleWrapper.cxx
index 9c802462ff09..b0367cb0f478 100644
--- a/chart2/source/controller/chartapiwrapper/TitleWrapper.cxx
+++ b/chart2/source/controller/chartapiwrapper/TitleWrapper.cxx
@@ -104,6 +104,63 @@ Any WrappedTitleStringProperty::getPropertyDefault( const 
Reference< beans::XPro
     return uno::Any( OUString() );//default title is an empty String
 }
 
+namespace {
+
+    class WrappedTitleFormStringsProperty : public WrappedProperty
+    {
+        public:
+            explicit WrappedTitleFormStringsProperty();
+
+            virtual void setPropertyValue( const Any& rOuterValue, const 
Reference< beans::XPropertySet >& xInnerPropertySet ) const override;
+            virtual Any getPropertyValue( const Reference< beans::XPropertySet 
>& xInnerPropertySet ) const override;
+            virtual Any getPropertyDefault( const Reference< 
beans::XPropertyState >& xInnerPropertyState ) const override;
+
+        protected:
+            Reference< uno::XComponentContext > m_xContext;
+    };
+
+}
+
+WrappedTitleFormStringsProperty::WrappedTitleFormStringsProperty()
+    : ::chart::WrappedProperty( "FormattedStrings", OUString() )
+{
+}
+
+void WrappedTitleFormStringsProperty::setPropertyValue( const Any& 
rOuterValue, const Reference< beans::XPropertySet >& xInnerPropertySet ) const
+{
+    Title* pTitle = dynamic_cast<Title*>(xInnerPropertySet.get());
+    if (pTitle)
+    {
+        Sequence< Reference< chart2::XFormattedString >> xFormattedStrings;
+        rOuterValue >>= xFormattedStrings;
+        TitleHelper::setFormattedString(pTitle, xFormattedStrings);
+    }
+}
+Any WrappedTitleFormStringsProperty::getPropertyValue( const Reference< 
beans::XPropertySet >& xInnerPropertySet ) const
+{
+    Any aRet(getPropertyDefault(Reference< beans::XPropertyState 
>(xInnerPropertySet, uno::UNO_QUERY)));
+    Reference< chart2::XTitle > xTitle(xInnerPropertySet, uno::UNO_QUERY);
+    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;
+        }
+    }
+    return aRet;
+}
+Any WrappedTitleFormStringsProperty::getPropertyDefault( const Reference< 
beans::XPropertyState >& /*xInnerPropertyState*/ ) const
+{
+    return uno::Any(Sequence< Reference< chart2::XFormattedString > >()); 
//default title is an empty Sequence of XFormattedStrings
+}
+
 namespace {
 
 class WrappedStackedTextProperty : public WrappedProperty
@@ -127,6 +184,7 @@ namespace
 enum
 {
     PROP_TITLE_STRING,
+    PROP_TITLE_FORMATTED_STRINGS,
     PROP_TITLE_VISIBLE,
     PROP_TITLE_TEXT_ROTATION,
     PROP_TITLE_TEXT_STACKED
@@ -141,6 +199,12 @@ void lcl_AddPropertiesToVector(
                   beans::PropertyAttribute::BOUND
                   | beans::PropertyAttribute::MAYBEVOID );
 
+    rOutProperties.emplace_back( "FormattedStrings",
+                  PROP_TITLE_FORMATTED_STRINGS,
+                  cppu::UnoType< Sequence< Reference< chart2::XFormattedString 
>>>::get(),
+                  beans::PropertyAttribute::BOUND
+                  | beans::PropertyAttribute::MAYBEVOID );
+
     rOutProperties.emplace_back( "Visible",
                   PROP_TITLE_VISIBLE,
                   cppu::UnoType<OUString>::get(),
@@ -475,6 +539,7 @@ std::vector< std::unique_ptr<WrappedProperty> > 
TitleWrapper::createWrappedPrope
     std::vector< std::unique_ptr<WrappedProperty> > aWrappedProperties;
 
     aWrappedProperties.emplace_back( new WrappedTitleStringProperty( 
m_spChart2ModelContact->m_xContext ) );
+    aWrappedProperties.emplace_back( new WrappedTitleFormStringsProperty() );
     aWrappedProperties.emplace_back( new WrappedTextRotationProperty( true ) );
     aWrappedProperties.emplace_back( new WrappedStackedTextProperty() );
     WrappedCharacterHeightProperty::addWrappedProperties( aWrappedProperties, 
this );
diff --git a/chart2/source/controller/main/ChartController_TextEdit.cxx 
b/chart2/source/controller/main/ChartController_TextEdit.cxx
index c88f06a66e45..600d46b434d0 100644
--- a/chart2/source/controller/main/ChartController_TextEdit.cxx
+++ b/chart2/source/controller/main/ChartController_TextEdit.cxx
@@ -128,17 +128,10 @@ bool ChartController::EndTextEdit()
     if(!pTextObject)
         return false;
 
-    SdrOutliner* pOutliner = m_pDrawViewWrapper->getOutliner();
     OutlinerParaObject* pParaObj = pTextObject->GetOutlinerParaObject();
-    if( !pParaObj || !pOutliner )
+    if( !pParaObj )
         return true;
 
-    pOutliner->SetText( *pParaObj );
-
-    OUString aString = pOutliner->GetText(
-                        pOutliner->GetParagraph( 0 ),
-                        pOutliner->GetParagraphCount() );
-
     OUString aObjectCID = m_aSelection.getSelectedCID();
     if ( !aObjectCID.isEmpty() )
     {
@@ -152,26 +145,7 @@ bool ChartController::EndTextEdit()
             GetFormattedTitle(pParaObj->GetTextObject(), 
pTextObject->getUnoShape());
 
         Title* pTitle = dynamic_cast<Title*>(xPropSet.get());
-        if (pTitle && aNewFormattedTitle.hasElements())
-        {
-            bool bStacked = false;
-            if (xPropSet.is())
-                xPropSet->getPropertyValue("StackCharacters") >>= bStacked;
-
-            if (bStacked)
-            {
-                for (uno::Reference< chart2::XFormattedString >const& 
formattedStr : aNewFormattedTitle)
-                {
-                    
formattedStr->setString(TitleHelper::getUnstackedStr(formattedStr->getString()));
-                }
-            }
-
-            pTitle->setText(aNewFormattedTitle);
-        }
-        else
-        {
-            TitleHelper::setCompleteString(aString, pTitle, m_xCC);
-        }
+        TitleHelper::setFormattedString(pTitle, aNewFormattedTitle);
 
         OSL_ENSURE(m_pTextActionUndoGuard, "ChartController::EndTextEdit: no 
TextUndoGuard!");
         if (m_pTextActionUndoGuard)
diff --git a/chart2/source/inc/TitleHelper.hxx 
b/chart2/source/inc/TitleHelper.hxx
index f2a8a0830b21..f4ea2204d7d5 100644
--- a/chart2/source/inc/TitleHelper.hxx
+++ b/chart2/source/inc/TitleHelper.hxx
@@ -78,6 +78,8 @@ public:
 
     static OUString getCompleteString( const rtl::Reference< ::chart::Title >& 
xTitle );
     static OUString getUnstackedStr( const OUString& rNewText );
+    static void setFormattedString( const rtl::Reference< ::chart::Title >& 
xTitle,
+        const css::uno::Sequence< css::uno::Reference< 
css::chart2::XFormattedString > >& aNewFormattedTitle );
     static void setCompleteString( const OUString& rNewText
         , const rtl::Reference< ::chart::Title >& xTitle
         , const css::uno::Reference< css::uno::XComponentContext > & xContext
diff --git a/chart2/source/tools/TitleHelper.cxx 
b/chart2/source/tools/TitleHelper.cxx
index ca137cb6e072..6fdb4f5adef3 100644
--- a/chart2/source/tools/TitleHelper.cxx
+++ b/chart2/source/tools/TitleHelper.cxx
@@ -327,32 +327,47 @@ OUString TitleHelper::getUnstackedStr(const OUString& 
rNewText)
     return aUnstackedStr.makeStringAndClear();
 }
 
+void TitleHelper::setFormattedString( const rtl::Reference< Title >& xTitle,
+    const css::uno::Sequence< css::uno::Reference< 
css::chart2::XFormattedString > >& aNewFormattedTitle )
+{
+    if (!xTitle.is() || !aNewFormattedTitle.hasElements())
+        return;
+
+    bool bStacked = false;
+    xTitle->getPropertyValue("StackCharacters") >>= bStacked;
+
+    if (bStacked)
+    {
+        for (uno::Reference< chart2::XFormattedString >const& formattedStr : 
aNewFormattedTitle)
+        {
+            
formattedStr->setString(TitleHelper::getUnstackedStr(formattedStr->getString()));
+        }
+    }
+
+    xTitle->setText(aNewFormattedTitle);
+}
+
 void TitleHelper::setCompleteString( const OUString& rNewText
                     , const rtl::Reference< Title >& xTitle
                     , const uno::Reference< uno::XComponentContext > & xContext
                     , const float * pDefaultCharHeight /* = 0 */
                     , bool bDialogTitle /*= false*/ )
 {
-    if(!xTitle.is())
+    if (!xTitle.is())
         return;
 
-    OUString aNewText = rNewText;
-
     bool bStacked = false;
     if( xTitle.is() )
         xTitle->getPropertyValue( "StackCharacters" ) >>= bStacked;
 
-    uno::Sequence< uno::Reference< XFormattedString > > aOldStringList = 
xTitle->getText();
+    OUString aNewText = rNewText;
     if( bStacked )
     {
         aNewText = getUnstackedStr(rNewText);
-        for (uno::Reference< XFormattedString >const & formattedStr : 
aOldStringList)
-        {
-            
formattedStr->setString(getUnstackedStr(formattedStr->getString()));
-        }
     }
 
     uno::Sequence< uno::Reference< XFormattedString > > aNewStringList;
+    uno::Sequence< uno::Reference< XFormattedString > > aOldStringList = 
xTitle->getText();
     if( aOldStringList.hasElements())
     {
         const OUString aFullString = getCompleteString(xTitle);
diff --git a/include/oox/export/chartexport.hxx 
b/include/oox/export/chartexport.hxx
index 640415003d15..1b708b83d479 100644
--- a/include/oox/export/chartexport.hxx
+++ b/include/oox/export/chartexport.hxx
@@ -25,6 +25,7 @@
 
 #include <com/sun/star/uno/Reference.hxx>
 #include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/chart2/FormattedString.hpp>
 #include <oox/dllapi.h>
 #include <oox/export/drawingml.hxx>
 #include <oox/export/shapes.hxx>
@@ -176,7 +177,8 @@ private:
     void exportLegend( const css::uno::Reference<
                           css::chart::XChartDocument >& rChartDoc );
     void exportTitle( const css::uno::Reference< css::drawing::XShape >& 
xShape,
-                          const OUString* pSubText = nullptr );
+                          const css::uno::Sequence< css::uno::Reference< 
css::chart2::XFormattedString > >& xFormattedSubTitle =
+                          css::uno::Sequence< css::uno::Reference< 
css::chart2::XFormattedString > >() );
     void exportPlotArea( const css::uno::Reference<
                              css::chart::XChartDocument >& rChartDoc );
     void exportAdditionalShapes( const 
css::uno::Reference<css::chart::XChartDocument >& rChartDoc );
diff --git a/oox/source/export/chartexport.cxx 
b/oox/source/export/chartexport.cxx
index f9c4c12d066d..75283815a831 100644
--- a/oox/source/export/chartexport.cxx
+++ b/oox/source/export/chartexport.cxx
@@ -1134,7 +1134,6 @@ void ChartExport::exportChart( const Reference< 
css::chart::XChartDocument >& xC
 
     // get Properties of ChartDocument
     bool bHasMainTitle = false;
-    OUString aSubTitle;
     bool bHasLegend = false;
     Reference< beans::XPropertySet > xDocPropSet( xChartDoc, uno::UNO_QUERY );
     if( xDocPropSet.is())
@@ -1152,12 +1151,13 @@ void ChartExport::exportChart( const Reference< 
css::chart::XChartDocument >& xC
         }
     } // if( xDocPropSet.is())
 
+    Sequence< uno::Reference< chart2::XFormattedString > > xFormattedSubTitle;
     Reference< beans::XPropertySet > xPropSubTitle( xChartDoc->getSubTitle(), 
UNO_QUERY );
     if( xPropSubTitle.is())
     {
         try
         {
-            xPropSubTitle->getPropertyValue("String") >>= aSubTitle;
+            xPropSubTitle->getPropertyValue("FormattedStrings") >>= 
xFormattedSubTitle;
         }
         catch( beans::UnknownPropertyException & )
         {
@@ -1171,12 +1171,12 @@ void ChartExport::exportChart( const Reference< 
css::chart::XChartDocument >& xC
     // titles
     if( bHasMainTitle )
     {
-        exportTitle( xChartDoc->getTitle(), !aSubTitle.isEmpty() ? &aSubTitle 
: nullptr );
+        exportTitle( xChartDoc->getTitle(), xFormattedSubTitle);
         pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
     }
-    else if( !aSubTitle.isEmpty() )
+    else if( xFormattedSubTitle.hasElements() )
     {
-        exportTitle( xChartDoc->getSubTitle(), nullptr );
+        exportTitle( xChartDoc->getSubTitle() );
         pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
     }
     else
@@ -1445,20 +1445,40 @@ void ChartExport::exportLegend( const Reference< 
css::chart::XChartDocument >& x
     pFS->endElement( FSNS( XML_c, XML_legend ) );
 }
 
-void ChartExport::exportTitle( const Reference< XShape >& xShape, const 
OUString* pSubText)
+void ChartExport::exportTitle( const Reference< XShape >& xShape,
+    const css::uno::Sequence< uno::Reference< css::chart2::XFormattedString > 
>& xFormattedSubTitle )
 {
-    OUString sText;
+    Sequence< uno::Reference< chart2::XFormattedString > > xFormattedTitle;
     Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
     if( xPropSet.is())
     {
-        xPropSet->getPropertyValue("String") >>= sText;
+        xPropSet->getPropertyValue("FormattedStrings") >>= xFormattedTitle;
     }
 
     // tdf#101322: add subtitle to title
-    if( pSubText )
-        sText = sText.isEmpty() ? *pSubText : sText + "
" + *pSubText;
+    if (xFormattedSubTitle.hasElements())
+    {
+        if (!xFormattedTitle.hasElements())
+        {
+            xFormattedTitle = xFormattedSubTitle;
+        }
+        else
+        {
+            sal_uInt32 nLength = xFormattedTitle.size();
+            const OUString aLastString = xFormattedTitle.getArray()[nLength - 
1]->getString();
+            xFormattedTitle.getArray()[nLength - 1]->setString(aLastString + 
OUStringChar('
'));
+            for (const uno::Reference<chart2::XFormattedString>& rxFS : 
xFormattedSubTitle)
+            {
+                if (!rxFS->getString().isEmpty())
+                {
+                    xFormattedTitle.realloc(nLength + 1);
+                    xFormattedTitle.getArray()[nLength++] = rxFS;
+                }
+            }
+        }
+    }
 
-    if( sText.isEmpty() )
+    if (!xFormattedTitle.hasElements())
         return;
 
     FSHelperPtr pFS = GetFS();
@@ -1482,7 +1502,6 @@ void ChartExport::exportTitle( const Reference< XShape >& 
xShape, const OUString
             XML_rot, oox::drawingml::calcRotationValue(nRotation) );
     // TODO: lstStyle
     pFS->singleElement(FSNS(XML_a, XML_lstStyle));
-    // FIXME: handle multiple paragraphs to parse aText
     pFS->startElement(FSNS(XML_a, XML_p));
 
     pFS->startElement(FSNS(XML_a, XML_pPr));
@@ -1493,13 +1512,37 @@ void ChartExport::exportTitle( const Reference< XShape 
>& xShape, const OUString
 
     pFS->endElement( FSNS( XML_a, XML_pPr ) );
 
-    pFS->startElement(FSNS(XML_a, XML_r));
-    bDummy = false;
-    WriteRunProperties( xPropSet, false, XML_rPr, true, bDummy, nDummy );
-    pFS->startElement(FSNS(XML_a, XML_t));
-    pFS->writeEscaped( sText );
-    pFS->endElement( FSNS( XML_a, XML_t ) );
-    pFS->endElement( FSNS( XML_a, XML_r ) );
+    for (const uno::Reference<chart2::XFormattedString>& rxFS : 
xFormattedTitle)
+    {
+        pFS->startElement(FSNS(XML_a, XML_r));
+        bDummy = false;
+        Reference< beans::XPropertySet > xRunPropSet(rxFS, uno::UNO_QUERY);
+        WriteRunProperties(xRunPropSet, false, XML_rPr, true, bDummy, nDummy);
+        pFS->startElement(FSNS(XML_a, XML_t));
+
+        // the linebreak should always be at the end of the XFormattedString 
text
+        bool bNextPara = rxFS->getString().endsWith(u"
");
+        if (!bNextPara)
+            pFS->writeEscaped(rxFS->getString());
+        else
+        {
+            sal_Int32 nEnd = rxFS->getString().lastIndexOf('
');
+            pFS->writeEscaped(rxFS->getString().replaceAt(nEnd, 1, u""));
+        }
+        pFS->endElement(FSNS(XML_a, XML_t));
+        pFS->endElement(FSNS(XML_a, XML_r));
+
+        if (bNextPara)
+        {
+            pFS->endElement(FSNS(XML_a, XML_p));
+
+            pFS->startElement(FSNS(XML_a, XML_p));
+            pFS->startElement(FSNS(XML_a, XML_pPr));
+            bDummy = false;
+            WriteRunProperties(xPropSet, false, XML_defRPr, true, bDummy, 
nDummy);
+            pFS->endElement(FSNS(XML_a, XML_pPr));
+        }
+    }
 
     pFS->endElement( FSNS( XML_a, XML_p ) );
 
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index 59b32eb2df4e..450d374b3b89 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -2518,6 +2518,9 @@ void DrawingML::WriteRunProperties( const Reference< 
XPropertySet >& rRun, bool
     {
         switch ( *o3tl::doAccess<sal_Int16>(mAny) )
         {
+            case awt::FontUnderline::NONE :
+                underline = "none";
+                break;
             case awt::FontUnderline::SINGLE :
                 underline = "sng";
                 break;
diff --git a/xmloff/source/chart/SchXMLAxisContext.cxx 
b/xmloff/source/chart/SchXMLAxisContext.cxx
index 39d93cb26662..b54808790845 100644
--- a/xmloff/source/chart/SchXMLAxisContext.cxx
+++ b/xmloff/source/chart/SchXMLAxisContext.cxx
@@ -564,6 +564,7 @@ void SchXMLAxisContext::SetAxisTitle()
     {
         try
         {
+            // TODO: ODF import for formatted chart titles
             xTitleProp->setPropertyValue("String", 
uno::Any(m_aCurrentAxis.aTitle) );
         }
         catch( beans::UnknownPropertyException & )
diff --git a/xmloff/source/chart/SchXMLChartContext.cxx 
b/xmloff/source/chart/SchXMLChartContext.cxx
index 9d5522feaceb..6072a1a09622 100644
--- a/xmloff/source/chart/SchXMLChartContext.cxx
+++ b/xmloff/source/chart/SchXMLChartContext.cxx
@@ -736,6 +736,7 @@ void SchXMLChartContext::endFastElement(sal_Int32 )
             {
                 try
                 {
+                    // TODO: ODF import for formatted chart titles
                     xTitleProp->setPropertyValue("String", 
uno::Any(maMainTitle) );
                 }
                 catch(const beans::UnknownPropertyException&)
@@ -751,6 +752,7 @@ void SchXMLChartContext::endFastElement(sal_Int32 )
             {
                 try
                 {
+                    // TODO: ODF import for formatted chart titles
                     xTitleProp->setPropertyValue("String", 
uno::Any(maSubTitle) );
                 }
                 catch(const beans::UnknownPropertyException&)
diff --git a/xmloff/source/chart/SchXMLExport.cxx 
b/xmloff/source/chart/SchXMLExport.cxx
index 79238a5b34be..56cdca7bdefa 100644
--- a/xmloff/source/chart/SchXMLExport.cxx
+++ b/xmloff/source/chart/SchXMLExport.cxx
@@ -1344,6 +1344,7 @@ void SchXMLExportHelper_Impl::parseDocument( Reference< 
chart::XChartDocument >
             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;
@@ -1385,6 +1386,7 @@ void SchXMLExportHelper_Impl::parseDocument( Reference< 
chart::XChartDocument >
             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;
@@ -2264,6 +2266,7 @@ 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;

Reply via email to