[Libreoffice-commits] core.git: Branch 'distro/collabora/co-2021' - 3 commits - chart2/source download.lst include/oox include/xmloff offapi/com oox/inc oox/source schema/libreoffice svx/uiconfig xmloff/source

Michael Stahl (via logerrit) logerrit at kemper.freedesktop.org
Mon Aug 30 08:31:05 UTC 2021


 chart2/source/model/main/FormattedString.cxx                |   18 +
 chart2/source/model/main/FormattedString.hxx                |    3 
 chart2/source/view/charttypes/VSeriesPlotter.cxx            |   25 +
 download.lst                                                |    4 
 include/oox/export/chartexport.hxx                          |   26 +-
 include/xmloff/xmltoken.hxx                                 |    2 
 offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl   |   10 
 oox/inc/drawingml/chart/datasourcecontext.hxx               |    1 
 oox/inc/drawingml/chart/seriesmodel.hxx                     |   15 +
 oox/source/drawingml/chart/datasourcecontext.cxx            |   21 +
 oox/source/drawingml/chart/seriescontext.cxx                |   55 +++-
 oox/source/drawingml/chart/seriesconverter.cxx              |   51 +++-
 oox/source/drawingml/chart/seriesmodel.cxx                  |    4 
 oox/source/export/chartexport.cxx                           |  153 +++++++++++-
 oox/source/token/tokens.txt                                 |    3 
 schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng |   12 
 svx/uiconfig/ui/sidebarparagraph.ui                         |   18 -
 xmloff/source/chart/SchXMLExport.cxx                        |   78 +++++-
 xmloff/source/chart/SchXMLPlotAreaContext.cxx               |   21 +
 xmloff/source/chart/SchXMLPlotAreaContext.hxx               |    4 
 xmloff/source/chart/SchXMLSeries2Context.cxx                |   25 +
 xmloff/source/chart/transporttypes.hxx                      |   10 
 xmloff/source/core/xmltoken.cxx                             |    2 
 xmloff/source/token/tokens.txt                              |    2 
 24 files changed, 499 insertions(+), 64 deletions(-)

New commits:
commit 74324c80b33d20721285bd1e4afe244082ae468e
Author:     Michael Stahl <michael.stahl at allotropia.de>
AuthorDate: Wed Aug 25 11:16:46 2021 +0200
Commit:     Andras Timar <andras.timar at collabora.com>
CommitDate: Mon Aug 30 10:27:16 2021 +0200

    openssl: upgrade to release 1.1.1l
    
    Fixes CVE-2021-3712, CVE-2021-3711 (not obvious if any of them affect LO)
    
    Change-Id: I98652348977a5a3c728f1d4fdf7293b76a93b630
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121026
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.stahl at allotropia.de>
    (cherry picked from commit 5c391f4346e86bd5d7528fbb42a3af64f98a03d3)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120987
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>

diff --git a/download.lst b/download.lst
index a2a08016976e..82dc3e21626f 100644
--- a/download.lst
+++ b/download.lst
@@ -226,8 +226,8 @@ export OFFICEOTRON_SHA256SUM := f2443f27561af52324eee03a1892d9f569adc8db9e7bca55
 export OFFICEOTRON_JAR := 8249374c274932a21846fa7629c2aa9b-officeotron-0.7.4-master.jar
 export OPENLDAP_SHA256SUM := cdd6cffdebcd95161a73305ec13fc7a78e9707b46ca9f84fb897cd5626df3824
 export OPENLDAP_TARBALL := openldap-2.4.45.tgz
-export OPENSSL_SHA256SUM := 892a0875b9872acd04a9fde79b1f943075d5ea162415de3047c327df33fbaee5
-export OPENSSL_TARBALL := openssl-1.1.1k.tar.gz
+export OPENSSL_SHA256SUM := 0b7a3e5e59c34827fe0c3a74b7ec8baef302b98fa80088d7f9153aa16fa76bd1
+export OPENSSL_TARBALL := openssl-1.1.1l.tar.gz
 export ORCUS_SHA256SUM := c700d1325f744104d9fca0d5a019434901e9d51a16eedfb05792f90a298587a4
 export ORCUS_TARBALL := liborcus-0.16.1.tar.bz2
 export OWNCLOUD_ANDROID_LIB_SHA256SUM := b18b3e3ef7fae6a79b62f2bb43cc47a5346b6330f6a383dc4be34439aca5e9fb
commit 5b04f8fbd7e9e41c653734b03c9c930b5d75908b
Author:     Caolán McNamara <caolanm at redhat.com>
AuthorDate: Thu Aug 26 16:12:42 2021 +0100
Commit:     Andras Timar <andras.timar at collabora.com>
CommitDate: Mon Aug 30 10:27:10 2021 +0200

    tdf#139660 one of the group needs to be active
    
    in this case using ToolButtons instead of ToggleToolButtons will work ok
    
    Change-Id: I806a8fc409ae346dd6734be13d69c4b171e54c08
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121123
    Tested-by: Jenkins
    Reviewed-by: Adolfo Jayme Barrientos <fitojb at ubuntu.com>

diff --git a/svx/uiconfig/ui/sidebarparagraph.ui b/svx/uiconfig/ui/sidebarparagraph.ui
index 2db34a7d6ef3..1f34c35b1c67 100644
--- a/svx/uiconfig/ui/sidebarparagraph.ui
+++ b/svx/uiconfig/ui/sidebarparagraph.ui
@@ -55,7 +55,7 @@
                 <property name="show_arrow">False</property>
                 <property name="icon_size">2</property>
                 <child>
-                  <object class="GtkToggleToolButton" id=".uno:LeftPara">
+                  <object class="GtkToolButton" id=".uno:LeftPara">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="use_underline">True</property>
@@ -66,7 +66,7 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkToggleToolButton" id=".uno:CenterPara">
+                  <object class="GtkToolButton" id=".uno:CenterPara">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="use_underline">True</property>
@@ -77,7 +77,7 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkToggleToolButton" id=".uno:RightPara">
+                  <object class="GtkToolButton" id=".uno:RightPara">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="use_underline">True</property>
@@ -88,7 +88,7 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkToggleToolButton" id=".uno:JustifyPara">
+                  <object class="GtkToolButton" id=".uno:JustifyPara">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="use_underline">True</property>
@@ -115,7 +115,7 @@
                 <property name="show_arrow">False</property>
                 <property name="icon_size">2</property>
                 <child>
-                  <object class="GtkToggleToolButton" id=".uno:ParaLeftToRight">
+                  <object class="GtkToolButton" id=".uno:ParaLeftToRight">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="use_underline">True</property>
@@ -126,7 +126,7 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkToggleToolButton" id=".uno:ParaRightToLeft">
+                  <object class="GtkToolButton" id=".uno:ParaRightToLeft">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="use_underline">True</property>
@@ -152,7 +152,7 @@
                 <property name="show_arrow">False</property>
                 <property name="icon_size">2</property>
                 <child>
-                  <object class="GtkToggleToolButton" id=".uno:CellVertTop">
+                  <object class="GtkToolButton" id=".uno:CellVertTop">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="use_underline">True</property>
@@ -163,7 +163,7 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkToggleToolButton" id=".uno:CellVertCenter">
+                  <object class="GtkToolButton" id=".uno:CellVertCenter">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="use_underline">True</property>
@@ -174,7 +174,7 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkToggleToolButton" id=".uno:CellVertBottom">
+                  <object class="GtkToolButton" id=".uno:CellVertBottom">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="use_underline">True</property>
commit 2ca9ba73573a1b579b1711ce72dc5859d2dfea52
Author:     Dennis Francis <dennis.francis at collabora.com>
AuthorDate: Wed Aug 25 20:51:56 2021 +0530
Commit:     Andras Timar <andras.timar at collabora.com>
CommitDate: Mon Aug 30 10:26:40 2021 +0200

    tdf#143942: oox: import/export labels from <c15:datalabelsRange>
    
    When <c15:showDataLabelsRange> boolean flag is present, the imported
    label texts are added as the first text field in oox data label model.
    The cell-range associated is also preserved. The export part preserves
    the how labels were store originally in <c15:datalabelsRange>.
    
    However in order to make the custom labels reflect the contents of the
    cells in the associated cell-range, more work needs to be done. For this
    the labels present in <c15:datalabelsRange> needs to be made available
    as a data-sequence with a new "role" like "point-labels" in
    XInternalDataProvider implementation and and make the label renderer
    read this data source rather than consulting the custom label fields
    property which is static after import.
    
    Change-Id: Ibc7045fa5ea209d463680c96efb49a06662d2500

diff --git a/chart2/source/model/main/FormattedString.cxx b/chart2/source/model/main/FormattedString.cxx
index 0d6e733a2c31..55960c081336 100644
--- a/chart2/source/model/main/FormattedString.cxx
+++ b/chart2/source/model/main/FormattedString.cxx
@@ -100,6 +100,7 @@ FormattedString::FormattedString() :
     m_aString(),
     m_aType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT),
     m_aGuid(),
+    m_bDataLabelsRange(false),
     m_xModifyEventForwarder( ModifyListenerHelper::createModifyEventForwarder())
 {}
 
@@ -109,6 +110,7 @@ FormattedString::FormattedString( const FormattedString & rOther ) :
     m_aString( rOther.m_aString ),
     m_aType(rOther.m_aType),
     m_aGuid(rOther.m_aGuid),
+    m_bDataLabelsRange(rOther.m_bDataLabelsRange),
     m_xModifyEventForwarder( ModifyListenerHelper::createModifyEventForwarder())
 {}
 
@@ -174,6 +176,22 @@ void SAL_CALL FormattedString::setGuid( const OUString& guid )
 
 }
 
+sal_Bool SAL_CALL FormattedString::getDataLabelsRange()
+{
+    MutexGuard aGuard( m_aMutex);
+    return m_bDataLabelsRange;
+}
+
+void SAL_CALL FormattedString::setDataLabelsRange( sal_Bool dataLabelsRange )
+{
+    {
+        MutexGuard aGuard( m_aMutex);
+        m_bDataLabelsRange = dataLabelsRange;
+    }
+    //don't keep the mutex locked while calling out
+    fireModifyEvent();
+}
+
 // ____ XModifyBroadcaster ____
 void SAL_CALL FormattedString::addModifyListener( const uno::Reference< util::XModifyListener >& aListener )
 {
diff --git a/chart2/source/model/main/FormattedString.hxx b/chart2/source/model/main/FormattedString.hxx
index a7f9e36bb134..763c929d6589 100644
--- a/chart2/source/model/main/FormattedString.hxx
+++ b/chart2/source/model/main/FormattedString.hxx
@@ -87,6 +87,8 @@ private:
         setFieldType( const css::chart2::DataPointCustomLabelFieldType FieldType ) override;
     virtual OUString SAL_CALL getGuid() override;
     void SAL_CALL setGuid( const OUString& guid ) override;
+    virtual sal_Bool SAL_CALL getDataLabelsRange() override;
+    virtual void SAL_CALL setDataLabelsRange( sal_Bool dataLabelsRange ) override;
 
     // ____ OPropertySet ____
     virtual css::uno::Any GetDefaultValue( sal_Int32 nHandle ) const override;
@@ -127,6 +129,7 @@ private:
     // ____ XDataPointCustomLabelField ____
     css::chart2::DataPointCustomLabelFieldType m_aType;
     OUString m_aGuid;
+    bool m_bDataLabelsRange;
 
     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 f405396e113c..6f2b3f820ff5 100644
--- a/chart2/source/view/charttypes/VSeriesPlotter.cxx
+++ b/chart2/source/view/charttypes/VSeriesPlotter.cxx
@@ -502,6 +502,7 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
         Sequence< OUString > aTextList( nTextListLength );
 
         bool bUseCustomLabel = nCustomLabelsCount > 0;
+        size_t nVisibleFieldLen = 0;
         if( bUseCustomLabel )
         {
             nTextListLength = ( nCustomLabelsCount > 3 ) ? nCustomLabelsCount : 3;
@@ -509,6 +510,8 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
             aTextList = Sequence< OUString >( nTextListLength );
             for( sal_uInt32 i = 0; i < nCustomLabelsCount; ++i )
             {
+                ++nVisibleFieldLen;
+                bool bPreserveContent = false;
                 switch( aCustomLabels[i]->getFieldType() )
                 {
                     case DataPointCustomLabelFieldType_VALUE:
@@ -541,8 +544,16 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
                         aTextList[i] = getLabelTextForValue(rDataSeries, nPointIndex, fValue, true);
                         break;
                     }
-                    case DataPointCustomLabelFieldType_CELLREF:
                     case DataPointCustomLabelFieldType_CELLRANGE:
+                    {
+                        if (aCustomLabels[i]->getDataLabelsRange())
+                        {
+                            bPreserveContent = true;
+                            --nVisibleFieldLen;
+                        }
+                    }
+                    [[fallthrough]];
+                    case DataPointCustomLabelFieldType_CELLREF:
                     {
                         // TODO: for now doesn't show placeholder
                         aTextList[i] = OUString();
@@ -561,7 +572,8 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
                     default:
                     break;
                 }
-                aCustomLabels[i]->setString( aTextList[i] );
+                if (!bPreserveContent)
+                    aCustomLabels[i]->setString( aTextList[i] );
             }
         }
         else
@@ -613,10 +625,13 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re
 
         if( bUseCustomLabel )
         {
-            Sequence< uno::Reference< XFormattedString > > aFormattedLabels( aCustomLabels.getLength() );
-            for( int i = 0; i < aFormattedLabels.getLength(); i++ )
+            Sequence< uno::Reference< XFormattedString > > aFormattedLabels( nVisibleFieldLen );
+            int nFormattedIndex = 0;
+            for( int i = 0; i < aCustomLabels.getLength(); i++ )
             {
-                aFormattedLabels[i] = aCustomLabels[i];
+                if (aCustomLabels[i]->getFieldType() == DataPointCustomLabelFieldType_CELLRANGE && aCustomLabels[i]->getDataLabelsRange())
+                    continue;
+                aFormattedLabels[nFormattedIndex++] = aCustomLabels[i];
             }
 
             // create text shape
diff --git a/include/oox/export/chartexport.hxx b/include/oox/export/chartexport.hxx
index b900c68b51a2..3771809c9d87 100644
--- a/include/oox/export/chartexport.hxx
+++ b/include/oox/export/chartexport.hxx
@@ -90,6 +90,29 @@ struct AxisIdPair{
     {}
 };
 
+class DataLabelsRange
+{
+public:
+
+    typedef std::map<sal_Int32, OUString> LabelsRangeMap;
+
+    bool empty() const;
+    size_t count() const;
+    bool hasLabel(sal_Int32 nIndex) const;
+    OUString getRange() const;
+
+    void setRange(const OUString& rRange);
+    void setLabel(sal_Int32 nIndex, const OUString& rText);
+
+    LabelsRangeMap::const_iterator begin() const;
+    LabelsRangeMap::const_iterator end() const;
+
+private:
+    OUString         maRange;
+    LabelsRangeMap   maLabels;
+};
+
+
 class OOX_DLLPUBLIC ChartExport final : public DrawingML {
 
 public:
@@ -182,7 +205,8 @@ private:
     void exportDataPoints(
         const css::uno::Reference< css::beans::XPropertySet >& xSeriesProperties,
         sal_Int32 nSeriesLength, sal_Int32 eChartType );
-    void exportDataLabels( const css::uno::Reference<css::chart2::XDataSeries>& xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType );
+    void exportDataLabels( const css::uno::Reference<css::chart2::XDataSeries>& xSeries, sal_Int32 nSeriesLength,
+        sal_Int32 eChartType, DataLabelsRange& rDLblsRange );
     void exportGrouping( bool isBar = false );
     void exportTrendlines( const css::uno::Reference< css::chart2::XDataSeries >& xSeries );
     void exportMarker( const css::uno::Reference< css::beans::XPropertySet >& xPropSet );
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index 1b145de4854a..1f4739c770e9 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -574,9 +574,11 @@ namespace xmloff::token {
         XML_DATA_BAR_ENTRY,
         XML_DATA_CELL_RANGE_ADDRESS,
         XML_DATA_LABEL,
+        XML_DATA_LABEL_GUID,
         XML_DATA_LABEL_NUMBER,
         XML_DATA_LABEL_SYMBOL,
         XML_DATA_LABEL_TEXT,
+        XML_DATA_LABELS_CELL_RANGE,
         XML_DATA_PILOT_SOURCE,
         XML_DATA_PILOT_FIELD,
         XML_DATA_PILOT_GRAND_TOTAL,
diff --git a/offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl b/offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl
index a6a1b0151c94..376e4ad751d2 100644
--- a/offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl
+++ b/offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl
@@ -29,6 +29,16 @@ interface XDataPointCustomLabelField : XFormattedString2
 
     void setGuid( [in] string guid );
 
+    /**
+        @since LibreOffice 7.3
+    */
+    boolean getDataLabelsRange();
+
+    /**
+        @since LibreOffice 7.3
+    */
+    void setDataLabelsRange( [in] boolean dataLabelsRange );
+
 };
 
 
diff --git a/oox/inc/drawingml/chart/datasourcecontext.hxx b/oox/inc/drawingml/chart/datasourcecontext.hxx
index 8c893c1973a8..c90122282a2f 100644
--- a/oox/inc/drawingml/chart/datasourcecontext.hxx
+++ b/oox/inc/drawingml/chart/datasourcecontext.hxx
@@ -67,6 +67,7 @@ public:
 
 private:
     sal_Int32           mnPtIndex;          /// Current data point index.
+    bool                mbReadC15;          /// Allow reading extension tags data under c15 namespace.
 };
 
 
diff --git a/oox/inc/drawingml/chart/seriesmodel.hxx b/oox/inc/drawingml/chart/seriesmodel.hxx
index c4fb557d282d..460293a5feee 100644
--- a/oox/inc/drawingml/chart/seriesmodel.hxx
+++ b/oox/inc/drawingml/chart/seriesmodel.hxx
@@ -41,12 +41,18 @@ struct DataLabelModelBase
     OptValue< bool >    mobShowPercent;     /// True = show percentual value in pie/doughnut charts.
     OptValue< bool >    mobShowSerName;     /// True = show series name.
     OptValue< bool >    mobShowVal;         /// True = show data point value.
+
+    /// True = the value from the <c15:datalabelsRange> corresponding to the
+    /// index of this label is used as the label text.
+    OptValue< bool >    mobShowDataLabelsRange;
     bool                mbDeleted;          /// True = data label(s) deleted.
 
     explicit            DataLabelModelBase(bool bMSO2007Doc);
                         ~DataLabelModelBase();
 };
 
+struct DataLabelsModel;
+
 struct DataLabelModel : public DataLabelModelBase
 {
     typedef ModelRef< LayoutModel > LayoutRef;
@@ -54,9 +60,10 @@ struct DataLabelModel : public DataLabelModelBase
 
     LayoutRef           mxLayout;           /// Layout/position of the data point label frame.
     TextRef             mxText;             /// Manual or linked text for this data point label.
+    const DataLabelsModel&    mrParent;     /// Reference to the labels container.
     sal_Int32           mnIndex;            /// Data point index for this data label.
 
-    explicit            DataLabelModel(bool bMSO2007Doc);
+    explicit            DataLabelModel(const DataLabelsModel& rParent, bool bMSO2007Doc);
                         ~DataLabelModel();
 };
 
@@ -67,6 +74,9 @@ struct DataLabelsModel : public DataLabelModelBase
 
     DataLabelVector     maPointLabels;      /// Settings for individual data point labels.
     ShapeRef            mxLeaderLines;      /// Formatting of connector lines between data points and labels.
+
+    /// Labels source (owned by SeriesModel's DataSourceMap)
+    const DataSourceModel*  mpLabelsSource;
     bool                mbShowLeaderLines;  /// True = show connector lines between data points and labels.
 
     explicit            DataLabelsModel(bool bMSO2007Doc);
@@ -171,7 +181,8 @@ struct SeriesModel
     {
         CATEGORIES,         /// Data point categories.
         VALUES,             /// Data point values.
-        POINTS              /// Data point size (e.g. bubble size in bubble charts).
+        POINTS,             /// Data point size (e.g. bubble size in bubble charts).
+        DATALABELS,         /// Data point labels.
     };
 
     typedef ModelMap< SourceType, DataSourceModel > DataSourceMap;
diff --git a/oox/source/drawingml/chart/datasourcecontext.cxx b/oox/source/drawingml/chart/datasourcecontext.cxx
index e9f2f3b189f6..15ca02975137 100644
--- a/oox/source/drawingml/chart/datasourcecontext.cxx
+++ b/oox/source/drawingml/chart/datasourcecontext.cxx
@@ -165,6 +165,7 @@ SvNumberFormatter* DoubleSequenceContext::getNumberFormatter()
 StringSequenceContext::StringSequenceContext( ContextHandler2Helper& rParent, DataSequenceModel& rModel )
     : DataSequenceContextBase( rParent, rModel )
     , mnPtIndex(-1)
+    , mbReadC15(false)
 {
 }
 
@@ -185,6 +186,16 @@ ContextHandlerRef StringSequenceContext::onCreateContext( sal_Int32 nElement, co
             }
         break;
 
+        case C15_TOKEN( datalabelsRange ):
+            mbReadC15 = true;
+            switch( nElement )
+            {
+                case C15_TOKEN( f ):
+                case C15_TOKEN( dlblRangeCache ):
+                    return this;
+            }
+        break;
+
         case C_TOKEN( strRef ):
             switch( nElement )
             {
@@ -196,6 +207,10 @@ ContextHandlerRef StringSequenceContext::onCreateContext( sal_Int32 nElement, co
 
         case C_TOKEN( strCache ):
         case C_TOKEN( strLit ):
+        case C15_TOKEN( dlblRangeCache ):
+            if (nElement == C15_TOKEN( dlblRangeCache ) && !mbReadC15)
+                break;
+
             switch( nElement )
             {
                 case C_TOKEN( ptCount ):
@@ -247,6 +262,10 @@ void StringSequenceContext::onCharacters( const OUString& rChars )
         case C_TOKEN( f ):
             mrModel.maFormula = rChars;
         break;
+        case C15_TOKEN( f ):
+            if (mbReadC15)
+                mrModel.maFormula = rChars;
+        break;
         case C_TOKEN( v ):
             if( mnPtIndex >= 0 )
                 mrModel.maData[ (mrModel.mnLevelCount-1) * mrModel.mnPointCount + mnPtIndex ] <<= rChars;
@@ -269,11 +288,13 @@ ContextHandlerRef DataSourceContext::onCreateContext( sal_Int32 nElement, const
     {
         case C_TOKEN( cat ):
         case C_TOKEN( xVal ):
+        case C_TOKEN( ext ):
             switch( nElement )
             {
                 case C_TOKEN( multiLvlStrRef ):
                 case C_TOKEN( strLit ):
                 case C_TOKEN( strRef ):
+                case C15_TOKEN( datalabelsRange ):
                     OSL_ENSURE( !mrModel.mxDataSeq, "DataSourceContext::onCreateContext - multiple data sequences" );
                     return new StringSequenceContext( *this, mrModel.mxDataSeq.create() );
 
diff --git a/oox/source/drawingml/chart/seriescontext.cxx b/oox/source/drawingml/chart/seriescontext.cxx
index 3f92818c5544..5afc32c1497d 100644
--- a/oox/source/drawingml/chart/seriescontext.cxx
+++ b/oox/source/drawingml/chart/seriescontext.cxx
@@ -100,15 +100,37 @@ DataLabelContext::~DataLabelContext()
 
 ContextHandlerRef DataLabelContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
 {
-    if( isRootElement() ) switch( nElement )
+    if( isRootElement() )
     {
-        case C_TOKEN( idx ):
-            mrModel.mnIndex = rAttribs.getInteger( XML_val, -1 );
-            return nullptr;
-        case C_TOKEN( layout ):
-            return new LayoutContext( *this, mrModel.mxLayout.create() );
-        case C_TOKEN( tx ):
-            return new TextContext( *this, mrModel.mxText.create() );
+        switch( nElement )
+        {
+            case C_TOKEN( idx ):
+                mrModel.mnIndex = rAttribs.getInteger( XML_val, -1 );
+                return nullptr;
+            case C_TOKEN( layout ):
+                return new LayoutContext( *this, mrModel.mxLayout.create() );
+            case C_TOKEN( tx ):
+                return new TextContext( *this, mrModel.mxText.create() );
+            case C_TOKEN( extLst ):
+                return this;
+        }
+    }
+    else
+    {
+        switch( getCurrentElement() )
+        {
+            case C_TOKEN( extLst ):
+                if ( nElement == C_TOKEN( ext ) )
+                    return this;
+            break;
+            case C_TOKEN( ext ):
+                if ( nElement == C15_TOKEN( showDataLabelsRange ) )
+                {
+                    mrModel.mobShowDataLabelsRange = rAttribs.getBool( XML_val );
+                    return nullptr;
+                }
+            break;
+        }
     }
     bool bMSO2007 = getFilter().isMSO2007Document();
     return lclDataLabelSharedCreateContext( *this, nElement, rAttribs, mrModel, bMSO2007 );
@@ -135,7 +157,7 @@ ContextHandlerRef DataLabelsContext::onCreateContext( sal_Int32 nElement, const
     if( isRootElement() ) switch( nElement )
     {
         case C_TOKEN( dLbl ):
-            return new DataLabelContext( *this, mrModel.maPointLabels.create(bMSO2007Doc) );
+            return new DataLabelContext( *this, mrModel.maPointLabels.create(mrModel, bMSO2007Doc) );
         case C_TOKEN( leaderLines ):
             return new ShapePrWrapperContext( *this, mrModel.mxLeaderLines.create() );
         case C_TOKEN( showLeaderLines ):
@@ -390,6 +412,8 @@ ContextHandlerRef SeriesContextBase::onCreateContext( sal_Int32 nElement, const
                     return new ShapePropertiesContext( *this, mrModel.mxShapeProp.create() );
                 case C_TOKEN( tx ):
                     return new TextContext( *this, mrModel.mxText.create() );
+                case C_TOKEN( extLst ):
+                    return this;
             }
         break;
 
@@ -406,6 +430,19 @@ ContextHandlerRef SeriesContextBase::onCreateContext( sal_Int32 nElement, const
                     return nullptr;
             }
         break;
+
+        case C_TOKEN( extLst ):
+            switch( nElement )
+            {
+                case C_TOKEN( ext ):
+                    if (mrModel.maSources.has( SeriesModel::DATALABELS ))
+                        break;
+
+                    DataSourceModel& rLabelsSource = mrModel.maSources.create( SeriesModel::DATALABELS );
+                    if (mrModel.mxLabels.is())
+                        mrModel.mxLabels->mpLabelsSource = &rLabelsSource;
+                    return new DataSourceContext( *this, rLabelsSource );
+            }
     }
     return nullptr;
 }
diff --git a/oox/source/drawingml/chart/seriesconverter.cxx b/oox/source/drawingml/chart/seriesconverter.cxx
index 2de8ee82d2f9..6f57cc8e73e4 100644
--- a/oox/source/drawingml/chart/seriesconverter.cxx
+++ b/oox/source/drawingml/chart/seriesconverter.cxx
@@ -318,9 +318,47 @@ void DataLabelConverter::convertFromModel( const Reference< XDataSeries >& rxDat
             if( nParagraphs > 1 )
                 nSequenceSize += nParagraphs - 1;
 
+            OptValue< OUString > oaLabelText;
+            OptValue< OUString > oaCellRange;
+            if (mrModel.mobShowDataLabelsRange.get(false))
+            {
+                const DataSourceModel* pLabelSource = mrModel.mrParent.mpLabelsSource;
+                if (pLabelSource && pLabelSource->mxDataSeq.is())
+                {
+                    oaCellRange = pLabelSource->mxDataSeq->maFormula;
+                    const auto& rLabelMap = pLabelSource->mxDataSeq->maData;
+                    const auto& rKV = rLabelMap.find(mrModel.mnIndex);
+                    if (rKV != rLabelMap.end())
+                        rKV->second >>= oaLabelText.use();
+                }
+            }
+
+            if (oaLabelText.has())
+                ++nSequenceSize;
+
             aSequence.realloc( nSequenceSize );
 
             int nPos = 0;
+            if (oaLabelText.has())
+            {
+                // Insert a "text" custom label field with the text found in the labels data source.
+                css::uno::Reference< XDataPointCustomLabelField > xCustomLabelText = DataPointCustomLabelField::create( xContext );
+
+                // Store properties
+                oox::PropertySet aPropertySet( xCustomLabelText );
+                convertTextProperty( aPropertySet, getFormatter(), mrModel.mxText->mxTextBody );
+                if (nParagraphs && rParagraphs[0])
+                {
+                    auto& pRuns = rParagraphs[0]->getRuns();
+                    if (pRuns.size())
+                        pRuns[0]->getTextCharacterProperties().pushToPropSet( aPropertySet, getFilter() );
+                }
+                xCustomLabelText->setString(oaLabelText.get());
+                xCustomLabelText->setFieldType( DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT );
+
+                aSequence[ nPos++ ] = xCustomLabelText;
+            }
+
             for( auto& pParagraph : rParagraphs )
             {
                 for( auto& pRun : pParagraph->getRuns() )
@@ -335,8 +373,17 @@ void DataLabelConverter::convertFromModel( const Reference< XDataSeries >& rxDat
                     TextField* pField = nullptr;
                     if( ( pField = dynamic_cast< TextField* >( pRun.get() ) ) )
                     {
-                        xCustomLabel->setString( pField->getText() );
-                        xCustomLabel->setFieldType( lcl_ConvertFieldNameToFieldEnum( pField->getType() ) );
+                        DataPointCustomLabelFieldType eType = lcl_ConvertFieldNameToFieldEnum( pField->getType() );
+
+                        if (eType == DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLRANGE && oaCellRange.has())
+                        {
+                            xCustomLabel->setString( oaCellRange.get() );
+                            xCustomLabel->setDataLabelsRange( true );
+                        }
+                        else
+                            xCustomLabel->setString( pField->getText() );
+
+                        xCustomLabel->setFieldType( eType );
                         xCustomLabel->setGuid( pField->getUuid() );
                     }
                     else if( pRun )
diff --git a/oox/source/drawingml/chart/seriesmodel.cxx b/oox/source/drawingml/chart/seriesmodel.cxx
index 563e0e48e3bf..4ad9b2593af4 100644
--- a/oox/source/drawingml/chart/seriesmodel.cxx
+++ b/oox/source/drawingml/chart/seriesmodel.cxx
@@ -31,8 +31,9 @@ DataLabelModelBase::~DataLabelModelBase()
 {
 }
 
-DataLabelModel::DataLabelModel(bool bMSO2007Doc) :
+DataLabelModel::DataLabelModel(const DataLabelsModel& rParent, bool bMSO2007Doc) :
     DataLabelModelBase(bMSO2007Doc),
+    mrParent( rParent ),
     mnIndex( -1 )
 {
 }
@@ -43,6 +44,7 @@ DataLabelModel::~DataLabelModel()
 
 DataLabelsModel::DataLabelsModel(bool bMSO2007Doc) :
     DataLabelModelBase(bMSO2007Doc),
+    mpLabelsSource( nullptr ),
     mbShowLeaderLines( !bMSO2007Doc )
 {
 }
diff --git a/oox/source/export/chartexport.cxx b/oox/source/export/chartexport.cxx
index 38295504105b..18fbfad9e7a9 100644
--- a/oox/source/export/chartexport.cxx
+++ b/oox/source/export/chartexport.cxx
@@ -485,6 +485,46 @@ static sal_Int32 lcl_getAlphaFromTransparenceGradient(const awt::Gradient& rGrad
     return (255 - nRed) * oox::drawingml::MAX_PERCENT / 255;
 }
 
+bool DataLabelsRange::empty() const
+{
+    return maLabels.empty();
+}
+
+size_t DataLabelsRange::count() const
+{
+    return maLabels.size();
+}
+
+bool DataLabelsRange::hasLabel(sal_Int32 nIndex) const
+{
+    return maLabels.find(nIndex) != maLabels.end();
+}
+
+OUString DataLabelsRange::getRange() const
+{
+    return maRange;
+}
+
+void DataLabelsRange::setRange(const OUString& rRange)
+{
+    maRange = rRange;
+}
+
+void DataLabelsRange::setLabel(sal_Int32 nIndex, const OUString& rText)
+{
+    maLabels.emplace(nIndex, rText);
+}
+
+DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::begin() const
+{
+    return maLabels.begin();
+}
+
+DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::end() const
+{
+    return maLabels.end();
+}
+
 ChartExport::ChartExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, Reference< frame::XModel > const & xModel, XmlFilterBase* pFB, DocumentType eDocumentType )
     : DrawingML( std::move(pFS), pFB, eDocumentType )
     , mnXmlNamespace( nXmlNamespace )
@@ -2047,6 +2087,40 @@ std::vector<Sequence<Reference<chart2::XDataSeries> > > splitDataSeriesByAxis(co
     return aSplitSeries;
 }
 
+void writeDataLabelsRange(FSHelperPtr& pFS, XmlFilterBase* pFB, DataLabelsRange& rDLblsRange)
+{
+    if (rDLblsRange.empty())
+        return;
+
+    pFS->startElement(FSNS(XML_c, XML_extLst));
+    pFS->startElement(FSNS(XML_c, XML_ext), XML_uri, "{02D57815-91ED-43cb-92C2-25804820EDAC}", FSNS(XML_xmlns, XML_c15), pFB->getNamespaceURL(OOX_NS(c15)));
+    // pFS->singleElement(FSNS(XML_c15, XML_showLeaderLines), XML_val, ToPsz10(bShowLeaderLines));
+    pFS->startElement(FSNS(XML_c15, XML_datalabelsRange));
+
+    // Write cell range.
+    pFS->startElement(FSNS(XML_c15, XML_f));
+    pFS->writeEscaped(rDLblsRange.getRange());
+    pFS->endElement(FSNS(XML_c15, XML_f));
+
+    // Write all labels.
+    pFS->startElement(FSNS(XML_c15, XML_dlblRangeCache));
+    pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(rDLblsRange.count()));
+    for (const auto& rLabelKV: rDLblsRange)
+    {
+        pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(rLabelKV.first));
+        pFS->startElement(FSNS(XML_c, XML_v));
+        pFS->writeEscaped(rLabelKV.second);
+        pFS->endElement(FSNS( XML_c, XML_v ));
+        pFS->endElement(FSNS(XML_c, XML_pt));
+    }
+
+    pFS->endElement(FSNS(XML_c15, XML_dlblRangeCache));
+
+    pFS->endElement(FSNS(XML_c15, XML_datalabelsRange));
+    pFS->endElement(FSNS(XML_c, XML_ext));
+    pFS->endElement(FSNS(XML_c, XML_extLst));
+}
+
 }
 
 void ChartExport::exportLineChart( const Reference< chart2::XChartType >& xChartType )
@@ -2431,8 +2505,9 @@ void ChartExport::exportSeries( const Reference<chart2::XChartType>& xChartType,
                     // export data points
                     exportDataPoints( uno::Reference< beans::XPropertySet >( rSeries, uno::UNO_QUERY ), nSeriesLength, eChartType );
 
+                    DataLabelsRange aDLblsRange;
                     // export data labels
-                    exportDataLabels(rSeries, nSeriesLength, eChartType);
+                    exportDataLabels(rSeries, nSeriesLength, eChartType, aDLblsRange);
 
                     exportTrendlines( rSeries );
 
@@ -2505,6 +2580,9 @@ void ChartExport::exportSeries( const Reference<chart2::XChartType>& xChartType,
                     if( eChartType == chart::TYPEID_BUBBLE )
                         pFS->singleElement(FSNS(XML_c, XML_bubble3D), XML_val, "0");
 
+                    if (!aDLblsRange.empty())
+                        writeDataLabelsRange(pFS, GetFB(), aDLblsRange);
+
                     pFS->endElement( FSNS( XML_c, XML_ser ) );
                 }
             }
@@ -3421,7 +3499,8 @@ void writeRunProperties( ChartExport* pChartExport, Reference<XPropertySet> cons
 }
 
 void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport,
-                       const Sequence<Reference<chart2::XDataPointCustomLabelField>>& rCustomLabelFields )
+                       const Sequence<Reference<chart2::XDataPointCustomLabelField>>& rCustomLabelFields,
+                       sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange )
 {
     pFS->startElement(FSNS(XML_c, XML_tx));
     pFS->startElement(FSNS(XML_c, XML_rich));
@@ -3430,15 +3509,54 @@ void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport,
     pFS->singleElement(FSNS(XML_a, XML_bodyPr));
 
     OUString sFieldType;
+    OUString sContent;
+    OUString sUid;
+    bool bShowDataLabelsRange = false;
     pFS->startElement(FSNS(XML_a, XML_p));
 
+    for (auto& rField : rCustomLabelFields)
+    {
+        if (rField->getFieldType() == chart2::DataPointCustomLabelFieldType_CELLRANGE &&
+            rField->getDataLabelsRange())
+            bShowDataLabelsRange = true;
+    }
+
     for (auto& rField : rCustomLabelFields)
     {
         Reference<XPropertySet> xPropertySet(rField, UNO_QUERY);
         chart2::DataPointCustomLabelFieldType aType = rField->getFieldType();
         sFieldType.clear();
+        sContent.clear();
+        sUid.clear();
         bool bNewParagraph = false;
 
+        if (bShowDataLabelsRange)
+        {
+            if (aType == chart2::DataPointCustomLabelFieldType_TEXT)
+            {
+                if (rDLblsRange.hasLabel(nLabelIndex))
+                    continue;
+
+                rDLblsRange.setLabel(nLabelIndex, rField->getString());
+                // Don't write the text field here.
+                // This is written under the extension root <c15:datalabelsRange>
+                continue;
+            }
+
+            if (aType == chart2::DataPointCustomLabelFieldType_CELLRANGE)
+            {
+                if (rDLblsRange.getRange().isEmpty())
+                    rDLblsRange.setRange(rField->getString());
+                sContent = "[CELLRANGE]";
+            }
+        }
+        else
+        {
+            sContent = rField->getString();
+        }
+
+        sUid = rField->getGuid();
+
         if (aType == chart2::DataPointCustomLabelFieldType_NEWLINE)
             bNewParagraph = true;
         else if (aType != chart2::DataPointCustomLabelFieldType_TEXT)
@@ -3458,7 +3576,7 @@ void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport,
             writeRunProperties(pChartExport, xPropertySet);
 
             pFS->startElement(FSNS(XML_a, XML_t));
-            pFS->writeEscaped(rField->getString());
+            pFS->writeEscaped(sContent);
             pFS->endElement(FSNS(XML_a, XML_t));
 
             pFS->endElement(FSNS(XML_a, XML_r));
@@ -3466,12 +3584,12 @@ void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport,
         else
         {
             // Field
-            pFS->startElement(FSNS(XML_a, XML_fld), XML_id, rField->getGuid(), XML_type,
+            pFS->startElement(FSNS(XML_a, XML_fld), XML_id, sUid, XML_type,
                               sFieldType);
             writeRunProperties(pChartExport, xPropertySet);
 
             pFS->startElement(FSNS(XML_a, XML_t));
-            pFS->writeEscaped(rField->getString());
+            pFS->writeEscaped(sContent);
             pFS->endElement(FSNS(XML_a, XML_t));
 
             pFS->endElement(FSNS(XML_a, XML_fld));
@@ -3484,7 +3602,8 @@ void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport,
 }
 
 void writeLabelProperties( const FSHelperPtr& pFS, ChartExport* pChartExport,
-    const uno::Reference<beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam )
+    const uno::Reference<beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam,
+    sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange )
 {
     if (!xPropSet.is())
         return;
@@ -3539,7 +3658,7 @@ void writeLabelProperties( const FSHelperPtr& pFS, ChartExport* pChartExport,
     pChartExport->exportTextProps(xPropSet);
 
     if (aCustomLabelFields.hasElements())
-        writeCustomLabel(pFS, pChartExport, aCustomLabelFields);
+        writeCustomLabel(pFS, pChartExport, aCustomLabelFields, nLabelIndex, rDLblsRange);
 
     if (rLabelParam.mbExport)
     {
@@ -3568,12 +3687,26 @@ void writeLabelProperties( const FSHelperPtr& pFS, ChartExport* pChartExport,
         pFS->writeEscaped( nLabelSeparator );
         pFS->endElement( FSNS( XML_c, XML_separator ) );
     }
+
+    if (rDLblsRange.hasLabel(nLabelIndex))
+    {
+        pFS->startElement(FSNS(XML_c, XML_extLst));
+        pFS->startElement(FSNS(XML_c, XML_ext), XML_uri,
+            "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}", FSNS(XML_xmlns, XML_c15),
+            pChartExport->GetFB()->getNamespaceURL(OOX_NS(c15)));
+
+        pFS->singleElement(FSNS(XML_c15, XML_showDataLabelsRange), XML_val, "1");
+
+        pFS->endElement(FSNS(XML_c, XML_ext));
+        pFS->endElement(FSNS(XML_c, XML_extLst));
+    }
 }
 
 }
 
 void ChartExport::exportDataLabels(
-    const uno::Reference<chart2::XDataSeries> & xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType )
+    const uno::Reference<chart2::XDataSeries> & xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType,
+    DataLabelsRange& rDLblsRange)
 {
     if (!xSeries.is() || nSeriesLength <= 0)
         return;
@@ -3704,12 +3837,12 @@ void ChartExport::exportDataLabels(
         }
 
         // Individual label property that overwrites the baseline.
-        writeLabelProperties(pFS, this, xLabelPropSet, aParam);
+        writeLabelProperties(pFS, this, xLabelPropSet, aParam, nIdx, rDLblsRange);
         pFS->endElement(FSNS(XML_c, XML_dLbl));
     }
 
     // Baseline label properties for all labels.
-    writeLabelProperties(pFS, this, xPropSet, aParam);
+    writeLabelProperties(pFS, this, xPropSet, aParam, -1, rDLblsRange);
 
     bool bShowLeaderLines = false;
     xPropSet->getPropertyValue("ShowCustomLeaderLines") >>= bShowLeaderLines;
diff --git a/oox/source/token/tokens.txt b/oox/source/token/tokens.txt
index 76dad54c8904..3bd4106b9c44 100644
--- a/oox/source/token/tokens.txt
+++ b/oox/source/token/tokens.txt
@@ -1656,6 +1656,7 @@ dataValidation
 dataValidations
 database
 databaseField
+datalabelsRange
 datastoreItem
 date
 date1904
@@ -1871,6 +1872,7 @@ dkTurquoise
 dkUpDiag
 dkVert
 dkViolet
+dlblRangeCache
 dllVersion
 dm
 dn
@@ -4645,6 +4647,7 @@ showComments
 showDLblsOverMax
 showDataAs
 showDataDropDown
+showDataLabelsRange
 showDataTips
 showDrill
 showDropDown
diff --git a/schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng b/schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng
index 89913cbf7e63..19653c45cd35 100644
--- a/schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng
+++ b/schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng
@@ -2493,4 +2493,16 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
     </rng:optional>
   </rng:define>
 
+  <!-- TODO no proposal -->
+  <rng:define name="chart-data-label-attlist" combine="interleave">
+    <rng:optional>
+      <rng:attribute name="loext:data-label-guid">
+        <rng:ref name="string"/>
+      </rng:attribute>
+      <rng:attribute name="loext:data-labels-cell-range">
+        <rng:ref name="cellRangeAddress"/>
+      </rng:attribute>
+    </rng:optional>
+  </rng:define>
+
 </rng:grammar>
diff --git a/xmloff/source/chart/SchXMLExport.cxx b/xmloff/source/chart/SchXMLExport.cxx
index 0ee68425c6fc..3c41c52fefcf 100644
--- a/xmloff/source/chart/SchXMLExport.cxx
+++ b/xmloff/source/chart/SchXMLExport.cxx
@@ -113,7 +113,18 @@ using ::std::vector;
 
 namespace
 {
-    using CustomLabelSeq = Sequence<Reference<chart2::XDataPointCustomLabelField>>;
+    struct CustomLabelData
+    {
+        CustomLabelData():
+            mbDataLabelsRange( false )
+        {
+        }
+
+        Sequence<Reference<chart2::XDataPointCustomLabelField>> maFields;
+        bool mbDataLabelsRange;
+        OUString maRange;
+        OUString maGuid;
+    };
 
     struct SchXMLDataPointStruct
     {
@@ -123,7 +134,7 @@ namespace
 
         // There is no internal equivalent for <chart:data-label>. It will be generated on the fly
         // on export. All about data label is hold in the data point.
-        CustomLabelSeq   mCustomLabelText; // <text:p> child element in <chart:data-label>
+        CustomLabelData   mCustomLabel; // <text:p> child element in <chart:data-label>
         OUString msDataLabelStyleName; // chart:style-name attribute in <chart:data-label>
 
         SchXMLDataPointStruct() : mnRepeat( 1 ) {}
@@ -282,30 +293,58 @@ public:
 
 namespace
 {
-CustomLabelSeq lcl_getCustomLabelField(SvXMLExport const& rExport,
+CustomLabelData lcl_getCustomLabelField(SvXMLExport const& rExport,
                                        sal_Int32 nDataPointIndex,
                                        const uno::Reference< chart2::XDataSeries >& rSeries)
 {
     if (!rSeries.is())
-        return CustomLabelSeq();
+        return CustomLabelData();
 
     // Custom data label text will be written to the <text:p> child element of a
     // <chart:data-label> element. That exists only since ODF 1.2.
     const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
         rExport.getSaneDefaultVersion());
     if (nCurrentODFVersion < SvtSaveOptions::ODFSVER_012)
-        return CustomLabelSeq();
+        return CustomLabelData();
 
     if(Reference<beans::XPropertySet> xLabels = rSeries->getDataPointByIndex(nDataPointIndex); xLabels.is())
     {
         if(Any aAny = xLabels->getPropertyValue("CustomLabelFields"); aAny.hasValue())
         {
+            CustomLabelData aData;
+            uno::Reference<chart2::XDataPointCustomLabelField> aTextField;
             Sequence<uno::Reference<chart2::XDataPointCustomLabelField>> aCustomLabels;
             aAny >>= aCustomLabels;
-            return aCustomLabels;
+            for (auto& rField: aCustomLabels)
+            {
+                if (rField->getFieldType() == chart2::DataPointCustomLabelFieldType_CELLRANGE)
+                {
+                    if (rField->getDataLabelsRange())
+                        aData.mbDataLabelsRange = true;
+                    aData.maRange = rField->getString();
+                    aData.maGuid = rField->getGuid();
+                }
+
+                if (!aTextField.is() && rField->getFieldType() == chart2::DataPointCustomLabelFieldType_TEXT)
+                {
+                    aTextField = rField;
+                }
+            }
+
+            if (aData.mbDataLabelsRange)
+            {
+                aData.maFields = Sequence<uno::Reference<chart2::XDataPointCustomLabelField>>(1);
+                aData.maFields[0] = aTextField;
+            }
+            else
+            {
+                aData.maFields = aCustomLabels;
+            }
+
+            return aData;
         }
     }
-    return CustomLabelSeq();
+    return CustomLabelData();
 }
 
 css::chart2::RelativePosition lcl_getCustomLabelPosition(
@@ -3466,7 +3505,7 @@ void SchXMLExportHelper_Impl::exportDataPoints(
                             maAutoStyleNameQueue.pop();
                         }
                         if(bExportNumFmt)
-                            aPoint.mCustomLabelText = lcl_getCustomLabelField(mrExport, nElement, xSeries);
+                            aPoint.mCustomLabel = lcl_getCustomLabelField(mrExport, nElement, xSeries);
                         aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(mrExport, nElement, xSeries);
 
                         aDataPointVector.push_back( aPoint );
@@ -3545,7 +3584,7 @@ void SchXMLExportHelper_Impl::exportDataPoints(
                             aPoint.maStyleName = maAutoStyleNameQueue.front();
                             maAutoStyleNameQueue.pop();
                         }
-                        aPoint.mCustomLabelText = lcl_getCustomLabelField(mrExport, nCurrIndex, xSeries);
+                        aPoint.mCustomLabel = lcl_getCustomLabelField(mrExport, nCurrIndex, xSeries);
                         aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(mrExport, nCurrIndex, xSeries);
                         if (!aDataLabelPropertyStates.empty())
                         {
@@ -3601,7 +3640,7 @@ void SchXMLExportHelper_Impl::exportDataPoints(
         aPoint = rPoint;
 
         if (aPoint.maStyleName == aLastPoint.maStyleName
-            && aLastPoint.mCustomLabelText.getLength() < 1
+            && aLastPoint.mCustomLabel.maFields.getLength() < 1
             && aLastPoint.mCustomLabelPos.Primary == 0.0
             && aLastPoint.mCustomLabelPos.Secondary == 0.0
             && aPoint.msDataLabelStyleName == aLastPoint.msDataLabelStyleName)
@@ -3658,15 +3697,30 @@ void SchXMLExportHelper_Impl::exportDataPoints(
 
 void SchXMLExportHelper_Impl::exportCustomLabel(const SchXMLDataPointStruct& rPoint)
 {
-    if (rPoint.mCustomLabelText.getLength() < 1 && rPoint.msDataLabelStyleName.isEmpty())
+    if (rPoint.mCustomLabel.maFields.getLength() < 1 && rPoint.msDataLabelStyleName.isEmpty())
         return; // nothing to export
 
     if (!rPoint.msDataLabelStyleName.isEmpty())
         mrExport.AddAttribute(XML_NAMESPACE_CHART, XML_STYLE_NAME, rPoint.msDataLabelStyleName);
+
+    if (rPoint.mCustomLabel.mbDataLabelsRange)
+    {
+        mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_LABELS_CELL_RANGE, rPoint.mCustomLabel.maRange);
+        mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_LABEL_GUID, rPoint.mCustomLabel.maGuid);
+    }
     // TODO svg:x and svg:y for <chart:data-label>
     SvXMLElementExport aLabelElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_LABEL, true, true);
     SvXMLElementExport aPara( mrExport, XML_NAMESPACE_TEXT, XML_P, true, false );
-    for (const Reference<chart2::XDataPointCustomLabelField>& label : rPoint.mCustomLabelText)
+    if (rPoint.mCustomLabel.mbDataLabelsRange)
+    {
+        // Will have only a single text field in this case.
+        // TODO add style
+        SvXMLElementExport aSpan( mrExport, XML_NAMESPACE_TEXT, XML_SPAN, true, false);
+        mrExport.GetDocHandler()->characters(rPoint.mCustomLabel.maFields[0]->getString());
+        return;
+    }
+
+    for (const Reference<chart2::XDataPointCustomLabelField>& label : rPoint.mCustomLabel.maFields)
     {
         // TODO add style
         SvXMLElementExport aSpan( mrExport, XML_NAMESPACE_TEXT, XML_SPAN, true, false);
diff --git a/xmloff/source/chart/SchXMLPlotAreaContext.cxx b/xmloff/source/chart/SchXMLPlotAreaContext.cxx
index 521fcb58ce41..ce8f915f7c4b 100644
--- a/xmloff/source/chart/SchXMLPlotAreaContext.cxx
+++ b/xmloff/source/chart/SchXMLPlotAreaContext.cxx
@@ -617,7 +617,7 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLDataLabelParaCon
 }
 
 SchXMLDataLabelContext::SchXMLDataLabelContext(SvXMLImport& rImport,
-                                               ::std::vector<OUString>& rLabels,
+                                               CustomLabelsInfo& rLabels,
                                                DataRowPointStyle& rDataLabelStyle)
     : SvXMLImportContext(rImport)
     , mrLabels(rLabels)
@@ -630,7 +630,7 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLDataLabelContext
     const css::uno::Reference< css::xml::sax::XFastAttributeList >&  )
 {
     if ( nElement == XML_ELEMENT(TEXT, XML_P) )
-        return new SchXMLDataLabelParaContext(GetImport(), mrLabels);
+        return new SchXMLDataLabelParaContext(GetImport(), mrLabels.mLabels);
     else
         XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
     return nullptr;
@@ -669,6 +669,19 @@ void SchXMLDataLabelContext::StartElement(const uno::Reference<xml::sax::XAttrib
                 mrDataLabelStyle.msStyleName = sValue;
             }
         }
+        else if (nPrefix == XML_NAMESPACE_LO_EXT)
+        {
+            if (IsXMLToken(aLocalName, XML_DATA_LABEL_GUID))
+            {
+                mrLabels.msLabelGuid = sValue;
+                mrLabels.mbDataLabelsRange = true;
+            }
+            else if (IsXMLToken(aLocalName, XML_DATA_LABELS_CELL_RANGE))
+            {
+                mrLabels.msLabelsCellRange = sValue;
+                mrLabels.mbDataLabelsRange = true;
+            }
+        }
     }
 }
 
@@ -744,7 +757,7 @@ void SchXMLDataPointContext::StartElement( const uno::Reference< xml::sax::XAttr
             if( IsXMLToken( aLocalName, XML_CUSTOM_LABEL_FIELD) && !mbHasLabelParagraph)
             {
                 sCustomLabelField = xAttrList->getValueByIndex( i );
-                mDataPoint.mCustomLabels.push_back(sCustomLabelField);
+                mDataPoint.mCustomLabels.mLabels.push_back(sCustomLabelField);
             }
             else if (IsXMLToken(aLocalName, XML_HIDE_LEGEND))
             {
@@ -779,7 +792,7 @@ void SchXMLDataPointContext::StartElement( const uno::Reference< xml::sax::XAttr
 
 void SchXMLDataPointContext::endFastElement(sal_Int32 )
 {
-    if(!mDataPoint.msStyleName.isEmpty() || mDataPoint.mCustomLabels.size() > 0)
+    if(!mDataPoint.msStyleName.isEmpty() || mDataPoint.mCustomLabels.mLabels.size() > 0)
     {
         mrStyleVector.push_back(mDataPoint);
     }
diff --git a/xmloff/source/chart/SchXMLPlotAreaContext.hxx b/xmloff/source/chart/SchXMLPlotAreaContext.hxx
index 1e392e8a3b3c..487d2a93a382 100644
--- a/xmloff/source/chart/SchXMLPlotAreaContext.hxx
+++ b/xmloff/source/chart/SchXMLPlotAreaContext.hxx
@@ -163,11 +163,11 @@ public:
 class SchXMLDataLabelContext: public SvXMLImportContext
 {
 private:
-    ::std::vector<OUString>& mrLabels;
+    CustomLabelsInfo& mrLabels;
     DataRowPointStyle& mrDataLabelStyle;
 public:
     SchXMLDataLabelContext(SvXMLImport& rImport,
-                            ::std::vector<OUString>& rLabels, DataRowPointStyle& rDataLabel);
+                            CustomLabelsInfo& rLabels, DataRowPointStyle& rDataLabel);
 
     virtual void StartElement(const css::uno::Reference<css::xml::sax::XAttributeList>& xAttrList) override;
 
diff --git a/xmloff/source/chart/SchXMLSeries2Context.cxx b/xmloff/source/chart/SchXMLSeries2Context.cxx
index 992a03d64113..e9469e6822ec 100644
--- a/xmloff/source/chart/SchXMLSeries2Context.cxx
+++ b/xmloff/source/chart/SchXMLSeries2Context.cxx
@@ -1220,15 +1220,20 @@ void SchXMLSeries2Context::setStylesToDataPoints( SeriesDefaultsAndStyles& rSeri
                 }
 
                 // Custom labels might be passed as property
-                if(auto nLabelCount = seriesStyle.mCustomLabels.size(); nLabelCount > 0)
+                if(const size_t nLabelCount = seriesStyle.mCustomLabels.mLabels.size(); nLabelCount > 0)
                 {
-                    Sequence< Reference<chart2::XDataPointCustomLabelField>> xLabels(nLabelCount);
+                    auto& rCustomLabels = seriesStyle.mCustomLabels;
+                    size_t nFields = nLabelCount;
+                    if (rCustomLabels.mbDataLabelsRange)
+                        ++nFields; // For appending CELLRANGE field.
+
+                    Sequence< Reference<chart2::XDataPointCustomLabelField>> xLabels(nFields);
                     Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
-                    for( auto j = 0; j< xLabels.getLength(); ++j )
+                    for( size_t j = 0; j < nLabelCount; ++j )
                     {
                         Reference< chart2::XDataPointCustomLabelField > xCustomLabel = chart2::DataPointCustomLabelField::create(xContext);
                         xLabels[j] = xCustomLabel;
-                        xCustomLabel->setString(seriesStyle.mCustomLabels[j]);
+                        xCustomLabel->setString(rCustomLabels.mLabels[j]);
                         xCustomLabel->setFieldType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT);
 
                         // Restore character properties on the text span manually, till
@@ -1251,6 +1256,18 @@ void SchXMLSeries2Context::setStylesToDataPoints( SeriesDefaultsAndStyles& rSeri
                             }
                         }
                     }
+
+                    if (rCustomLabels.mbDataLabelsRange)
+                    {
+                        // append CELLRANGE field using the attributes of <chart:data-label>.
+                        Reference< chart2::XDataPointCustomLabelField > xCustomLabel = chart2::DataPointCustomLabelField::create(xContext);
+                        xCustomLabel->setDataLabelsRange(true);
+                        xCustomLabel->setFieldType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLRANGE);
+                        xCustomLabel->setString(rCustomLabels.msLabelsCellRange);
+                        xCustomLabel->setGuid(rCustomLabels.msLabelGuid);
+                        xLabels[nFields - 1] = xCustomLabel;
+                    }
+
                     xPointProp->setPropertyValue("CustomLabelFields", uno::Any(xLabels));
                     xPointProp->setPropertyValue("DataCaption", uno::Any(chart::ChartDataCaption::CUSTOM));
                 }
diff --git a/xmloff/source/chart/transporttypes.hxx b/xmloff/source/chart/transporttypes.hxx
index 28527c6c06f8..4e395ead292c 100644
--- a/xmloff/source/chart/transporttypes.hxx
+++ b/xmloff/source/chart/transporttypes.hxx
@@ -146,6 +146,14 @@ struct RegressionStyle
     {}
 };
 
+struct CustomLabelsInfo
+{
+    ::std::vector<OUString> mLabels;
+    bool mbDataLabelsRange = false;
+    OUString msLabelGuid;
+    OUString msLabelsCellRange;
+};
+
 struct DataRowPointStyle
 {
     enum StyleType
@@ -171,7 +179,7 @@ struct DataRowPointStyle
     sal_Int32 m_nPointRepeat;
     OUString msStyleName;
     OUString msStyleNameOfParent; // e.g. target of line and fill styles of data-labels
-    ::std::vector<OUString> mCustomLabels;
+    CustomLabelsInfo mCustomLabels;
     double mCustomLabelPos[2] = { 0.0, 0.0 };
     // for svg:x and svg:y attribute (in core unit), of element <chart:data-label>
     std::optional<sal_Int32> mo_nLabelAbsolutePosX;
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index 02482b39515e..9600a0689fc5 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -579,9 +579,11 @@ namespace xmloff::token {
         TOKEN( "data-bar-entry",                  XML_DATA_BAR_ENTRY ),
         TOKEN( "data-cell-range-address",         XML_DATA_CELL_RANGE_ADDRESS ),
         TOKEN( "data-label",                      XML_DATA_LABEL ),
+        TOKEN( "data-label-guid",                 XML_DATA_LABEL_GUID ),
         TOKEN( "data-label-number",               XML_DATA_LABEL_NUMBER ),
         TOKEN( "data-label-symbol",               XML_DATA_LABEL_SYMBOL ),
         TOKEN( "data-label-text",                 XML_DATA_LABEL_TEXT ),
+        TOKEN( "data-labels-cell-range",          XML_DATA_LABELS_CELL_RANGE ),
         TOKEN( "data-pilot-source",               XML_DATA_PILOT_SOURCE ),
         TOKEN( "data-pilot-field",                XML_DATA_PILOT_FIELD ),
         TOKEN( "data-pilot-grand-total",          XML_DATA_PILOT_GRAND_TOTAL ),
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index ead900650328..bb5e0e89f311 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -491,9 +491,11 @@ data-bar
 data-bar-entry
 data-cell-range-address
 data-label
+data-label-guid
 data-label-number
 data-label-symbol
 data-label-text
+data-labels-cell-range
 data-pilot-source
 data-pilot-field
 data-pilot-grand-total


More information about the Libreoffice-commits mailing list