[Libreoffice-commits] core.git: chart2/qa xmloff/source
Regina Henschel (via logerrit)
logerrit at kemper.freedesktop.org
Sat Sep 19 12:27:57 UTC 2020
chart2/qa/extras/chart2geometry.cxx | 56 +++
chart2/qa/extras/data/odt/tdf135366_data_label_export.odt |binary
chart2/qa/extras/data/pptx/tdf135366_CustomLabelText.pptx |binary
xmloff/source/chart/SchXMLExport.cxx | 210 +++++++++++---
4 files changed, 235 insertions(+), 31 deletions(-)
New commits:
commit 6d7a72ab1c044f7e1f30f7c4133dafdb214dfcbf
Author: Regina Henschel <rb.henschel at t-online.de>
AuthorDate: Thu Sep 10 14:42:55 2020 +0200
Commit: Regina Henschel <rb.henschel at t-online.de>
CommitDate: Sat Sep 19 14:27:24 2020 +0200
tdf#135366 Save line and fill of data labels to ODF
LibreOffice has line and fill properties of data labels in charts
as loext attributes in the style of the <chart:series> or
<chart:data-point> element. For ODF there has to be a
<chart:data-label> element with line and fill properties in its
style.
This patch adds the needed <chart:data-label> elements and their
associated <style:style> elements.
The element <chart:data-lable> exists in ODF since version 1.2.
The solution requires no extended namespace. The check is adapted
in lcl_getCustomLabelField.
Import was already done in commit
87d1ebeb11a00301745ee3c3c03fffb7033ab59d
Change-Id: I829dae5433e8257c775aa4f08e511d514df4e936
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/102381
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 b3f537c372e0..be6d842d7780 100644
--- a/chart2/qa/extras/chart2geometry.cxx
+++ b/chart2/qa/extras/chart2geometry.cxx
@@ -21,6 +21,7 @@
#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/saveopt.hxx>
#include <libxml/xpathInternals.h>
@@ -52,6 +53,8 @@ public:
void testTdf128345Legend_CS_TG_axial_import();
void testTdf135366LabelOnSeries();
void testTdf135366LabelOnPoint();
+ void testTdf135366LabelExport();
+ void testTdf135366_CustomLabelText();
CPPUNIT_TEST_SUITE(Chart2GeometryTest);
CPPUNIT_TEST(testTdf135184RoundLineCap);
@@ -66,6 +69,8 @@ public:
CPPUNIT_TEST(testTdf128345Legend_CS_TG_axial_import);
CPPUNIT_TEST(testTdf135366LabelOnSeries);
CPPUNIT_TEST(testTdf135366LabelOnPoint);
+ CPPUNIT_TEST(testTdf135366LabelExport);
+ CPPUNIT_TEST(testTdf135366_CustomLabelText);
CPPUNIT_TEST_SUITE_END();
@@ -502,6 +507,57 @@ void Chart2GeometryTest::testTdf135366LabelOnPoint()
nFillColor);
}
+void Chart2GeometryTest::testTdf135366LabelExport()
+{
+ // Error was, that line and fill properties were not exported as
+ // graphic-properties of a <chart:data-label> element, but only
+ // as loext chart-properties of the <chart:data-point> element.
+ load("/chart2/qa/extras/data/odt/", "tdf135366_data_label_export.odt");
+ xmlDocUniquePtr pXmlDoc = parseExport("Object 1/content.xml", "writer8");
+ CPPUNIT_ASSERT(pXmlDoc);
+
+ // Find label style
+ const OString sLabelPath(
+ "//office:document-content/office:body/office:chart/chart:chart/chart:plot-area"
+ "/chart:series/chart:data-point[1]/chart:data-label/@chart:style-name");
+ const OUString sOULabelStyleName = getXPathContent(pXmlDoc, sLabelPath);
+
+ // Verify content of graphic properties of label style
+ const OString sStylePath(
+ "//office:document-content/office:automatic-styles/style:style[@style:name='"
+ + OU2O(sOULabelStyleName) + "']/style:graphic-properties");
+ assertXPath(pXmlDoc, sStylePath, 1);
+ assertXPath(pXmlDoc, sStylePath + "[@draw:fill='solid']");
+ assertXPath(pXmlDoc, sStylePath + "[@draw:fill-color='#5050a0']");
+ assertXPath(pXmlDoc, sStylePath + "[@draw:stroke='solid']");
+ assertXPath(pXmlDoc, sStylePath + "[@svg:stroke-width='0.254cm']");
+ assertXPath(pXmlDoc, sStylePath + "[@svg:stroke-color='#00ffff']");
+}
+
+void Chart2GeometryTest::testTdf135366_CustomLabelText()
+{
+ // Error was, that custom text in a data label was only exported in ODF extended,
+ // although the used <chart:data-label> element exists since ODF 1.2.
+ SvtSaveOptions aSaveOpt;
+ const SvtSaveOptions::ODFDefaultVersion nCurrentODFVersion(aSaveOpt.GetODFDefaultVersion());
+ aSaveOpt.SetODFDefaultVersion(SvtSaveOptions::ODFVER_012);
+ load("/chart2/qa/extras/data/pptx/", "tdf135366_CustomLabelText.pptx");
+ xmlDocUniquePtr pXmlDoc = parseExport("Object 1/content.xml", "impress8");
+ CPPUNIT_ASSERT(pXmlDoc);
+
+ // Find custom text. As of version 7.0 it is in a <text:span> element.
+ const OString sCustomTextPath(
+ "//office:document-content/office:body/office:chart/chart:chart/chart:plot-area"
+ "/chart:series/chart:data-point[2]/chart:data-label/text:p/text:span");
+ assertXPath(pXmlDoc, sCustomTextPath, 1);
+
+ // Verify text content
+ const OUString sOUTextContent = getXPathContent(pXmlDoc, sCustomTextPath);
+ CPPUNIT_ASSERT_EQUAL(OUString("Custom"), sOUTextContent);
+
+ aSaveOpt.SetODFDefaultVersion(nCurrentODFVersion);
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(Chart2GeometryTest);
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/chart2/qa/extras/data/odt/tdf135366_data_label_export.odt b/chart2/qa/extras/data/odt/tdf135366_data_label_export.odt
new file mode 100644
index 000000000000..85759f2adeca
Binary files /dev/null and b/chart2/qa/extras/data/odt/tdf135366_data_label_export.odt differ
diff --git a/chart2/qa/extras/data/pptx/tdf135366_CustomLabelText.pptx b/chart2/qa/extras/data/pptx/tdf135366_CustomLabelText.pptx
new file mode 100644
index 000000000000..58d73fcd26c1
Binary files /dev/null and b/chart2/qa/extras/data/pptx/tdf135366_CustomLabelText.pptx differ
diff --git a/xmloff/source/chart/SchXMLExport.cxx b/xmloff/source/chart/SchXMLExport.cxx
old mode 100644
new mode 100755
index a2c18e494d19..4911d5180d03
--- a/xmloff/source/chart/SchXMLExport.cxx
+++ b/xmloff/source/chart/SchXMLExport.cxx
@@ -117,8 +117,12 @@ namespace
{
OUString maStyleName;
sal_Int32 mnRepeat;
- CustomLabelSeq mCustomLabelText;
- chart2::RelativePosition mCustomLabelPos;
+ chart2::RelativePosition mCustomLabelPos; // loext:custom-label-pos-x and -y
+
+ // 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>
+ OUString msDataLabelStyleName; // chart:style-name attribute in <chart:data-label>
SchXMLDataPointStruct() : mnRepeat( 1 ) {}
};
@@ -226,8 +230,8 @@ public:
const css::uno::Reference< css::chart2::XDiagram > & xDiagram,
bool bExportContent );
- void exportCustomLabel(const CustomLabelSeq & xCustomLabel);
- void exportCustomLabelPosition(const chart2::RelativePosition & xCustomLabelPosition);
+ void exportCustomLabel(const SchXMLDataPointStruct& rPoint);
+ void exportCustomLabelPosition(const chart2::RelativePosition& xCustomLabelPosition);
void exportRegressionCurve(
const css::uno::Reference<css::chart2::XDataSeries>& xSeries,
@@ -276,15 +280,17 @@ public:
namespace
{
-
CustomLabelSeq lcl_getCustomLabelField(sal_Int32 nDataPointIndex,
- const uno::Reference< chart2::XDataSeries >& rSeries)
+ const uno::Reference< chart2::XDataSeries >& rSeries)
{
- if( !rSeries.is() )
+ if (!rSeries.is())
return CustomLabelSeq();
- const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(SvtSaveOptions().GetODFSaneDefaultVersion());
- if ((nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) == 0) // do not export to ODF 1.3 or older
+ // 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(
+ SvtSaveOptions().GetODFSaneDefaultVersion());
+ if (nCurrentODFVersion < SvtSaveOptions::ODFSVER_012)
return CustomLabelSeq();
if(Reference<beans::XPropertySet> xLabels = rSeries->getDataPointByIndex(nDataPointIndex); xLabels.is())
@@ -2535,7 +2541,64 @@ namespace
//no doubles and no texts
return false;
}
+
+// 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 method generates ODF from internal API name.
+void lcl_createDataLabelProperties(
+ std::vector<XMLPropertyState>& rDataLabelPropertyStates,
+ const Reference<beans::XPropertySet>& xPropSet,
+ const rtl::Reference<XMLChartExportPropertyMapper>& xExpPropMapper)
+{
+ if (!xExpPropMapper.is() || !xPropSet.is())
+ return;
+
+ const uno::Reference<beans::XPropertySetInfo> xInfo(xPropSet->getPropertySetInfo());
+ const uno::Reference<beans::XPropertyState> xPropState(xPropSet, uno::UNO_QUERY);
+ const rtl::Reference<XMLPropertySetMapper>& rPropertySetMapper(
+ xExpPropMapper->getPropertySetMapper());
+ if (!xInfo.is() || !xPropState.is() || !rPropertySetMapper.is())
+ return;
+
+ struct API2ODFMapItem
+ {
+ OUString sAPIName;
+ sal_uInt16 nNameSpace; // from include/xmloff/xmlnamespace.hxx
+ OUString sLocalName;
+ API2ODFMapItem(const OUString& sAPI, const sal_uInt16 nNS, const OUString& sLocal)
+ : sAPIName(sAPI)
+ , nNameSpace(nNS)
+ , sLocalName(sLocal)
+ {
+ }
+ };
+
+ const API2ODFMapItem aLabelFoo2ODFArray[]
+ = { API2ODFMapItem("LabelBorderStyle", XML_NAMESPACE_DRAW, "stroke"),
+ API2ODFMapItem("LabelBorderWidth", XML_NAMESPACE_SVG, "stroke-width"),
+ API2ODFMapItem("LabelBorderColor", XML_NAMESPACE_SVG, "stroke-color"),
+ API2ODFMapItem("LabelBorderDashName", XML_NAMESPACE_DRAW, "stroke-dash"),
+ API2ODFMapItem("LabelBorderTransparency", XML_NAMESPACE_SVG, "stroke-opacity"),
+ API2ODFMapItem("LabelFillStyle", XML_NAMESPACE_DRAW, "fill"),
+ API2ODFMapItem("LabelFillBackground", XML_NAMESPACE_DRAW, "fill-hatch-solid"),
+ API2ODFMapItem("LabelFillHatchName", XML_NAMESPACE_DRAW, "fill-hatch-name"),
+ API2ODFMapItem("LabelFillColor", XML_NAMESPACE_DRAW, "fill-color") };
+
+ for (const auto& rIt : aLabelFoo2ODFArray)
+ {
+ if (!xInfo->hasPropertyByName(rIt.sAPIName)
+ || xPropState->getPropertyState(rIt.sAPIName) != beans::PropertyState_DIRECT_VALUE)
+ continue;
+ sal_Int32 nTargetIndex
+ = rPropertySetMapper->GetEntryIndex(rIt.nNameSpace, rIt.sLocalName, 0);
+ if (nTargetIndex < 0)
+ continue;
+ XMLPropertyState aDataLabelStateItem(nTargetIndex,
+ xPropSet->getPropertyValue(rIt.sAPIName));
+ rDataLabelPropertyStates.emplace_back(aDataLabelStateItem);
+ }
}
+} // anonymous namespace
void SchXMLExportHelper_Impl::exportSeries(
const Reference< chart2::XDiagram > & xNewDiagram,
@@ -2552,6 +2615,7 @@ void SchXMLExportHelper_Impl::exportSeries(
OUString aFirstYDomainRange;
std::vector< XMLPropertyState > aPropertyStates;
+ std::vector< XMLPropertyState > aDataLabelPropertyStates;
const Sequence< Reference< chart2::XCoordinateSystem > >
aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
@@ -2862,6 +2926,37 @@ void SchXMLExportHelper_Impl::exportSeries(
nSeriesLength, xNewDiagram, bExportContent );
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(SvtSaveOptions().GetODFSaneDefaultVersion());
+
+ // create <chart:data-label> child element if needed.
+ if (xPropSet.is() && mxExpPropMapper.is())
+ {
+ // Generate style for <chart:data-label> child element
+ if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
+ {
+ lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet,
+ mxExpPropMapper);
+ }
+ }
+ if (bExportContent)
+ {
+ if (!aDataLabelPropertyStates.empty())
+ {
+ // write style name
+ AddAutoStyleAttribute(aDataLabelPropertyStates);
+ // Further content does currently not exist for a <chart:data-label>
+ // element as child of a <chart:series>.
+ SvXMLElementExport(mrExport, XML_NAMESPACE_CHART, XML_DATA_LABEL, true,
+ true);
+ }
+ }
+ else
+ {
+ // add the style for the to be <chart:data-label> too
+ if (!aDataLabelPropertyStates.empty())
+ CollectAutoStyle(aDataLabelPropertyStates);
+ }
+ aDataLabelPropertyStates.clear();
+
if (bExportContent && nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older
{
Sequence< OUString > aSupportedMappings = rChartType->getSupportedPropertyRoles();
@@ -2873,6 +2968,7 @@ void SchXMLExportHelper_Impl::exportSeries(
}
}
aPropertyStates.clear();
+ aDataLabelPropertyStates.clear();
}
}
}
@@ -3235,6 +3331,7 @@ void SchXMLExportHelper_Impl::exportDataPoints(
uno::Reference< chart2::XDataSeries > xSeries( xSeriesProperties, uno::UNO_QUERY );
std::vector< XMLPropertyState > aPropertyStates;
+ std::vector<XMLPropertyState> aDataLabelPropertyStates;
bool bVaryColorsByPoint = false;
Sequence< sal_Int32 > aDataPointSeq;
@@ -3272,6 +3369,7 @@ void SchXMLExportHelper_Impl::exportDataPoints(
for( nElement = 0; nElement < nSeriesLength; ++nElement )
{
aPropertyStates.clear();
+ aDataLabelPropertyStates.clear();
uno::Reference< beans::XPropertySet > xPropSet;
bool bExportNumFmt = false;
if( aAttrPointSet.find( nElement ) != aEndIt )
@@ -3303,25 +3401,46 @@ void SchXMLExportHelper_Impl::exportDataPoints(
lcl_exportNumberFormat( "PercentageNumberFormat", xPropSet, mrExport );
}
- aPropertyStates = mxExpPropMapper->Filter( xPropSet );
- if( !aPropertyStates.empty() )
+ // Generate style for <chart:data-label> child element
+ if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
{
- if( bExportContent )
+ lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet,
+ mxExpPropMapper);
+ }
+
+ aPropertyStates = mxExpPropMapper->Filter(xPropSet);
+ if (!aPropertyStates.empty() || !aDataLabelPropertyStates.empty())
+ {
+ if (bExportContent)
{
// write data-point with style
- SAL_WARN_IF( maAutoStyleNameQueue.empty(), "xmloff.chart", "Autostyle queue empty!" );
-
SchXMLDataPointStruct aPoint;
- aPoint.maStyleName = maAutoStyleNameQueue.front();
+ if (!aPropertyStates.empty())
+ {
+ SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart",
+ "Autostyle queue empty!");
+ aPoint.maStyleName = maAutoStyleNameQueue.front();
+ maAutoStyleNameQueue.pop();
+ }
+ if (!aDataLabelPropertyStates.empty())
+ {
+ SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart",
+ "Autostyle queue empty!");
+ aPoint.msDataLabelStyleName = maAutoStyleNameQueue.front();
+ maAutoStyleNameQueue.pop();
+ }
if(bExportNumFmt)
aPoint.mCustomLabelText = lcl_getCustomLabelField(nElement, xSeries);
aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(nElement, xSeries);
- maAutoStyleNameQueue.pop();
+
aDataPointVector.push_back( aPoint );
}
else
{
- CollectAutoStyle( aPropertyStates );
+ if (!aPropertyStates.empty())
+ CollectAutoStyle(aPropertyStates);
+ if (!aDataLabelPropertyStates.empty())
+ CollectAutoStyle(aDataLabelPropertyStates);
}
}
}
@@ -3333,6 +3452,7 @@ void SchXMLExportHelper_Impl::exportDataPoints(
for( sal_Int32 nCurrIndex : aDataPointSeq )
{
aPropertyStates.clear();
+ aDataLabelPropertyStates.clear();
//assuming sorted indices in pPoints
if( nCurrIndex<0 || nCurrIndex>=nSeriesLength )
@@ -3366,25 +3486,47 @@ void SchXMLExportHelper_Impl::exportDataPoints(
lcl_exportNumberFormat( "PercentageNumberFormat", xPropSet, mrExport );
}
+ // Generate style for <chart:data-label> child element
+ if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
+ {
+ lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet,
+ mxExpPropMapper);
+ }
+
aPropertyStates = mxExpPropMapper->Filter( xPropSet );
- if( !aPropertyStates.empty() )
+
+ if (!aPropertyStates.empty() || !aDataLabelPropertyStates.empty())
{
if( bExportContent )
{
// write data-point with style
- SAL_WARN_IF( maAutoStyleNameQueue.empty(), "xmloff.chart", "Autostyle queue empty!" );
SchXMLDataPointStruct aPoint;
- aPoint.maStyleName = maAutoStyleNameQueue.front();
+ if (!aPropertyStates.empty())
+ {
+ SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart",
+ "Autostyle queue empty!");
+ aPoint.maStyleName = maAutoStyleNameQueue.front();
+ maAutoStyleNameQueue.pop();
+ }
aPoint.mCustomLabelText = lcl_getCustomLabelField(nCurrIndex, xSeries);
aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(nCurrIndex, xSeries);
- maAutoStyleNameQueue.pop();
+ if (!aDataLabelPropertyStates.empty())
+ {
+ SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart",
+ "Autostyle queue empty!");
+ aPoint.msDataLabelStyleName = maAutoStyleNameQueue.front();
+ maAutoStyleNameQueue.pop();
+ }
aDataPointVector.push_back( aPoint );
nLastIndex = nCurrIndex;
}
else
{
- CollectAutoStyle( aPropertyStates );
+ if (!aPropertyStates.empty())
+ CollectAutoStyle(aPropertyStates);
+ if (!aDataLabelPropertyStates.empty())
+ CollectAutoStyle(aDataLabelPropertyStates);
}
continue;
}
@@ -3421,8 +3563,11 @@ void SchXMLExportHelper_Impl::exportDataPoints(
{
aPoint = rPoint;
- if( aPoint.maStyleName == aLastPoint.maStyleName && aLastPoint.mCustomLabelText.getLength() < 1 &&
- aLastPoint.mCustomLabelPos.Primary == 0.0 && aLastPoint.mCustomLabelPos.Secondary == 0.0 )
+ if (aPoint.maStyleName == aLastPoint.maStyleName
+ && aLastPoint.mCustomLabelText.getLength() < 1
+ && aLastPoint.mCustomLabelPos.Primary == 0.0
+ && aLastPoint.mCustomLabelPos.Secondary == 0.0
+ && aPoint.msDataLabelStyleName == aLastPoint.msDataLabelStyleName)
aPoint.mnRepeat += aLastPoint.mnRepeat;
else if( aLastPoint.mnRepeat > 0 )
{
@@ -3443,9 +3588,9 @@ void SchXMLExportHelper_Impl::exportDataPoints(
}
}
nIndex++;
- exportCustomLabelPosition(aLastPoint.mCustomLabelPos);
+ exportCustomLabelPosition(aLastPoint.mCustomLabelPos); // adds attributes
SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, true, true );
- exportCustomLabel(aLastPoint.mCustomLabelText);
+ exportCustomLabel(aLastPoint);
}
aLastPoint = aPoint;
}
@@ -3469,19 +3614,22 @@ void SchXMLExportHelper_Impl::exportDataPoints(
}
}
- exportCustomLabelPosition(aLastPoint.mCustomLabelPos);
+ exportCustomLabelPosition(aLastPoint.mCustomLabelPos); // adds attributes
SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, true, true );
- exportCustomLabel(aLastPoint.mCustomLabelText);
+ exportCustomLabel(aLastPoint);
}
-void SchXMLExportHelper_Impl::exportCustomLabel( const CustomLabelSeq & xCustomLabel )
+void SchXMLExportHelper_Impl::exportCustomLabel(const SchXMLDataPointStruct& rPoint)
{
- if( xCustomLabel.getLength() < 1 )
+ if (rPoint.mCustomLabelText.getLength() < 1 && rPoint.msDataLabelStyleName.isEmpty())
return; // nothing to export
+ if (!rPoint.msDataLabelStyleName.isEmpty())
+ mrExport.AddAttribute(XML_NAMESPACE_CHART, XML_STYLE_NAME, rPoint.msDataLabelStyleName);
+ // 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 : xCustomLabel )
+ for (const Reference<chart2::XDataPointCustomLabelField>& label : rPoint.mCustomLabelText)
{
// TODO add style
SvXMLElementExport aSpan( mrExport, XML_NAMESPACE_TEXT, XML_SPAN, true, false);
More information about the Libreoffice-commits
mailing list