[Libreoffice-commits] core.git: chart2/qa xmloff/source

Regina Henschel (via logerrit) logerrit at kemper.freedesktop.org
Sat Sep 5 15:49:49 UTC 2020


 chart2/qa/extras/chart2geometry.cxx                       |  100 +++++-
 chart2/qa/extras/data/ods/tdf135366_data_label_series.ods |binary
 chart2/qa/extras/data/odt/tdf135366_data_label_point.odt  |binary
 xmloff/source/chart/SchXMLPlotAreaContext.cxx             |   62 +++
 xmloff/source/chart/SchXMLPlotAreaContext.hxx             |   16 
 xmloff/source/chart/SchXMLSeries2Context.cxx              |  231 ++++++++++----
 xmloff/source/chart/SchXMLSeries2Context.hxx              |    3 
 xmloff/source/chart/transporttypes.hxx                    |   18 +
 8 files changed, 350 insertions(+), 80 deletions(-)

New commits:
commit 87d1ebeb11a00301745ee3c3c03fffb7033ab59d
Author:     Regina Henschel <rb.henschel at t-online.de>
AuthorDate: Sat Aug 22 23:03:02 2020 +0200
Commit:     Regina Henschel <rb.henschel at t-online.de>
CommitDate: Sat Sep 5 17:49:15 2020 +0200

    tdf#135366 ODF import of line and fill of data labels
    
    LibreOffice has line and fill properties of data labels in a chart in
    properties of kind "LabelBorderWidth" or "LabelFillColor" at a data
    point or, as defaults for the points, at a series.
    
    But ODF has such information in a <style:style> element, which is
    refered by a <chart:data-label> element. That one can be child of a
    <chart:data-point> and child of a <chart:series> element.
    
    Microsoft Office correctly uses the <chart:data-point> element and its
    style for the line and fill properties of data labels. Up to now LO
    cannot import such information and does not write the ODF elements.
    Instead LibreOffice reads and writes attributes in 'loext' namespace.
    
    Using the "LabelFoo" properties was implemented by Kohei Yoshida,
    July 2014. Although there is no published service, these properties
    can be used in macros. Because they are now available since six
    years, the decision was to keep this internal model and convert on
    import and export.
    
    This patch implements the import of the ODF fill and line properties
    from the <chart:data-label> element and converts them to the internal
    used properties.
    
    LibreOffice has currently only implemented a few of the possible line
    and fill properties. When more are implemented, their <ODF,LabelFoo>
    pairs need to be added to the array aApiToLabelFooPairs, further
    adaptions are not needed.
    
    The <chart:data-label> contains in addition the absolute position of
    a data label. LibreOffice has internally only a position offset
    relative to the regular position of the label. The conversion of the
    position is not included in the patch.
    
    Change-Id: I5fd868945585971dac3dd945804a4a2951b8c12d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/101194
    Tested-by: Jenkins
    Reviewed-by: Regina Henschel <rb.henschel at t-online.de>

diff --git a/chart2/qa/extras/chart2geometry.cxx b/chart2/qa/extras/chart2geometry.cxx
index 091dafe67265..b3f537c372e0 100644
--- a/chart2/qa/extras/chart2geometry.cxx
+++ b/chart2/qa/extras/chart2geometry.cxx
@@ -11,10 +11,14 @@
 
 #include <test/xmltesttools.hxx>
 
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/XDataSeries.hpp>
 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
 #include <com/sun/star/lang/XServiceName.hpp>
 #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
-#include <com/sun/star/drawing/LineStyle.hpp>
 
 #include <unotools/ucbstreamhelper.hxx>
 
@@ -46,6 +50,8 @@ public:
     void testTdf128345ChartWall_CS_TG_import();
     void testTdf128345Legend_CS_TG_axial_export();
     void testTdf128345Legend_CS_TG_axial_import();
+    void testTdf135366LabelOnSeries();
+    void testTdf135366LabelOnPoint();
 
     CPPUNIT_TEST_SUITE(Chart2GeometryTest);
     CPPUNIT_TEST(testTdf135184RoundLineCap);
@@ -58,6 +64,8 @@ public:
     CPPUNIT_TEST(testTdf128345ChartWall_CS_TG_import);
     CPPUNIT_TEST(testTdf128345Legend_CS_TG_axial_export);
     CPPUNIT_TEST(testTdf128345Legend_CS_TG_axial_import);
+    CPPUNIT_TEST(testTdf135366LabelOnSeries);
+    CPPUNIT_TEST(testTdf135366LabelOnPoint);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -71,12 +79,12 @@ protected:
     xmlDocUniquePtr parseExport(const OUString& rDir, const OUString& rFilterFormat);
 };
 
-// This is copied from Chart2ExportTest. It allows to access the chart form a MS Office document
+namespace
+{
+// This is copied from Chart2ExportTest. It allows to access the chart from a MS Office document
 // without knowing whether the file is a chart1.xml or chart2.xml... As of August 2020, Calc
 // and Impress use a static variable for the number and therefore the number depends on whether
 // there had already been savings before.
-namespace
-{
 struct CheckForChartName
 {
 private:
@@ -410,6 +418,90 @@ void Chart2GeometryTest::testTdf128345Legend_CS_TG_axial_import()
     assertXPath(pXmlDoc2, sStart + " and @draw:end='100%']");
 }
 
+void Chart2GeometryTest::testTdf135366LabelOnSeries()
+{
+    // Error was, that the fill and line properties of a <chart:data-label> were not
+    // imported at all. Here they should be at the series.
+    load("/chart2/qa/extras/data/ods/", "tdf135366_data_label_series.ods");
+    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0, mxComponent);
+    CPPUNIT_ASSERT(xChartDoc.is());
+    Reference<chart2::XDataSeries> xDataSeries = getDataSeriesFromDoc(xChartDoc, 0);
+    CPPUNIT_ASSERT(xDataSeries.is());
+    Reference<beans::XPropertySet> xPropSet(xDataSeries, UNO_QUERY_THROW);
+    uno::Any aAny;
+
+    aAny = xPropSet->getPropertyValue("LabelBorderStyle");
+    drawing::LineStyle eLineStyle;
+    CPPUNIT_ASSERT_MESSAGE("No LabelBorderStyle set.", aAny >>= eLineStyle);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("solid line expected", drawing::LineStyle_SOLID, eLineStyle);
+
+    sal_Int32 nBorderWidth;
+    sal_Int32 nExpectedWidth = 95;
+    xPropSet->getPropertyValue("LabelBorderWidth") >>= nBorderWidth;
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("LabelBorderWidth", nExpectedWidth, nBorderWidth);
+
+    sal_Int32 nLineColor;
+    sal_Int32 nExpectedLineColor = 255;
+    xPropSet->getPropertyValue("LabelBorderColor") >>= nLineColor;
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("line color blue, 255=#0000FF", nExpectedLineColor, nLineColor);
+
+    aAny = xPropSet->getPropertyValue("LabelFillStyle");
+    drawing::FillStyle eFillStyle;
+    CPPUNIT_ASSERT_MESSAGE("No LabelFillStyle set", aAny >>= eFillStyle);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("solid fill expected", drawing::FillStyle_SOLID, eFillStyle);
+
+    sal_Int32 nFillColor;
+    sal_Int32 nExpectedFillColor = 65280;
+    xPropSet->getPropertyValue("LabelFillColor") >>= nFillColor;
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("fill color green, 65280=#00FF00", nExpectedFillColor, nFillColor);
+}
+
+void Chart2GeometryTest::testTdf135366LabelOnPoint()
+{
+    // Error was, that the fill and line properties of a <chart:data-label> were not
+    // imported at all. Here they should be at point 2.
+    load("/chart2/qa/extras/data/odt/", "tdf135366_data_label_point.odt");
+    uno::Reference<chart2::XChartDocument> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY);
+    CPPUNIT_ASSERT(xChartDoc.is());
+    Reference<chart2::XDataSeries> xDataSeries = getDataSeriesFromDoc(xChartDoc, 0);
+    CPPUNIT_ASSERT(xDataSeries.is());
+    Reference<beans::XPropertySet> xPropSet(xDataSeries->getDataPointByIndex(2),
+                                            uno::UNO_SET_THROW);
+    uno::Any aAny;
+
+    aAny = xPropSet->getPropertyValue("LabelBorderStyle");
+    drawing::LineStyle eLineStyle;
+    CPPUNIT_ASSERT_MESSAGE("No LabelBorderStyle set.", aAny >>= eLineStyle);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("solid line expected", drawing::LineStyle_SOLID, eLineStyle);
+
+    sal_Int32 nBorderWidth;
+    sal_Int32 nExpectedWidth = 381;
+    xPropSet->getPropertyValue("LabelBorderWidth") >>= nBorderWidth;
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("LabelBorderWidth", nExpectedWidth, nBorderWidth);
+
+    sal_Int32 nLineTransparency;
+    sal_Int32 nExpectedTransparency = 30;
+    xPropSet->getPropertyValue("LabelBorderTransparency") >>= nLineTransparency;
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("line transparency", nExpectedTransparency, nLineTransparency);
+
+    sal_Int32 nLineColor;
+    sal_Int32 nExpectedLineColor = 10206041;
+    xPropSet->getPropertyValue("LabelBorderColor") >>= nLineColor;
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("line color greenish, 10206041=#9BBB59", nExpectedLineColor,
+                                 nLineColor);
+
+    aAny = xPropSet->getPropertyValue("LabelFillStyle");
+    drawing::FillStyle eFillStyle;
+    CPPUNIT_ASSERT_MESSAGE("No LabelFillStyle set", aAny >>= eFillStyle);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("solid fill expected", drawing::FillStyle_SOLID, eFillStyle);
+
+    sal_Int32 nFillColor;
+    sal_Int32 nExpectedFillColor = 14277081;
+    xPropSet->getPropertyValue("LabelFillColor") >>= nFillColor;
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("fill color gray, 14277081=#d9d9d9", nExpectedFillColor,
+                                 nFillColor);
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(Chart2GeometryTest);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/chart2/qa/extras/data/ods/tdf135366_data_label_series.ods b/chart2/qa/extras/data/ods/tdf135366_data_label_series.ods
new file mode 100644
index 000000000000..e7c1f7d89de2
Binary files /dev/null and b/chart2/qa/extras/data/ods/tdf135366_data_label_series.ods differ
diff --git a/chart2/qa/extras/data/odt/tdf135366_data_label_point.odt b/chart2/qa/extras/data/odt/tdf135366_data_label_point.odt
new file mode 100644
index 000000000000..3c176a37adff
Binary files /dev/null and b/chart2/qa/extras/data/odt/tdf135366_data_label_point.odt differ
diff --git a/xmloff/source/chart/SchXMLPlotAreaContext.cxx b/xmloff/source/chart/SchXMLPlotAreaContext.cxx
index 02eb8a2f0f42..8782ef7818ff 100644
--- a/xmloff/source/chart/SchXMLPlotAreaContext.cxx
+++ b/xmloff/source/chart/SchXMLPlotAreaContext.cxx
@@ -616,9 +616,12 @@ SvXMLImportContextRef SchXMLDataLabelParaContext::CreateChildContext(
     return xContext;
 }
 
-SchXMLDataLabelContext::SchXMLDataLabelContext( SvXMLImport& rImport, const OUString& rLocalName, ::std::vector<OUString>& rLabels):
-    SvXMLImportContext( rImport, XML_NAMESPACE_CHART, rLocalName),
-    mrLabels(rLabels)
+SchXMLDataLabelContext::SchXMLDataLabelContext(SvXMLImport& rImport, const OUString& rLocalName,
+                                               ::std::vector<OUString>& rLabels,
+                                               DataRowPointStyle& rDataLabelStyle)
+    : SvXMLImportContext(rImport, XML_NAMESPACE_CHART, rLocalName)
+    , mrLabels(rLabels)
+    , mrDataLabelStyle(rDataLabelStyle)
 {
 }
 
@@ -634,6 +637,42 @@ SvXMLImportContextRef SchXMLDataLabelContext::CreateChildContext(
     return xContext;
 }
 
+void SchXMLDataLabelContext::StartElement(const uno::Reference<xml::sax::XAttributeList>& xAttrList)
+{
+    const SvXMLNamespaceMap& rMap = GetImport().GetNamespaceMap();
+    const sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
+    for (sal_Int16 i = 0; i < nAttrCount; i++)
+    {
+        const OUString& rAttrName = xAttrList->getNameByIndex(i);
+        OUString aLocalName;
+        sal_uInt16 nPrefix = rMap.GetKeyByAttrName(rAttrName, &aLocalName);
+        const OUString sValue(xAttrList->getValueByIndex(i));
+
+        if (nPrefix == XML_NAMESPACE_SVG)
+        {
+            if (IsXMLToken(aLocalName, XML_X))
+            {
+                sal_Int32 nResultValue;
+                GetImport().GetMM100UnitConverter().convertMeasureToCore(nResultValue, sValue);
+                mrDataLabelStyle.mo_nLabelAbsolutePosX = nResultValue;
+            }
+            else if (IsXMLToken(aLocalName, XML_Y))
+            {
+                sal_Int32 nResultValue;
+                GetImport().GetMM100UnitConverter().convertMeasureToCore(nResultValue, sValue);
+                mrDataLabelStyle.mo_nLabelAbsolutePosY = nResultValue;
+            }
+        }
+        else if (nPrefix == XML_NAMESPACE_CHART)
+        {
+            if (IsXMLToken(aLocalName, XML_STYLE_NAME))
+            {
+                mrDataLabelStyle.msStyleName = sValue;
+            }
+        }
+    }
+}
+
 
 SchXMLDataPointContext::SchXMLDataPointContext(  SchXMLImportHelper& rImportHelper,
                                                  SvXMLImport& rImport, const OUString& rLocalName,
@@ -645,7 +684,8 @@ SchXMLDataPointContext::SchXMLDataPointContext(  SchXMLImportHelper& rImportHelp
         mrImportHelper( rImportHelper ),
         mrStyleVector( rStyleVector ),
         mrIndex( rIndex ),
-        mDataPoint(DataRowPointStyle::DATA_POINT, xSeries, rIndex, 1, OUString{})
+        mDataPoint(DataRowPointStyle::DATA_POINT, xSeries, rIndex, 1, OUString{}),
+        mDataLabel(DataRowPointStyle::DATA_LABEL_POINT, xSeries, rIndex, 1, OUString{})
 {
     mDataPoint.mbSymbolSizeForSeriesIsMissingInFile = bSymbolSizeForSeriesIsMissingInFile;
 }
@@ -662,7 +702,8 @@ SvXMLImportContextRef SchXMLDataPointContext::CreateChildContext(
     {
         case XML_TOK_SERIES_DATA_LABEL:
             mbHasLabelParagraph = true;
-            pContext = new SchXMLDataLabelContext( GetImport(), rLocalName, mDataPoint.mCustomLabels);
+            pContext = new SchXMLDataLabelContext(GetImport(), rLocalName, mDataPoint.mCustomLabels,
+                                                  mDataLabel);
             break;
     }
     return pContext;
@@ -692,11 +733,13 @@ void SchXMLDataPointContext::StartElement( const uno::Reference< xml::sax::XAttr
             {
                 sAutoStyleName = xAttrList->getValueByIndex( i );
                 mDataPoint.msStyleName = sAutoStyleName;
+                mDataLabel.msStyleNameOfParent = sAutoStyleName;
             }
             else if( IsXMLToken( aLocalName, XML_REPEATED ) )
             {
                 nRepeat = xAttrList->getValueByIndex( i ).toInt32();
                 mDataPoint.m_nPointRepeat = nRepeat;
+                mDataLabel.m_nPointRepeat = nRepeat;
             }
         }
         else if( nPrefix == XML_NAMESPACE_LO_EXT)
@@ -741,9 +784,14 @@ void SchXMLDataPointContext::StartElement( const uno::Reference< xml::sax::XAttr
 
 void SchXMLDataPointContext::EndElement()
 {
-    if( !mDataPoint.msStyleName.isEmpty() || mDataPoint.mCustomLabels.size() > 0)
+    if(!mDataPoint.msStyleName.isEmpty() || mDataPoint.mCustomLabels.size() > 0)
+    {
+        mrStyleVector.push_back(mDataPoint);
+    }
+    if (!mDataLabel.msStyleName.isEmpty() || mDataLabel.mo_nLabelAbsolutePosX.has_value()
+        || mDataLabel.mo_nLabelAbsolutePosY.has_value())
     {
-        mrStyleVector.push_back( mDataPoint );
+        mrStyleVector.push_back(mDataLabel);
     }
 }
 
diff --git a/xmloff/source/chart/SchXMLPlotAreaContext.hxx b/xmloff/source/chart/SchXMLPlotAreaContext.hxx
index 1541c4ef6788..8434630cfc08 100644
--- a/xmloff/source/chart/SchXMLPlotAreaContext.hxx
+++ b/xmloff/source/chart/SchXMLPlotAreaContext.hxx
@@ -166,22 +166,28 @@ class SchXMLDataLabelContext: public SvXMLImportContext
 {
 private:
     ::std::vector<OUString>& mrLabels;
+    DataRowPointStyle& mrDataLabelStyle;
 public:
-    SchXMLDataLabelContext( SvXMLImport& rImport, const OUString& rLocalName, ::std::vector<OUString>& rLabels);
+    SchXMLDataLabelContext(SvXMLImport& rImport, const OUString& rLocalName,
+                            ::std::vector<OUString>& rLabels, DataRowPointStyle& rDataLabel);
+
+    virtual void StartElement(const css::uno::Reference<css::xml::sax::XAttributeList>& xAttrList) override;
+
     virtual SvXMLImportContextRef CreateChildContext(
-        sal_uInt16 nPrefix,
-        const OUString& rLocalName,
-        const css::uno::Reference< css::xml::sax::XAttributeList >& xAttrList ) override;
+        sal_uInt16 nPrefix, const OUString& rLocalName,
+        const css::uno::Reference<css::xml::sax::XAttributeList>& xAttrList) override;
 };
 
 class SchXMLDataPointContext : public SvXMLImportContext
 {
 private:
     SchXMLImportHelper& mrImportHelper;
-    ::std::vector< DataRowPointStyle >& mrStyleVector;
+    ::std::vector<DataRowPointStyle>& mrStyleVector;
     bool mbHasLabelParagraph = false;
     sal_Int32& mrIndex;
     DataRowPointStyle mDataPoint;
+    // We let the data point manage the DataRowPointStyle-struct of its data label
+    DataRowPointStyle mDataLabel;
 
 public:
     SchXMLDataPointContext(  SchXMLImportHelper& rImportHelper,
diff --git a/xmloff/source/chart/SchXMLSeries2Context.cxx b/xmloff/source/chart/SchXMLSeries2Context.cxx
index 5a9ef3800060..58f8a6b28eb3 100644
--- a/xmloff/source/chart/SchXMLSeries2Context.cxx
+++ b/xmloff/source/chart/SchXMLSeries2Context.cxx
@@ -55,6 +55,8 @@
 #include <xmloff/prstylei.hxx>
 #include <tools/diagnose_ex.h>
 
+#include <algorithm> // std::find_if
+
 using namespace ::com::sun::star;
 using namespace ::xmloff::token;
 
@@ -290,7 +292,9 @@ SchXMLSeries2Context::SchXMLSeries2Context(
         mrLSequencesPerIndex( rLSequencesPerIndex ),
         mrGlobalChartTypeUsedBySeries( rGlobalChartTypeUsedBySeries ),
         mbSymbolSizeIsMissingInFile(false),
-        maChartSize( rChartSize )
+        maChartSize( rChartSize ),
+        // A series manages the DataRowPointStyle-struct of a data-label child element.
+        mDataLabel(DataRowPointStyle::DATA_LABEL_SERIES, OUString{})
 {
     if( aGlobalChartTypeName == "com.sun.star.chart2.DonutChartType" )
     {
@@ -636,6 +640,15 @@ void SchXMLSeries2Context::EndElement()
             aStyle.mbSymbolSizeForSeriesIsMissingInFile=mbSymbolSizeIsMissingInFile;
             mrStyleVector.push_back( aStyle );
         }
+        // And styles for a data-label child element too. In contrast to data-labels as child of data points,
+        // an information about absolute position is useless here. We need only style information.
+        if (!mDataLabel.msStyleName.isEmpty())
+        {
+            mDataLabel.msStyleNameOfParent = msAutoStyleName;
+            mDataLabel.m_xSeries = m_xSeries;
+            mDataLabel.mnAttachedAxis = mnAttachedAxis; // not needed, but be consistent with its parent
+            mrStyleVector.push_back(mDataLabel);
+        }
     }
 
     for( std::vector< DomainInfo >::reverse_iterator aIt( aDomainInfos.rbegin() ); aIt!= aDomainInfos.rend(); ++aIt )
@@ -712,6 +725,13 @@ SvXMLImportContextRef SchXMLSeries2Context::CreateChildContext(
             pContext = new SchXMLDataPointContext( mrImportHelper, GetImport(), rLocalName,
                                                    mrStyleVector, m_xSeries, mnDataPointIndex, mbSymbolSizeIsMissingInFile );
             break;
+        case XML_TOK_SERIES_DATA_LABEL:
+            // CustomLabels are useless for a data label element as child of a series, because it serves as default
+            // for all data labels. But the ctor expects it, so use that of the mDataLabel struct as ersatz.
+            pContext = new SchXMLDataLabelContext(GetImport(), rLocalName, mDataLabel.mCustomLabels,
+                                                  mDataLabel);
+            break;
+
         case XML_TOK_SERIES_PROPERTY_MAPPING:
             pContext = new SchXMLPropertyMappingContext( mrImportHelper,
                     GetImport(), rLocalName,
@@ -797,6 +817,26 @@ void SchXMLSeries2Context::setDefaultsToSeries( SeriesDefaultsAndStyles& rSeries
     }
 }
 
+// ODF has the line and fill properties in a <style:style> element, which is referenced by the
+// <chart:data-label> element. But LibreOffice has them as special label properties of the series
+// or point respectively. The following array maps the API name of the ODF property to the name of
+// the internal property. Those are of kind "LabelFoo".
+// The array is used in methods setStylesToSeries and setStylesToDataPoints.
+const std::pair<OUString, OUString> aApiToLabelFooPairs[]
+    = { { "LineStyle", "LabelBorderStyle" },
+        { "LineWidth", "LabelBorderWidth" },
+        { "LineColor", "LabelBorderColor" },
+        // The name "LaberBorderDash" ist defined, but the associated API name "LineDash" belongs to
+        // the <draw:stroke-dash> element and is not used directly as line property.
+        //{"LineDash", "LabelBorderDash"},
+        { "LineDashName", "LabelBorderDashName" },
+        { "LineTransparence", "LabelBorderTransparency" },
+        { "FillStyle", "LabelFillStyle" },
+        { "FillBackground", "LabelFillBackground" },
+        { "FillHatchName", "LabelFillHatchName" },
+        { "FillColor", "LabelFillColor" } };
+
+
 //static
 void SchXMLSeries2Context::setStylesToSeries( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles
         , const SvXMLStylesContext* pStylesCtxt
@@ -810,78 +850,110 @@ void SchXMLSeries2Context::setStylesToSeries( SeriesDefaultsAndStyles& rSeriesDe
     // iterate over series
     for (const auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector)
     {
-        if( seriesStyle.meType == DataRowPointStyle::DATA_SERIES )
+        if (seriesStyle.meType != DataRowPointStyle::DATA_SERIES)
+            continue;
+        try
         {
-            try
+            uno::Reference< beans::XPropertySet > xSeriesProp( seriesStyle.m_xOldAPISeries );
+            if( !xSeriesProp.is() )
+                continue;
+
+            if( seriesStyle.mnAttachedAxis != 1 )
             {
-                uno::Reference< beans::XPropertySet > xSeriesProp( seriesStyle.m_xOldAPISeries );
-                if( !xSeriesProp.is() )
-                    continue;
+                xSeriesProp->setPropertyValue("Axis"
+                    , uno::makeAny(chart::ChartAxisAssign::SECONDARY_Y) );
+            }
 
-                if( seriesStyle.mnAttachedAxis != 1 )
+            if( seriesStyle.msStyleName.isEmpty())
+                continue;
+
+            if( rCurrStyleName != seriesStyle.msStyleName )
+            {
+                rCurrStyleName = seriesStyle.msStyleName;
+                rpStyle = pStylesCtxt->FindStyleChildContext(
+                SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName );
+            }
+
+            //set style to series
+            // note: SvXMLStyleContext::FillPropertySet is not const
+            XMLPropStyleContext * pPropStyleContext =
+                const_cast< XMLPropStyleContext * >(
+                    dynamic_cast< const XMLPropStyleContext * >( rpStyle ));
+
+            if (!pPropStyleContext)
+                continue;
+
+            // error bar style must be set before the other error
+            // bar properties (which may be alphabetically before
+            // this property)
+            bool bHasErrorBarRangesFromData = false;
+            {
+                const OUString aErrorBarStylePropName( "ErrorBarStyle");
+                uno::Any aErrorBarStyle(
+                    SchXMLTools::getPropertyFromContext( aErrorBarStylePropName, pPropStyleContext, pStylesCtxt ));
+                if( aErrorBarStyle.hasValue())
                 {
-                    xSeriesProp->setPropertyValue("Axis"
-                        , uno::makeAny(chart::ChartAxisAssign::SECONDARY_Y) );
+                    xSeriesProp->setPropertyValue( aErrorBarStylePropName, aErrorBarStyle );
+                    sal_Int32 eEBStyle = chart::ErrorBarStyle::NONE;
+                    bHasErrorBarRangesFromData =
+                        ( ( aErrorBarStyle >>= eEBStyle ) &&
+                         eEBStyle == chart::ErrorBarStyle::FROM_DATA );
                 }
+            }
 
-                if( !seriesStyle.msStyleName.isEmpty())
-                {
-                    if( rCurrStyleName != seriesStyle.msStyleName )
-                    {
-                        rCurrStyleName = seriesStyle.msStyleName;
-                        rpStyle = pStylesCtxt->FindStyleChildContext(
-                            SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName );
-                    }
+            //don't set the style to the min max line series of a stock chart
+            //otherwise the min max line properties gets overwritten and the series becomes invisible typically
+            if (bIsStockChart)
+            {
+                if (SchXMLSeriesHelper::isCandleStickSeries(
+                        seriesStyle.m_xSeries,
+                        rImportHelper.GetChartDocument()))
+                    continue;
+            }
 
-                    //set style to series
-                    // note: SvXMLStyleContext::FillPropertySet is not const
-                    XMLPropStyleContext * pPropStyleContext =
-                        const_cast< XMLPropStyleContext * >(
-                            dynamic_cast< const XMLPropStyleContext * >( rpStyle ));
-                    if( pPropStyleContext )
+            // Has the series a data-label child element?
+            auto pItLabel
+                = std::find_if(rSeriesDefaultsAndStyles.maSeriesStyleVector.begin(),
+                               rSeriesDefaultsAndStyles.maSeriesStyleVector.end(),
+                               [seriesStyle](const DataRowPointStyle& rStyle) {
+                                   return rStyle.meType == DataRowPointStyle::DATA_LABEL_SERIES
+                                          && rStyle.msStyleNameOfParent == seriesStyle.msStyleName;
+                               });
+            if (pItLabel != rSeriesDefaultsAndStyles.maSeriesStyleVector.end())
+            {
+                // Bring the information from the data-label to the series
+                const SvXMLStyleContext* pLabelStyleContext(pStylesCtxt->FindStyleChildContext(
+                    SchXMLImportHelper::GetChartFamilyID(), (*pItLabel).msStyleName));
+                // note: SvXMLStyleContext::FillPropertySet is not const
+                XMLPropStyleContext* pLabelPropStyleContext = const_cast<XMLPropStyleContext*>(
+                    dynamic_cast<const XMLPropStyleContext*>(pLabelStyleContext));
+                if (pLabelPropStyleContext)
+                {
+                    // Test each to be mapped property whether the data-label has a value for it.
+                    // If found, set it at series.
+                    uno::Reference<beans::XPropertySetInfo> xSeriesPropInfo(
+                        xSeriesProp->getPropertySetInfo());
+                    for (const auto& rPropPair : aApiToLabelFooPairs)
                     {
-                        // error bar style must be set before the other error
-                        // bar properties (which may be alphabetically before
-                        // this property)
-                        bool bHasErrorBarRangesFromData = false;
-                        {
-                            const OUString aErrorBarStylePropName( "ErrorBarStyle");
-                            uno::Any aErrorBarStyle(
-                                SchXMLTools::getPropertyFromContext( aErrorBarStylePropName, pPropStyleContext, pStylesCtxt ));
-                            if( aErrorBarStyle.hasValue())
-                            {
-                                xSeriesProp->setPropertyValue( aErrorBarStylePropName, aErrorBarStyle );
-                                sal_Int32 eEBStyle = chart::ErrorBarStyle::NONE;
-                                bHasErrorBarRangesFromData =
-                                    ( ( aErrorBarStyle >>= eEBStyle ) &&
-                                      eEBStyle == chart::ErrorBarStyle::FROM_DATA );
-                            }
-                        }
-
-                        //don't set the style to the min max line series of a stock chart
-                        //otherwise the min max line properties gets overwritten and the series becomes invisible typically
-                        bool bIsMinMaxSeries = false;
-                        if( bIsStockChart )
-                        {
-                            if( SchXMLSeriesHelper::isCandleStickSeries( seriesStyle.m_xSeries
-                                    , rImportHelper.GetChartDocument() ) )
-                                bIsMinMaxSeries = true;
-                        }
-                        if( !bIsMinMaxSeries )
-                        {
-                            pPropStyleContext->FillPropertySet( xSeriesProp );
-                            if( seriesStyle.mbSymbolSizeForSeriesIsMissingInFile )
-                                lcl_setSymbolSizeIfNeeded( xSeriesProp, rImport );
-                            if( bHasErrorBarRangesFromData )
-                                lcl_insertErrorBarLSequencesToMap( rInOutLSequencesPerIndex, xSeriesProp );
-                        }
+                        uno::Any aPropValue(SchXMLTools::getPropertyFromContext(
+                            rPropPair.first, pLabelPropStyleContext, pStylesCtxt));
+                        if (aPropValue.hasValue()
+                            && xSeriesPropInfo->hasPropertyByName(rPropPair.second))
+                            xSeriesProp->setPropertyValue(rPropPair.second, aPropValue);
                     }
                 }
             }
-            catch( const uno::Exception & )
-            {
+
+            pPropStyleContext->FillPropertySet( xSeriesProp );
+            if( seriesStyle.mbSymbolSizeForSeriesIsMissingInFile )
+                lcl_setSymbolSizeIfNeeded( xSeriesProp, rImport );
+            if( bHasErrorBarRangesFromData )
+                lcl_insertErrorBarLSequencesToMap( rInOutLSequencesPerIndex, xSeriesProp );
+
+        }
+        catch( const uno::Exception & )
+        {
                 TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to series" );
-            }
         }
     }
 }
@@ -1101,8 +1173,43 @@ void SchXMLSeries2Context::setStylesToDataPoints( SeriesDefaultsAndStyles& rSeri
                 XMLPropStyleContext * pPropStyleContext =
                     const_cast< XMLPropStyleContext * >(
                         dynamic_cast< const XMLPropStyleContext * >( rpStyle ));
-                if( pPropStyleContext )
+                if (pPropStyleContext)
                 {
+                    // Has the point a data-label child element?
+                    auto pItLabel = std::find_if(
+                        rSeriesDefaultsAndStyles.maSeriesStyleVector.begin(),
+                        rSeriesDefaultsAndStyles.maSeriesStyleVector.end(),
+                        [seriesStyle](const DataRowPointStyle& rStyle) {
+                            return rStyle.meType == DataRowPointStyle::DATA_LABEL_POINT
+                                   && rStyle.msStyleNameOfParent == seriesStyle.msStyleName;
+                        });
+                    if (pItLabel != rSeriesDefaultsAndStyles.maSeriesStyleVector.end())
+                    {
+                        // Bring the information from the data-label to the point
+                        const SvXMLStyleContext* pLabelStyleContext(
+                            pStylesCtxt->FindStyleChildContext(
+                                SchXMLImportHelper::GetChartFamilyID(), (*pItLabel).msStyleName));
+                        // note: SvXMLStyleContext::FillPropertySet is not const
+                        XMLPropStyleContext* pLabelPropStyleContext
+                            = const_cast<XMLPropStyleContext*>(
+                                dynamic_cast<const XMLPropStyleContext*>(pLabelStyleContext));
+                        if (pLabelPropStyleContext)
+                        {
+                            // Test each to be mapped property whether the data-label has a value for it.
+                            // If found, set it at the point.
+                            uno::Reference<beans::XPropertySetInfo> xPointPropInfo(
+                                xPointProp->getPropertySetInfo());
+                            for (const auto& rPropPair : aApiToLabelFooPairs)
+                            {
+                                uno::Any aPropValue(SchXMLTools::getPropertyFromContext(
+                                    rPropPair.first, pLabelPropStyleContext, pStylesCtxt));
+                                if (aPropValue.hasValue()
+                                    && xPointPropInfo->hasPropertyByName(rPropPair.second))
+                                    xPointProp->setPropertyValue(rPropPair.second, aPropValue);
+                            }
+                        }
+                    }
+
                     pPropStyleContext->FillPropertySet( xPointProp );
                     if( seriesStyle.mbSymbolSizeForSeriesIsMissingInFile )
                         lcl_resetSymbolSizeForPointsIfNecessary( xPointProp, rImport, pPropStyleContext, pStylesCtxt );
diff --git a/xmloff/source/chart/SchXMLSeries2Context.hxx b/xmloff/source/chart/SchXMLSeries2Context.hxx
index 7f825974e9a6..8e5e4636580a 100644
--- a/xmloff/source/chart/SchXMLSeries2Context.hxx
+++ b/xmloff/source/chart/SchXMLSeries2Context.hxx
@@ -46,7 +46,6 @@ private:
     ::std::vector< SchXMLAxis >& mrAxes;
     ::std::vector< DataRowPointStyle >& mrStyleVector;
     ::std::vector< RegressionStyle >& mrRegressionStyleVector;
-
     css::uno::Reference< css::chart2::XDataSeries > m_xSeries;
     sal_Int32 mnSeriesIndex;
     sal_Int32 mnDataPointIndex;
@@ -67,6 +66,8 @@ private:
     bool& mrGlobalChartTypeUsedBySeries;
     bool mbSymbolSizeIsMissingInFile;
     css::awt::Size maChartSize;
+    // We let the series manage the DataRowPointStyle-struct of its data label
+    DataRowPointStyle mDataLabel;
 
 public:
     SchXMLSeries2Context( SchXMLImportHelper& rImpHelper,
diff --git a/xmloff/source/chart/transporttypes.hxx b/xmloff/source/chart/transporttypes.hxx
index 673729f43033..e75218a06446 100644
--- a/xmloff/source/chart/transporttypes.hxx
+++ b/xmloff/source/chart/transporttypes.hxx
@@ -24,6 +24,7 @@
 
 #include <vector>
 #include <map>
+#include <optional>
 
 enum SchXMLCellType
 {
@@ -156,7 +157,9 @@ struct DataRowPointStyle
         DATA_POINT,
         DATA_SERIES,
         MEAN_VALUE,
-        ERROR_INDICATOR
+        ERROR_INDICATOR,
+        DATA_LABEL_POINT,
+        DATA_LABEL_SERIES
     };
 
     StyleType meType;
@@ -171,8 +174,12 @@ struct DataRowPointStyle
     sal_Int32 m_nPointIndex;
     sal_Int32 m_nPointRepeat;
     OUString msStyleName;
+    OUString msStyleNameOfParent; // e.g. target of line and fill styles of data-labels
     ::std::vector<OUString> 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;
+    std::optional<sal_Int32> mo_nLabelAbsolutePosY;
     OUString msSeriesStyleNameForDonuts;
 
     sal_Int32 mnAttachedAxis;
@@ -192,6 +199,15 @@ struct DataRowPointStyle
             mnAttachedAxis( nAttachedAxis ),
             mbSymbolSizeForSeriesIsMissingInFile( false )
         {}
+
+    // ctor for use in import of <chart:data-label> as child of <chart:series>
+    DataRowPointStyle(StyleType eType, const OUString& sStyleName, sal_Int32 nAttachedAxis = 0)
+        : meType(eType)
+        , msStyleName(sStyleName)
+        , mnAttachedAxis(nAttachedAxis)
+        , mbSymbolSizeForSeriesIsMissingInFile(false)
+    {
+    }
 };
 
 typedef ::std::multimap< OUString, css::uno::Reference<


More information about the Libreoffice-commits mailing list