From 85753e0f2759960dd87f6ecf9245e764a9f9bf11 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Szymon=20K=C5=82os?= <szymon.klos@collabora.com>
Date: Thu, 4 Jan 2018 22:15:32 +0100
Subject: [PATCH 1/2] tdf#114821 import complex data labels in bar chart
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* import static text & fields:
   VALUE, SERIESNAME, COLUMNNAME
* text is formatted
* DataPointCustomLabelField with field type
  (DataPointCustomLabelFieldType) was introduced.
* text can have many portions & multiple lines
* unit tests for import data labels with formatting

Not implemented: CELLREF field support which needs
importing some additional data from extLst

Shows custom text as a label for data points.

Change-Id: Iba8fd508eb16356b05586b93d7b8da32240d2b91
Reviewed-on: https://gerrit.libreoffice.org/48243
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Szymon Kłos <szymon.klos@collabora.com>
---
 chart2/inc/unonames.hxx                            |   1 +
 chart2/qa/extras/chart2import.cxx                  | 154 +++++++++++++++++++++
 chart2/qa/extras/data/pptx/tdf115107-2.pptx        | Bin 0 -> 50519 bytes
 chart2/qa/extras/data/pptx/tdf115107.pptx          | Bin 0 -> 50726 bytes
 chart2/source/chartcore.component                  |   1 +
 chart2/source/model/main/DataPointProperties.cxx   |  11 +-
 chart2/source/model/main/DataPointProperties.hxx   |   3 +-
 chart2/source/model/main/FormattedString.cxx       |  40 ++++++
 chart2/source/model/main/FormattedString.hxx       |  16 ++-
 chart2/source/view/charttypes/VSeriesPlotter.cxx   | 126 +++++++++++++++--
 chart2/source/view/inc/AbstractShapeFactory.hxx    |   7 +
 chart2/source/view/inc/OpenglShapeFactory.hxx      |   7 +
 chart2/source/view/inc/ShapeFactory.hxx            |   7 +
 chart2/source/view/inc/VSeriesPlotter.hxx          |   2 +
 chart2/source/view/main/OpenglShapeFactory.cxx     |  12 ++
 chart2/source/view/main/ShapeFactory.cxx           |  94 +++++++++++++
 offapi/UnoApi_offapi.mk                            |   3 +
 .../sun/star/chart2/DataPointCustomLabelField.idl  |  26 ++++
 .../star/chart2/DataPointCustomLabelFieldType.idl  |  33 +++++
 offapi/com/sun/star/chart2/DataPointProperties.idl |   8 ++
 .../sun/star/chart2/XDataPointCustomLabelField.idl |  43 ++++++
 oox/inc/drawingml/textfield.hxx                    |   2 +
 oox/source/drawingml/chart/seriesconverter.cxx     |  78 +++++++++++
 oox/source/token/properties.txt                    |   1 +
 24 files changed, 657 insertions(+), 18 deletions(-)
 create mode 100644 chart2/qa/extras/data/pptx/tdf115107-2.pptx
 create mode 100644 chart2/qa/extras/data/pptx/tdf115107.pptx
 create mode 100644 offapi/com/sun/star/chart2/DataPointCustomLabelField.idl
 create mode 100644 offapi/com/sun/star/chart2/DataPointCustomLabelFieldType.idl
 create mode 100644 offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl

diff --git a/chart2/inc/unonames.hxx b/chart2/inc/unonames.hxx
index 867a155e14aa..44c3bf67d3cb 100644
--- a/chart2/inc/unonames.hxx
+++ b/chart2/inc/unonames.hxx
@@ -29,6 +29,7 @@
 #define CHART_UNONAME_LABEL_BORDER_DASH     "LabelBorderDash"
 #define CHART_UNONAME_LABEL_BORDER_DASHNAME "LabelBorderDashName"
 #define CHART_UNONAME_LABEL_BORDER_TRANS    "LabelBorderTransparency"
+#define CHART_UNONAME_CUSTOM_LABEL_FIELDS   "CustomLabelFields"

 #endif

diff --git a/chart2/source/chartcore.component b/chart2/source/chartcore.component
index f7a1783eda68..45c87f93d633 100644
--- a/chart2/source/chartcore.component
+++ b/chart2/source/chartcore.component
@@ -158,6 +158,7 @@
     constructor="com_sun_star_comp_chart_FormattedString_get_implementation">
     <service name="com.sun.star.beans.PropertySet"/>
     <service name="com.sun.star.chart2.FormattedString"/>
+    <service name="com.sun.star.chart2.DataPointCustomLabelField"/>
   </implementation>
   <implementation name="com.sun.star.comp.chart.LineChartType"
     constructor="com_sun_star_comp_chart_LineChartType_get_implementation">
diff --git a/chart2/source/model/main/DataPointProperties.cxx b/chart2/source/model/main/DataPointProperties.cxx
index 66e0b88d1f5a..dbc950c2195b 100644
--- a/chart2/source/model/main/DataPointProperties.cxx
+++ b/chart2/source/model/main/DataPointProperties.cxx
@@ -30,7 +30,7 @@
 #include <com/sun/star/style/XStyle.hpp>
 #include <com/sun/star/drawing/BitmapMode.hpp>
 #include <com/sun/star/drawing/RectanglePoint.hpp>
-
+#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
 #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
 #include <com/sun/star/chart2/DataPointLabel.hpp>
 #include <com/sun/star/chart2/Symbol.hpp>
@@ -406,6 +406,12 @@ void DataPointProperties::AddPropertiesToVector(
                   cppu::UnoType<sal_Int16>::get(),
                   beans::PropertyAttribute::BOUND
                   | beans::PropertyAttribute::MAYBEDEFAULT );
+
+    rOutProperties.emplace_back( CHART_UNONAME_CUSTOM_LABEL_FIELDS,
+                  PROP_DATAPOINT_CUSTOM_LABEL_FIELDS,
+                  cppu::UnoType<uno::Sequence<uno::Reference<chart2::XDataPointCustomLabelField>>>::get(),
+                  beans::PropertyAttribute::BOUND
+                  | beans::PropertyAttribute::MAYBEDEFAULT);
 }

 void DataPointProperties::AddDefaultsToMap(
@@ -486,6 +492,9 @@ void DataPointProperties::AddDefaultsToMap(
     PropertyHelper::setPropertyValueDefault(rOutMap, PROP_DATAPOINT_LABEL_BORDER_DASH, drawing::LineDash());
     PropertyHelper::setEmptyPropertyValueDefault(rOutMap, PROP_DATAPOINT_LABEL_BORDER_DASH_NAME);
     PropertyHelper::setPropertyValueDefault<sal_Int16>(rOutMap, PROP_DATAPOINT_LABEL_BORDER_TRANS, 0);
+
+    uno::Sequence<uno::Reference<chart2::XDataPointCustomLabelField>> aFields(0);
+    PropertyHelper::setPropertyValueDefault(rOutMap, PROP_DATAPOINT_CUSTOM_LABEL_FIELDS, aFields);
 }

 } //  namespace chart
diff --git a/chart2/source/model/main/DataPointProperties.hxx b/chart2/source/model/main/DataPointProperties.hxx
index 3daf838e5da5..6490b5c8cfe5 100644
--- a/chart2/source/model/main/DataPointProperties.hxx
+++ b/chart2/source/model/main/DataPointProperties.hxx
@@ -80,7 +80,8 @@ namespace DataPointProperties
         PROP_DATAPOINT_LABEL_BORDER_WIDTH,
         PROP_DATAPOINT_LABEL_BORDER_DASH,
         PROP_DATAPOINT_LABEL_BORDER_DASH_NAME,
-        PROP_DATAPOINT_LABEL_BORDER_TRANS
+        PROP_DATAPOINT_LABEL_BORDER_TRANS,
+        PROP_DATAPOINT_CUSTOM_LABEL_FIELDS

         // additionally some properites from ::chart::LineProperties
     };
diff --git a/chart2/source/model/main/FormattedString.cxx b/chart2/source/model/main/FormattedString.cxx
index 4f26acc95e3f..2dfcca2d35c5 100644
--- a/chart2/source/model/main/FormattedString.cxx
+++ b/chart2/source/model/main/FormattedString.cxx
@@ -95,6 +95,8 @@ namespace chart
 FormattedString::FormattedString() :
         ::property::OPropertySet( m_aMutex ),
     m_aString(),
+    m_aType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT),
+    m_aGuid(),
     m_xModifyEventForwarder( ModifyListenerHelper::createModifyEventForwarder())
 {}

@@ -103,6 +105,8 @@ FormattedString::FormattedString( const FormattedString & rOther ) :
         impl::FormattedString_Base(),
         ::property::OPropertySet( rOther, m_aMutex ),
     m_aString( rOther.m_aString ),
+    m_aType(rOther.m_aType),
+    m_aGuid(rOther.m_aGuid),
     m_xModifyEventForwarder( ModifyListenerHelper::createModifyEventForwarder())
 {}

@@ -133,6 +137,41 @@ void SAL_CALL FormattedString::setString( const OUString& String )

 }

+// ____ XDataPointCustomLabelField ____
+css::chart2::DataPointCustomLabelFieldType SAL_CALL FormattedString::getFieldType()
+{
+    MutexGuard aGuard(GetMutex());
+    return m_aType;
+}
+
+void SAL_CALL
+FormattedString::setFieldType(const css::chart2::DataPointCustomLabelFieldType Type)
+{
+    {
+        MutexGuard aGuard(GetMutex());
+        m_aType = Type;
+    }
+    //don't keep the mutex locked while calling out
+    fireModifyEvent();
+}
+
+OUString SAL_CALL FormattedString::getGuid()
+{
+    MutexGuard aGuard( GetMutex());
+    return m_aGuid;
+}
+
+void SAL_CALL FormattedString::setGuid( const OUString& guid )
+{
+    {
+        MutexGuard aGuard( GetMutex());
+        m_aGuid= guid;
+    }
+    //don't keep the mutex locked while calling out
+    fireModifyEvent();
+
+}
+
 // ____ XModifyBroadcaster ____
 void SAL_CALL FormattedString::addModifyListener( const uno::Reference< util::XModifyListener >& aListener )
 {
@@ -226,6 +265,7 @@ sal_Bool SAL_CALL FormattedString::supportsService( const OUString& rServiceName
 css::uno::Sequence< OUString > SAL_CALL FormattedString::getSupportedServiceNames()
 {
     return {
+        "com.sun.star.chart2.DataPointCustomLabelField",
         "com.sun.star.chart2.FormattedString",
         "com.sun.star.beans.PropertySet" };
 }
diff --git a/chart2/source/model/main/FormattedString.hxx b/chart2/source/model/main/FormattedString.hxx
index 16c73372ebcc..aee7674081be 100644
--- a/chart2/source/model/main/FormattedString.hxx
+++ b/chart2/source/model/main/FormattedString.hxx
@@ -26,6 +26,8 @@
 #include <ModifyListenerHelper.hxx>
 #include <com/sun/star/lang/XServiceInfo.hpp>
 #include <com/sun/star/chart2/XFormattedString2.hpp>
+#include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
+#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
 #include <com/sun/star/util/XCloneable.hpp>
 #include <com/sun/star/uno/XComponentContext.hpp>

@@ -35,7 +37,7 @@ namespace chart
 namespace impl
 {
 typedef ::cppu::WeakImplHelper<
-    css::chart2::XFormattedString2,
+    css::chart2::XDataPointCustomLabelField, // inherits from XFormattedString2
     css::lang::XServiceInfo,
     css::util::XCloneable,
     css::util::XModifyBroadcaster,
@@ -82,6 +84,13 @@ private:
     virtual OUString SAL_CALL getString() override;
     virtual void SAL_CALL setString( const OUString& String ) override;

+    // ____ XDataPointCustomLabelField ____
+    virtual css::chart2::DataPointCustomLabelFieldType SAL_CALL getFieldType() override;
+    virtual void SAL_CALL
+        setFieldType( const css::chart2::DataPointCustomLabelFieldType FieldType ) override;
+    virtual OUString SAL_CALL getGuid() override;
+    void SAL_CALL setGuid( const OUString& guid ) override;
+
     // ____ OPropertySet ____
     virtual css::uno::Any GetDefaultValue( sal_Int32 nHandle ) const override;

@@ -115,8 +124,13 @@ private:

     void fireModifyEvent();

+    // ____ XFormattedString ____
     OUString m_aString;

+    // ____ XDataPointCustomLabelField ____
+    css::chart2::DataPointCustomLabelFieldType m_aType;
+    OUString m_aGuid;
+
     css::uno::Reference< css::util::XModifyListener > m_xModifyEventForwarder;
 };

diff --git a/chart2/source/view/charttypes/VSeriesPlotter.cxx b/chart2/source/view/charttypes/VSeriesPlotter.cxx
index af1544b22815..23cad6ada542 100644
--- a/chart2/source/view/charttypes/VSeriesPlotter.cxx
+++ b/chart2/source/view/charttypes/VSeriesPlotter.cxx
@@ -56,6 +56,7 @@

 #include <com/sun/star/chart/ErrorBarStyle.hpp>
 #include <com/sun/star/chart/TimeUnit.hpp>
+#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
 #include <com/sun/star/container/XChild.hpp>
 #include <com/sun/star/chart2/RelativePosition.hpp>
@@ -413,9 +414,21 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
                     , sal_Int32 nTextWidth )
 {
     uno::Reference< drawing::XShape > xTextShape;
+    Sequence<uno::Reference<XDataPointCustomLabelField>> aCustomLabels;

     try
     {
+        const uno::Reference< css::beans::XPropertySet >& xPropertySet(
+            rDataSeries.getPropertiesOfPoint( nPointIndex ) );
+        if( xPropertySet.is() )
+        {
+            uno::Any aAny = xPropertySet->getPropertyValue( CHART_UNONAME_CUSTOM_LABEL_FIELDS );
+            if( aAny.hasValue() )
+            {
+                aAny >>= aCustomLabels;
+            }
+        }
+
         awt::Point aScreenPosition2D(rScreenPosition2D);
         if(eAlignment==LABEL_ALIGN_LEFT)
             aScreenPosition2D.X -= nOffset;
@@ -495,26 +508,75 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
         }

         sal_Int32 nLineCountForSymbolsize = 0;
-        Sequence< OUString > aTextList(3);
+        sal_uInt32 nTextListLength = 3;
+        sal_uInt32 nCustomLabelsCount = aCustomLabels.getLength();
+        bool bUseCustomLabel = false;
+        Sequence< OUString > aTextList( nTextListLength );
+
+        bUseCustomLabel = nCustomLabelsCount > 0;
+        if( bUseCustomLabel )
         {
-            if(pLabel->ShowCategoryName)
+            nTextListLength = ( nCustomLabelsCount > 3 ) ? nCustomLabelsCount : 3;
+            aSeparator = "";
+            aTextList = Sequence< OUString >( nTextListLength );
+            for( sal_uInt32 i = 0; i < nCustomLabelsCount; ++i )
             {
-                if( m_pExplicitCategoriesProvider )
+                switch( aCustomLabels[i]->getFieldType() )
                 {
-                    Sequence< OUString > aCategories( m_pExplicitCategoriesProvider->getSimpleCategories() );
-                    if( nPointIndex >= 0 && nPointIndex < aCategories.getLength() )
+                    case DataPointCustomLabelFieldType_VALUE:
+                    {
+                        aTextList[i] = getLabelTextForValue( rDataSeries, nPointIndex, fValue, false );
+                        break;
+                    }
+                    case DataPointCustomLabelFieldType_CATEGORYNAME:
+                    {
+                        aTextList[i] = getCategoryName( nPointIndex );
+                        break;
+                    }
+                    case DataPointCustomLabelFieldType_SERIESNAME:
                     {
-                        aTextList[0] = aCategories[nPointIndex];
+                        OUString aRole;
+                        if ( m_xChartTypeModel )
+                            aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel();
+                        uno::Reference< XDataSeries > xSeries( rDataSeries.getModel() );
+                        aTextList[i] = DataSeriesHelper::getDataSeriesLabel( xSeries, aRole );
+                        break;
                     }
+                    case DataPointCustomLabelFieldType_CELLREF:
+                    {
+                        // TODO: for now doesn't show placeholder
+                        aTextList[i] = OUString();
+                        break;
+                    }
+                    case DataPointCustomLabelFieldType_TEXT:
+                    {
+                        aTextList[i] = aCustomLabels[i]->getString();
+                        break;
+                    }
+                    case DataPointCustomLabelFieldType_NEWLINE:
+                    {
+                        aTextList[i] = "\n";
+                        break;
+                    }
+                    default:
+                    break;
                 }
+                aCustomLabels[i]->setString( aTextList[i] );
+            }
+        }
+        else
+        {
+            if( pLabel->ShowCategoryName )
+            {
+                aTextList[0] = getCategoryName( nPointIndex );
             }

-            if(pLabel->ShowNumber)
+            if( pLabel->ShowNumber )
             {
                 aTextList[1] = getLabelTextForValue(rDataSeries, nPointIndex, fValue, false);
             }

-            if(pLabel->ShowNumberInPercent)
+            if( pLabel->ShowNumberInPercent )
             {
                 if(fSumValue==0.0)
                     fSumValue=1.0;
@@ -524,13 +586,13 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re

                 aTextList[2] = getLabelTextForValue(rDataSeries, nPointIndex, fValue, true);
             }
+        }

-            for( sal_Int32 nN = 0; nN < 3; ++nN)
+        for( sal_Int32 nN = 0; nN < aTextList.getLength(); ++nN )
+        {
+            if( !aTextList[nN].isEmpty() )
             {
-                if( !aTextList[nN].isEmpty() )
-                {
-                    ++nLineCountForSymbolsize;
-                }
+                ++nLineCountForSymbolsize;
             }
         }

@@ -549,7 +611,28 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
         // a multi-line label.
         bool bMultiLineLabel = ( aSeparator == "\n" );

-        if( bMultiLineLabel )
+        if( bUseCustomLabel )
+        {
+            Sequence< uno::Reference< XFormattedString > > aFormattedLabels( aCustomLabels.getLength() );
+            for( int i = 0; i < aFormattedLabels.getLength(); i++ )
+            {
+                uno::Reference< XFormattedString > xString( aCustomLabels[i], uno::UNO_QUERY );
+                aFormattedLabels[i] = xString;
+            }
+
+            // center the text
+            sal_uInt32 nProperties = pPropNames->getLength();
+            pPropNames->realloc( nProperties + 1 );
+            pPropValues->realloc( nProperties + 1 );
+            (*pPropNames)[ nProperties ] = UNO_NAME_EDIT_PARA_ADJUST;
+            (*pPropValues)[ nProperties ] <<= style::ParagraphAdjust_CENTER;
+
+            // create text shape
+            xTextShape = AbstractShapeFactory::getOrCreateShapeFactory( m_xShapeFactory )->
+                createText( xTarget_, aFormattedLabels, *pPropNames, *pPropValues,
+                    AbstractShapeFactory::makeTransformation( aScreenPosition2D ) );
+        }
+        else if( bMultiLineLabel )
         {
             // prepare properties for each paragraph
             // we want to have the value and percent value centered respect
@@ -575,7 +658,7 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
         {
             // join text list elements
             OUStringBuffer aText;
-            for( sal_Int32 nN = 0; nN < 3; ++nN)
+            for( sal_uInt32 nN = 0; nN < nTextListLength; ++nN)
             {
                 if( !aTextList[nN].isEmpty() )
                 {
@@ -2050,6 +2133,19 @@ VDataSeries* VSeriesPlotter::getFirstSeries() const
     return nullptr;
 }

+OUString VSeriesPlotter::getCategoryName( sal_Int32 nPointIndex ) const
+{
+    if (m_pExplicitCategoriesProvider)
+    {
+        Sequence< OUString > aCategories(m_pExplicitCategoriesProvider->getSimpleCategories());
+        if (nPointIndex >= 0 && nPointIndex < aCategories.getLength())
+        {
+            return aCategories[nPointIndex];
+        }
+    }
+    return OUString();
+}
+
 uno::Sequence< OUString > VSeriesPlotter::getSeriesNames() const
 {
     std::vector<OUString> aRetVector;
diff --git a/chart2/source/view/inc/AbstractShapeFactory.hxx b/chart2/source/view/inc/AbstractShapeFactory.hxx
index 26512214341d..e281e6291ebf 100644
--- a/chart2/source/view/inc/AbstractShapeFactory.hxx
+++ b/chart2/source/view/inc/AbstractShapeFactory.hxx
@@ -200,6 +200,13 @@ public:
                     , const css::uno::Any& rATransformation ) = 0;

     virtual css::uno::Reference< css::drawing::XShape >
+        createText(const css::uno::Reference< css::drawing::XShapes >& xTarget
+            , css::uno::Sequence< css::uno::Reference< css::chart2::XFormattedString > >& xFormattedString
+            , const tNameSequence& rPropNames
+            , const tAnySequence& rPropValues
+            , const css::uno::Any& rATransformation) = 0;
+
+    virtual css::uno::Reference< css::drawing::XShape >
         createText( const css::uno::Reference< css::drawing::XShapes >& xTarget2D,
                 const css::awt::Size& rSize,
                 const css::awt::Point& rPosition,
diff --git a/chart2/source/view/inc/OpenglShapeFactory.hxx b/chart2/source/view/inc/OpenglShapeFactory.hxx
index 01b2223cf59c..7963068ce79e 100644
--- a/chart2/source/view/inc/OpenglShapeFactory.hxx
+++ b/chart2/source/view/inc/OpenglShapeFactory.hxx
@@ -151,6 +151,13 @@ public:
                     , const css::uno::Any& rATransformation ) override;

     virtual css::uno::Reference< css::drawing::XShape >
+        createText( const css::uno::Reference< css::drawing::XShapes >& xTarget
+                    , css::uno::Sequence< css::uno::Reference< css::chart2::XFormattedString > >& xFormattedString
+                    , const tNameSequence& rPropNames
+                    , const tAnySequence& rPropValues
+                    , const css::uno::Any& rATransformation ) override;
+
+    virtual css::uno::Reference< css::drawing::XShape >
         createText( const css::uno::Reference< css::drawing::XShapes >& xTarget2D,
                 const css::awt::Size& rSize,
                 const css::awt::Point& rPosition,
diff --git a/chart2/source/view/inc/ShapeFactory.hxx b/chart2/source/view/inc/ShapeFactory.hxx
index 02a75d3a0764..ec1080732142 100644
--- a/chart2/source/view/inc/ShapeFactory.hxx
+++ b/chart2/source/view/inc/ShapeFactory.hxx
@@ -189,6 +189,13 @@ public:
                     , const css::uno::Any& rATransformation ) override;

     virtual css::uno::Reference< css::drawing::XShape >
+        createText(const css::uno::Reference< css::drawing::XShapes >& xTarget
+            , css::uno::Sequence< css::uno::Reference< css::chart2::XFormattedString > >& xFormattedString
+            , const tNameSequence& rPropNames
+            , const tAnySequence& rPropValues
+            , const css::uno::Any& rATransformation) override;
+
+    virtual css::uno::Reference< css::drawing::XShape >
         createText( const css::uno::Reference< css::drawing::XShapes >& xTarget2D,
                 const css::awt::Size& rSize,
                 const css::awt::Point& rPosition,
diff --git a/chart2/source/view/inc/VSeriesPlotter.hxx b/chart2/source/view/inc/VSeriesPlotter.hxx
index 54f3f7733d63..d8b1cf05eb2b 100644
--- a/chart2/source/view/inc/VSeriesPlotter.hxx
+++ b/chart2/source/view/inc/VSeriesPlotter.hxx
@@ -389,6 +389,8 @@ protected:

     VDataSeries* getFirstSeries() const;

+    OUString getCategoryName( sal_Int32 nPointIndex ) const;
+
 protected:
     PlottingPositionHelper*    m_pMainPosHelper;

diff --git a/chart2/source/view/main/OpenglShapeFactory.cxx b/chart2/source/view/main/OpenglShapeFactory.cxx
index 9cb3318f7fa2..e6c4980b8017 100644
--- a/chart2/source/view/main/OpenglShapeFactory.cxx
+++ b/chart2/source/view/main/OpenglShapeFactory.cxx
@@ -408,6 +408,18 @@ uno::Reference< drawing::XShape >
 }

 uno::Reference< drawing::XShape >
+        OpenglShapeFactory::createText( const uno::Reference< drawing::XShapes >& xTarget
+                    , uno::Sequence< uno::Reference< chart2::XFormattedString > >& rFormattedString
+                    , const tNameSequence& rPropNames
+                    , const tAnySequence& rPropValues
+                    , const uno::Any& rATransformation )
+{
+    dummy::DummyText* pText = new dummy::DummyText( rFormattedString[0]->getString(), rPropNames, rPropValues,
+            rATransformation, xTarget, 0 );
+    return pText;
+}
+
+uno::Reference< drawing::XShape >
         OpenglShapeFactory::createText( const uno::Reference< drawing::XShapes >& xTarget,
                 const awt::Size& , const awt::Point& rPos,
                 uno::Sequence< uno::Reference< chart2::XFormattedString > >& rFormattedString,
diff --git a/chart2/source/view/main/ShapeFactory.cxx b/chart2/source/view/main/ShapeFactory.cxx
index 3ed37f316225..2a72f969fa10 100644
--- a/chart2/source/view/main/ShapeFactory.cxx
+++ b/chart2/source/view/main/ShapeFactory.cxx
@@ -2237,6 +2237,100 @@ uno::Reference< drawing::XShape >
 }

 uno::Reference< drawing::XShape >
+    ShapeFactory::createText( const uno::Reference< drawing::XShapes >& xTarget
+                , uno::Sequence< uno::Reference< chart2::XFormattedString > >& xFormattedString
+                , const tNameSequence& rPropNames
+                , const tAnySequence& rPropValues
+                , const uno::Any& rATransformation )
+{
+    if( !xTarget.is() )
+        return nullptr;
+
+    if( !xFormattedString.hasElements() )
+        return nullptr;
+
+    sal_Int32 nNumberOfParagraphs = xFormattedString.getLength();
+
+    bool bNotEmpty = false;
+    for( sal_Int32 nN = 0; nN < nNumberOfParagraphs; ++nN )
+    {
+        if( !xFormattedString[nN]->getString().isEmpty() )
+        {
+            bNotEmpty = true;
+            break;
+        }
+    }
+    if( !bNotEmpty )
+        return nullptr;
+
+    //create shape and add to page
+    uno::Reference< drawing::XShape > xShape(
+            m_xShapeFactory->createInstance(
+            "com.sun.star.drawing.TextShape" ), uno::UNO_QUERY );
+    xTarget->add(xShape);
+
+    //set paragraph properties
+    bNotEmpty = false;
+    Reference< text::XText > xText( xShape, uno::UNO_QUERY );
+    if( xText.is() )
+    {
+        // the first cursor is used for appending the next paragraph,
+        // after a new string has been inserted the cursor is moved at the end
+        // of the inserted string
+        // the second cursor is used for selecting the paragraph and apply the
+        // passed text properties
+        Reference< text::XTextCursor > xInsertCursor = xText->createTextCursor();
+        Reference< text::XTextCursor > xSelectionCursor = xText->createTextCursor();
+        if( xInsertCursor.is() && xSelectionCursor.is() )
+        {
+            uno::Reference< beans::XPropertySet > xSelectionProp( xSelectionCursor, uno::UNO_QUERY );
+            if( xSelectionProp.is() )
+            {
+                for( sal_Int32 nN = 0; nN < nNumberOfParagraphs; ++nN )
+                {
+                    if( !xFormattedString[nN]->getString().isEmpty() )
+                    {
+                        xInsertCursor->gotoEnd( false );
+                        xSelectionCursor->gotoEnd( false );
+                        xText->insertString( xInsertCursor, xFormattedString[nN]->getString(), false );
+                        bNotEmpty = true;
+                        xSelectionCursor->gotoEnd( true ); // select current paragraph
+                        uno::Reference< beans::XPropertySet > xStringProperties( xFormattedString[nN], uno::UNO_QUERY );
+                        PropertyMapper::setMappedProperties( xSelectionProp, xStringProperties,
+                            PropertyMapper::getPropertyNameMapForTextShapeProperties() );
+                    }
+                }
+            }
+        }
+    }
+
+    if( !bNotEmpty )
+        return nullptr;
+
+    uno::Reference< beans::XPropertySet > xProp( xShape, uno::UNO_QUERY );
+    if( xProp.is() )
+    {
+        //set whole text shape properties
+        PropertyMapper::setMultiProperties( rPropNames, rPropValues, xProp );
+
+        if( rATransformation.hasValue() )
+        {
+            //set position matrix
+            //the matrix needs to be set at the end behind autogrow and such position influencing properties
+            try
+            {
+                xProp->setPropertyValue( "Transformation", rATransformation );
+            }
+            catch( const uno::Exception& e )
+            {
+                SAL_WARN("chart2", "Exception caught. " << e );
+            }
+        }
+    }
+    return xShape;
+}
+
+uno::Reference< drawing::XShape >
         ShapeFactory::createText( const uno::Reference< drawing::XShapes >& xTarget,
                 const awt::Size& rSize,
                 const awt::Point& rPos,
diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk
index 472771a5c15d..b50864a25880 100644
--- a/offapi/UnoApi_offapi.mk
+++ b/offapi/UnoApi_offapi.mk
@@ -69,6 +69,8 @@ $(eval $(call gb_UnoApi_add_idlfiles_nohdl,offapi,com/sun/star/awt/tree,\
 $(eval $(call gb_UnoApi_add_idlfiles_nohdl,offapi,com/sun/star/chart2,\
 	CartesianCoordinateSystem2d \
 	CartesianCoordinateSystem3d \
+	DataPointCustomLabelField \
+	DataPointCustomLabelFieldType \
 	ExponentialRegressionCurve \
 	ExponentialScaling \
 	FormattedString \
@@ -2024,6 +2026,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/chart2,\
 	XCoordinateSystem \
 	XCoordinateSystemContainer \
 	XDataInterpreter \
+	XDataPointCustomLabelField \
 	XDataSeries \
 	XDataSeriesContainer \
 	XDefaultSizeTransmitter \
diff --git a/offapi/com/sun/star/chart2/DataPointCustomLabelField.idl b/offapi/com/sun/star/chart2/DataPointCustomLabelField.idl
new file mode 100644
index 000000000000..cebe1c3273f3
--- /dev/null
+++ b/offapi/com/sun/star/chart2/DataPointCustomLabelField.idl
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef com_sun_star_chart2_DataPointCustomLabelField_idl
+#define com_sun_star_chart2_DataPointCustomLabelField_idl
+
+#include <com/sun/star/chart2/XDataPointCustomLabelField.idl>
+
+module com { module sun { module star { module chart2 {
+
+/**
+    @since LibreOffice 6.1
+*/
+service DataPointCustomLabelField : XDataPointCustomLabelField;
+
+}; }; }; };
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
\ No newline at end of file
diff --git a/offapi/com/sun/star/chart2/DataPointCustomLabelFieldType.idl b/offapi/com/sun/star/chart2/DataPointCustomLabelFieldType.idl
new file mode 100644
index 000000000000..0b7f925342a6
--- /dev/null
+++ b/offapi/com/sun/star/chart2/DataPointCustomLabelFieldType.idl
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef com_sun_star_chart2_DataPointCustomLabelFieldType_idl
+#define com_sun_star_chart2_DataPointCustomLabelFieldType_idl
+
+module com { module sun { module star { module chart2 {
+
+/** The Field type enumeration for custom data point labels.
+
+    @since LibreOffice 6.1
+ */
+enum DataPointCustomLabelFieldType
+{
+    TEXT,
+    VALUE,
+    SERIESNAME,
+    CATEGORYNAME,
+    CELLREF,
+    NEWLINE
+};
+
+}; }; }; };
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
\ No newline at end of file
diff --git a/offapi/com/sun/star/chart2/DataPointProperties.idl b/offapi/com/sun/star/chart2/DataPointProperties.idl
index 31bd0a42696c..df5cf071ee90 100644
--- a/offapi/com/sun/star/chart2/DataPointProperties.idl
+++ b/offapi/com/sun/star/chart2/DataPointProperties.idl
@@ -31,6 +31,7 @@
 #include <com/sun/star/drawing/RectanglePoint.idl>
 #include <com/sun/star/chart2/DataPointLabel.idl>
 #include <com/sun/star/chart2/Symbol.idl>
+#include <com/sun/star/chart2/XFormattedString2.idl>

 module com
 {
@@ -258,6 +259,13 @@ service DataPointProperties

     [property] DataPointLabel                         Label;

+    /** specifies a text with possible fields that is used as a data point label,
+        if set then Label property is ignored
+
+        @since LibreOffice 6.1
+    */
+    [optional, property] sequence<XDataPointCustomLabelField>  CustomLabelFields;
+
     /** specifies a string that is used to separate the parts of a data label (caption)
      */
     [optional, property] string LabelSeparator;
diff --git a/offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl b/offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl
new file mode 100644
index 000000000000..a6a1b0151c94
--- /dev/null
+++ b/offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#ifndef com_sun_star_chart2_DataPointCustomLabelField_idl
+#define com_sun_star_chart2_DataPointCustomLabelField_idl
+
+#include <com/sun/star/chart2/XFormattedString2.idl>
+#include <com/sun/star/chart2/DataPointCustomLabelFieldType.idl>
+
+module com { module sun { module star { module chart2 {
+
+/**
+    Provides interface for DataPointCustomLabelField service.
+
+    @since LibreOffice 6.1
+*/
+interface XDataPointCustomLabelField : XFormattedString2
+{
+    DataPointCustomLabelFieldType getFieldType();
+
+    void setFieldType( [in] DataPointCustomLabelFieldType fieldType );
+
+    string getGuid();
+
+    void setGuid( [in] string guid );
+
+};
+
+
+
+} ; // chart2
+} ; // com
+} ; // sun
+} ; // star
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/oox/inc/drawingml/textfield.hxx b/oox/inc/drawingml/textfield.hxx
index 9f5bf9cf27af..7cc15a04fce4 100644
--- a/oox/inc/drawingml/textfield.hxx
+++ b/oox/inc/drawingml/textfield.hxx
@@ -38,7 +38,9 @@ public:
     const TextParagraphProperties& getTextParagraphProperties() const { return maTextParagraphProperties; }

     void setType( const OUString& sType ) { msType = sType; }
+    const OUString& getType() const { return msType; }
     void setUuid( const OUString & sUuid ) { msUuid = sUuid; }
+    const OUString& getUuid() const { return msUuid; }

     virtual sal_Int32    insertAt(
                         const ::oox::core::XmlFilterBase& rFilterBase,
diff --git a/oox/source/drawingml/chart/seriesconverter.cxx b/oox/source/drawingml/chart/seriesconverter.cxx
index 6eceb213ea04..1a5c2ba8d96d 100644
--- a/oox/source/drawingml/chart/seriesconverter.cxx
+++ b/oox/source/drawingml/chart/seriesconverter.cxx
@@ -22,11 +22,16 @@
 #include <com/sun/star/chart/DataLabelPlacement.hpp>
 #include <com/sun/star/chart/ErrorBarStyle.hpp>
 #include <com/sun/star/chart2/DataPointLabel.hpp>
+#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
+#include <com/sun/star/chart2/DataPointCustomLabelField.hpp>
+#include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
 #include <com/sun/star/chart2/XDataSeries.hpp>
 #include <com/sun/star/chart2/XRegressionCurve.hpp>
 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
 #include <com/sun/star/chart2/data/XDataSink.hpp>
 #include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
+#include <com/sun/star/chart2/XFormattedString2.hpp>
+#include <com/sun/star/chart2/FormattedString.hpp>
 #include <osl/diagnose.h>
 #include <basegfx/numeric/ftools.hxx>
 #include <drawingml/chart/datasourceconverter.hxx>
@@ -41,6 +46,10 @@
 #include <oox/token/properties.hxx>
 #include <oox/token/tokens.hxx>
 #include <drawingml/lineproperties.hxx>
+#include <drawingml/textparagraph.hxx>
+#include <drawingml/textrun.hxx>
+#include <drawingml/textfield.hxx>
+#include <drawingml/textbody.hxx>

 namespace oox {
 namespace drawingml {
@@ -200,6 +209,20 @@ void importBorderProperties( PropertySet& rPropSet, Shape& rShape, const Graphic
     rPropSet.setProperty(PROP_LabelBorderColor, uno::makeAny(nColor));
 }

+DataPointCustomLabelFieldType lcl_ConvertFieldNameToFieldEnum( const OUString& rField )
+{
+    if (rField == "VALUE")
+        return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_VALUE;
+    else if (rField == "SERIESNAME")
+        return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_SERIESNAME;
+    else if (rField == "CATEGORYNAME")
+        return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CATEGORYNAME;
+    else if (rField == "CELLREF")
+        return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLREF;
+    else
+        return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT;
+}
+
 } // namespace

 DataLabelConverter::DataLabelConverter( const ConverterRoot& rParent, DataLabelModel& rModel ) :
@@ -247,6 +270,61 @@ void DataLabelConverter::convertFromModel( const Reference< XDataSeries >& rxDat

         if (mrModel.mxShapeProp)
             importBorderProperties(aPropSet, *mrModel.mxShapeProp, getFilter().getGraphicHelper());
+
+        if( mrModel.mxText && mrModel.mxText->mxTextBody && mrModel.mxText->mxTextBody->getParagraphs().size() )
+        {
+            css::uno::Reference< XComponentContext > xContext = getComponentContext();
+            uno::Sequence< css::uno::Reference< XDataPointCustomLabelField > > aSequence;
+
+            auto& rParagraphs = mrModel.mxText->mxTextBody->getParagraphs();
+
+            int nSequenceSize = 0;
+            for( auto& pParagraph : rParagraphs )
+                nSequenceSize += pParagraph->getRuns().size();
+
+            int nParagraphs = rParagraphs.size();
+            if( nParagraphs > 1 )
+                nSequenceSize += nParagraphs - 1;
+
+            aSequence.realloc( nSequenceSize );
+
+            int nPos = 0;
+            for( auto& pParagraph : rParagraphs )
+            {
+                for( auto& pRun : pParagraph->getRuns() )
+                {
+                    css::uno::Reference< XDataPointCustomLabelField > xCustomLabel = DataPointCustomLabelField::create( xContext );
+
+                    // Store properties
+                    oox::PropertySet aPropertySet( xCustomLabel );
+                    pRun->getTextCharacterProperties().pushToPropSet( aPropertySet, getFilter() );
+
+                    TextField* pField = nullptr;
+                    if( ( pField = dynamic_cast< TextField* >( pRun.get() ) ) )
+                    {
+                        xCustomLabel->setString( pField->getText() );
+                        xCustomLabel->setFieldType( lcl_ConvertFieldNameToFieldEnum( pField->getType() ) );
+                        xCustomLabel->setGuid( pField->getUuid() );
+                    }
+                    else if( pRun.get() )
+                    {
+                        xCustomLabel->setString( pRun->getText() );
+                        xCustomLabel->setFieldType( DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT );
+                    }
+                    aSequence[ nPos++ ] = xCustomLabel;
+                }
+
+                if( nParagraphs > 1 && nPos < nSequenceSize )
+                {
+                    css::uno::Reference< XDataPointCustomLabelField > xCustomLabel = DataPointCustomLabelField::create( xContext );
+                    xCustomLabel->setFieldType( DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_NEWLINE );
+                    xCustomLabel->setString("\n");
+                    aSequence[ nPos++ ] = xCustomLabel;
+                }
+            }
+
+            aPropSet.setProperty( PROP_CustomLabelFields, makeAny( aSequence ) );
+        }
     }
     catch( Exception& )
     {
diff --git a/oox/source/token/properties.txt b/oox/source/token/properties.txt
index e6bc79ff3d08..1c700894a112 100644
--- a/oox/source/token/properties.txt
+++ b/oox/source/token/properties.txt
@@ -278,6 +278,7 @@ LabelBorderWidth
 LabelPlacement
 LabelPosition
 LabelSeparator
+CustomLabelFields
 LayoutInfo
 LeftBorder
 LeftBorderDistance
--
2.13.6


From 12415c5d82817b4d056d205b6afc940872b83682 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Szymon=20K=C5=82os?= <szymon.klos@collabora.com>
Date: Sat, 27 Jan 2018 20:29:38 +0100
Subject: [PATCH 2/2] tdf#114821 export complex data labels in charts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Change-Id: I9b0893dfde4efc10bb05e6e17b7128b016efeb71
Reviewed-on: https://gerrit.libreoffice.org/48788
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Szymon Kłos <szymon.klos@collabora.com>
---
 chart2/qa/extras/chart2export.cxx | 159 ++++++++++++++++++++++++++++++++++++++
 oox/source/export/chartexport.cxx | 107 ++++++++++++++++++++++++-
 2 files changed, 262 insertions(+), 4 deletions(-)

diff --git a/oox/source/export/chartexport.cxx b/oox/source/export/chartexport.cxx
index 128a166c6c4a..63375d6c2fca 100644
--- a/oox/source/export/chartexport.cxx
+++ b/oox/source/export/chartexport.cxx
@@ -60,6 +60,8 @@
 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
 #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
 #include <com/sun/star/chart2/DataPointLabel.hpp>
+#include <com/sun/star/chart2/DataPointCustomLabelField.hpp>
+#include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
 #include <com/sun/star/chart2/Symbol.hpp>
 #include <com/sun/star/chart2/data/XDataSource.hpp>
 #include <com/sun/star/chart2/data/XDataSink.hpp>
@@ -2866,17 +2868,111 @@ const char* toOOXMLPlacement( sal_Int32 nPlacement )
     return "outEnd";
 }

-void writeLabelProperties(
-    const FSHelperPtr& pFS, const uno::Reference<beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam )
+OUString getFieldTypeString( const chart2::DataPointCustomLabelFieldType aType )
+{
+    switch (aType)
+    {
+    case chart2::DataPointCustomLabelFieldType_CATEGORYNAME:
+        return OUString("CATEGORYNAME");
+
+    case chart2::DataPointCustomLabelFieldType_SERIESNAME:
+        return OUString("SERIESNAME");
+
+    case chart2::DataPointCustomLabelFieldType_VALUE:
+        return OUString("VALUE");
+
+    case chart2::DataPointCustomLabelFieldType_CELLREF:
+        return OUString("CELLREF");
+
+    default:
+        break;
+    }
+    return OUString();
+}
+
+void writeRunProperties( ChartExport* pChartExport, Reference<XPropertySet>& xPropertySet )
+{
+    bool bDummy = false;
+    sal_Int32 nDummy;
+    pChartExport->WriteRunProperties(xPropertySet, false, XML_rPr, true, bDummy, nDummy);
+}
+
+void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport,
+                       const Sequence<Reference<chart2::XDataPointCustomLabelField>>& rCustomLabelFields )
+{
+    pFS->startElement(FSNS(XML_c, XML_tx), FSEND);
+    pFS->startElement(FSNS(XML_c, XML_rich), FSEND);
+
+    // TODO: body properties?
+    pFS->singleElement(FSNS(XML_a, XML_bodyPr), FSEND);
+
+    OUString sFieldType;
+    bool bNewParagraph;
+    pFS->startElement(FSNS(XML_a, XML_p), FSEND);
+
+    for (auto& rField : rCustomLabelFields)
+    {
+        Reference<XPropertySet> xPropertySet(rField, UNO_QUERY);
+        chart2::DataPointCustomLabelFieldType aType = rField->getFieldType();
+        sFieldType.clear();
+        bNewParagraph = false;
+
+        if (aType == chart2::DataPointCustomLabelFieldType_NEWLINE)
+            bNewParagraph = true;
+        else if (aType != chart2::DataPointCustomLabelFieldType_TEXT)
+            sFieldType = getFieldTypeString(aType);
+
+        if (bNewParagraph)
+        {
+            pFS->endElement(FSNS(XML_a, XML_p));
+            pFS->startElement(FSNS(XML_a, XML_p), FSEND);
+            continue;
+        }
+
+        if (sFieldType.isEmpty())
+        {
+            // Normal text run
+            pFS->startElement(FSNS(XML_a, XML_r), FSEND);
+            writeRunProperties(pChartExport, xPropertySet);
+
+            pFS->startElement(FSNS(XML_a, XML_t), FSEND);
+            pFS->writeEscaped(rField->getString());
+            pFS->endElement(FSNS(XML_a, XML_t));
+
+            pFS->endElement(FSNS(XML_a, XML_r));
+        }
+        else
+        {
+            // Field
+            pFS->startElement(FSNS(XML_a, XML_fld), XML_id, USS(rField->getGuid()), XML_type, USS(sFieldType), FSEND);
+            writeRunProperties(pChartExport, xPropertySet);
+
+            pFS->startElement(FSNS(XML_a, XML_t), FSEND);
+            pFS->writeEscaped(rField->getString());
+            pFS->endElement(FSNS(XML_a, XML_t));
+
+            pFS->endElement(FSNS(XML_a, XML_fld));
+        }
+    }
+
+    pFS->endElement(FSNS(XML_a, XML_p));
+    pFS->endElement(FSNS(XML_c, XML_rich));
+    pFS->endElement(FSNS(XML_c, XML_tx));
+}
+
+void writeLabelProperties( const FSHelperPtr& pFS, ChartExport* pChartExport,
+    const uno::Reference<beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam )
 {
     if (!xPropSet.is())
         return;

     chart2::DataPointLabel aLabel;
+    Sequence<Reference<chart2::XDataPointCustomLabelField>> aCustomLabelFields;
     sal_Int32 nLabelBorderWidth = 0;
     sal_Int32 nLabelBorderColor = 0x00FFFFFF;

     xPropSet->getPropertyValue("Label") >>= aLabel;
+    xPropSet->getPropertyValue("CustomLabelFields") >>= aCustomLabelFields;
     xPropSet->getPropertyValue("LabelBorderWidth") >>= nLabelBorderWidth;
     xPropSet->getPropertyValue("LabelBorderColor") >>= nLabelBorderColor;

@@ -2897,6 +2993,9 @@ void writeLabelProperties(
         pFS->endElement(FSNS(XML_c, XML_spPr));
     }

+    if (aCustomLabelFields.getLength() > 0)
+        writeCustomLabel(pFS, pChartExport, aCustomLabelFields);
+
     if (rLabelParam.mbExport)
     {
         sal_Int32 nLabelPlacement = rLabelParam.meDefault;
@@ -2992,12 +3091,12 @@ void ChartExport::exportDataLabels(
         // Individual label property that overwrites the baseline.
         pFS->startElement(FSNS(XML_c, XML_dLbl), FSEND);
         pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, I32S(nIdx), FSEND);
-        writeLabelProperties(pFS, xLabelPropSet, aParam);
+        writeLabelProperties(pFS, this, xLabelPropSet, aParam);
         pFS->endElement(FSNS(XML_c, XML_dLbl));
     }

     // Baseline label properties for all labels.
-    writeLabelProperties(pFS, xPropSet, aParam);
+    writeLabelProperties(pFS, this, xPropSet, aParam);

     pFS->singleElement(FSNS(XML_c, XML_showLeaderLines),
             XML_val, "0",
--
2.13.6