tdf#123206 Store custom label as chart:data-label
Use the chart:data-label element instead of using the
loext:custom-label-field attribute.
chart:data-label stores can be a child of chart:data-point and it may
contain a text:o element for holding one or more paragraphs of custom
label text.
This commit aims to export and import chart:data-label with paragraphs
put into different text:span elements. These span elements may hold a
text:style-name attribute in order to achieve formatted text.
This structure is already in the ODF format.
Change-Id: I0bea7ce1a16af9c47b33555e18545bdaae7e95ca
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/85659
Tested-by: Jenkins
Reviewed-by: Tamás Bunth <btomi96@gmail.com>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/86113
Reviewed-by: Andras Timar <andras.timar@collabora.com>
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index 97748c0..2b53622 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -548,6 +548,7 @@
XML_DATA_BAR,
XML_DATA_BAR_ENTRY,
XML_DATA_CELL_RANGE_ADDRESS,
XML_DATA_LABEL,
XML_DATA_LABEL_NUMBER,
XML_DATA_LABEL_SYMBOL,
XML_DATA_LABEL_TEXT,
diff --git a/xmloff/inc/SchXMLImport.hxx b/xmloff/inc/SchXMLImport.hxx
index e8ca86c..10bb46c 100644
--- a/xmloff/inc/SchXMLImport.hxx
+++ b/xmloff/inc/SchXMLImport.hxx
@@ -76,6 +76,7 @@
enum SchXMLSeriesElemTokenMap
{
XML_TOK_SERIES_DATA_POINT,
XML_TOK_SERIES_DATA_LABEL,
XML_TOK_SERIES_DOMAIN,
XML_TOK_SERIES_MEAN_VALUE_LINE,
XML_TOK_SERIES_REGRESSION_CURVE,
diff --git a/xmloff/source/chart/SchXMLExport.cxx b/xmloff/source/chart/SchXMLExport.cxx
index 5f5bd22..e17d903 100644
--- a/xmloff/source/chart/SchXMLExport.cxx
+++ b/xmloff/source/chart/SchXMLExport.cxx
@@ -123,11 +123,13 @@
namespace
{
using CustomLabelSeq = Sequence<Reference<chart2::XDataPointCustomLabelField>>;
struct SchXMLDataPointStruct
{
OUString maStyleName;
sal_Int32 mnRepeat;
OUString msCustomLabelText;
CustomLabelSeq mCustomLabelText;
SchXMLDataPointStruct() : mnRepeat( 1 ) {}
};
@@ -239,6 +241,8 @@
const css::uno::Reference< css::chart2::XDiagram > & xDiagram,
bool bExportContent );
void exportCustomLabel(const CustomLabelSeq & xCustomLabel);
void exportRegressionCurve(
const css::uno::Reference<css::chart2::XDataSeries>& xSeries,
const css::awt::Size& rPageSize,
@@ -287,31 +291,26 @@
namespace
{
OUString lcl_getCustomLabelField(sal_Int32 nDataPointIndex,
CustomLabelSeq lcl_getCustomLabelField(sal_Int32 nDataPointIndex,
const uno::Reference< chart2::XDataSeries >& rSeries)
{
if( !rSeries.is() )
return OUString{};
return CustomLabelSeq();
const SvtSaveOptions::ODFDefaultVersion nCurrentODFVersion( SvtSaveOptions().GetODFDefaultVersion() );
if( nCurrentODFVersion <= SvtSaveOptions::ODFVER_012 )//do not export to ODF 1.2 or older
return OUString{};
return CustomLabelSeq();
// export custom label text
if(Reference<beans::XPropertySet> xLabels = rSeries->getDataPointByIndex(nDataPointIndex); xLabels.is())
{
if(Any aAny = xLabels->getPropertyValue("CustomLabelFields"); aAny.hasValue())
{
Sequence<uno::Reference<chart2::XDataPointCustomLabelField>> aCustomLabels;
aAny >>= aCustomLabels;
OUString sLabel;
// TODO export formatted string instead of simple characters
for(auto& aLabel : aCustomLabels)
sLabel += aLabel->getString();
return sLabel;
return aCustomLabels;
}
}
return OUString{};
return CustomLabelSeq();
}
class lcl_MatchesRole
@@ -3321,7 +3320,7 @@
SchXMLDataPointStruct aPoint;
aPoint.maStyleName = maAutoStyleNameQueue.front();
if(bExportNumFmt)
aPoint.msCustomLabelText = lcl_getCustomLabelField(nElement, xSeries);
aPoint.mCustomLabelText = lcl_getCustomLabelField(nElement, xSeries);
maAutoStyleNameQueue.pop();
aDataPointVector.push_back( aPoint );
}
@@ -3350,7 +3349,7 @@
{
SchXMLDataPointStruct aPoint;
aPoint.mnRepeat = nCurrIndex - nLastIndex - 1;
aPoint.msCustomLabelText = lcl_getCustomLabelField(nCurrIndex, xSeries);
aPoint.mCustomLabelText = lcl_getCustomLabelField(nCurrIndex, xSeries);
aDataPointVector.push_back( aPoint );
}
@@ -3383,7 +3382,7 @@
SAL_WARN_IF( maAutoStyleNameQueue.empty(), "xmloff.chart", "Autostyle queue empty!" );
SchXMLDataPointStruct aPoint;
aPoint.maStyleName = maAutoStyleNameQueue.front();
aPoint.msCustomLabelText = lcl_getCustomLabelField(nCurrIndex, xSeries);
aPoint.mCustomLabelText = lcl_getCustomLabelField(nCurrIndex, xSeries);
maAutoStyleNameQueue.pop();
aDataPointVector.push_back( aPoint );
@@ -3399,7 +3398,7 @@
// if we get here the property states are empty
SchXMLDataPointStruct aPoint;
aPoint.msCustomLabelText = lcl_getCustomLabelField(nCurrIndex, xSeries);
aPoint.mCustomLabelText = lcl_getCustomLabelField(nCurrIndex, xSeries);
aDataPointVector.push_back( aPoint );
nLastIndex = nCurrIndex;
@@ -3429,13 +3428,10 @@
{
aPoint = rPoint;
if( aPoint.maStyleName == aLastPoint.maStyleName && aPoint.msCustomLabelText.isEmpty() )
if( aPoint.maStyleName == aLastPoint.maStyleName && aLastPoint.mCustomLabelText.getLength() < 1 )
aPoint.mnRepeat += aLastPoint.mnRepeat;
else if( aLastPoint.mnRepeat > 0 )
{
// export custom label text
if(!aLastPoint.msCustomLabelText.isEmpty())
mrExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_CUSTOM_LABEL_FIELD, aLastPoint.msCustomLabelText);
// write last element
if( !aLastPoint.maStyleName.isEmpty() )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aLastPoint.maStyleName );
@@ -3445,16 +3441,13 @@
OUString::number( ( aLastPoint.mnRepeat ) ));
SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, true, true );
exportCustomLabel(aLastPoint.mCustomLabelText);
}
aLastPoint = aPoint;
}
// write last element if it hasn't been written in last iteration
if( aPoint.maStyleName == aLastPoint.maStyleName )
{
// export custom label text
if(!aLastPoint.msCustomLabelText.isEmpty())
mrExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_CUSTOM_LABEL_FIELD, aLastPoint.msCustomLabelText);
if( !aLastPoint.maStyleName.isEmpty() )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aLastPoint.maStyleName );
@@ -3463,6 +3456,22 @@
OUString::number( ( aLastPoint.mnRepeat ) ));
SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, true, true );
exportCustomLabel(aLastPoint.mCustomLabelText);
}
}
void SchXMLExportHelper_Impl::exportCustomLabel( const CustomLabelSeq & xCustomLabel )
{
if( xCustomLabel.getLength() < 1 )
return; // nothing to export
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 )
{
// TODO add style
SvXMLElementExport aSpan( mrExport, XML_NAMESPACE_TEXT, XML_SPAN, true, false);
mrExport.GetDocHandler()->characters(label->getString());
}
}
diff --git a/xmloff/source/chart/SchXMLImport.cxx b/xmloff/source/chart/SchXMLImport.cxx
index bc3219e..62e2c17 100644
--- a/xmloff/source/chart/SchXMLImport.cxx
+++ b/xmloff/source/chart/SchXMLImport.cxx
@@ -228,6 +228,7 @@
{ XML_NAMESPACE_CHART, XML_REGRESSION_CURVE, XML_TOK_SERIES_REGRESSION_CURVE },
{ XML_NAMESPACE_CHART, XML_ERROR_INDICATOR, XML_TOK_SERIES_ERROR_INDICATOR },
{ XML_NAMESPACE_LO_EXT, XML_PROPERTY_MAPPING, XML_TOK_SERIES_PROPERTY_MAPPING },
{ XML_NAMESPACE_CHART, XML_DATA_LABEL, XML_TOK_SERIES_DATA_LABEL },
XML_TOKEN_MAP_END
};
diff --git a/xmloff/source/chart/SchXMLPlotAreaContext.cxx b/xmloff/source/chart/SchXMLPlotAreaContext.cxx
index f94d3de..385b59d 100644
--- a/xmloff/source/chart/SchXMLPlotAreaContext.cxx
+++ b/xmloff/source/chart/SchXMLPlotAreaContext.cxx
@@ -596,17 +596,87 @@
SchXMLAxisContext::CorrectAxisPositions( uno::Reference< chart2::XChartDocument >( mrImportHelper.GetChartDocument(), uno::UNO_QUERY ), maChartTypeServiceName, GetImport().GetODFVersion(), m_bAxisPositionAttributeImported );
}
SchXMLDataPointContext::SchXMLDataPointContext( SvXMLImport& rImport, const OUString& rLocalName,
SchXMLDataLabelSpanContext::SchXMLDataLabelSpanContext( SvXMLImport& rImport, const OUString& rLocalName, ::std::vector<OUString>& rLabels):
SvXMLImportContext( rImport, XML_NAMESPACE_TEXT, rLocalName),
mrLabels(rLabels)
{
}
void SchXMLDataLabelSpanContext::Characters(const OUString& sChars)
{
mrLabels.push_back(sChars);
}
SchXMLDataLabelParaContext::SchXMLDataLabelParaContext( SvXMLImport& rImport, const OUString& rLocalName, ::std::vector<OUString>& rLabels):
SvXMLImportContext( rImport, XML_NAMESPACE_TEXT, rLocalName),
mrLabels(rLabels)
{
}
SvXMLImportContextRef SchXMLDataLabelParaContext::CreateChildContext(
sal_uInt16 nPrefix,
const OUString& rLocalName,
const uno::Reference< xml::sax::XAttributeList >& /*xAttrList*/ )
{
SvXMLImportContextRef xContext;
if ( IsXMLToken( rLocalName, XML_SPAN ) && nPrefix == XML_NAMESPACE_TEXT )
xContext = new SchXMLDataLabelSpanContext(GetImport(), rLocalName, mrLabels);
return xContext;
}
SchXMLDataLabelContext::SchXMLDataLabelContext( SvXMLImport& rImport, const OUString& rLocalName, ::std::vector<OUString>& rLabels):
SvXMLImportContext( rImport, XML_NAMESPACE_CHART, rLocalName),
mrLabels(rLabels)
{
}
SvXMLImportContextRef SchXMLDataLabelContext::CreateChildContext(
sal_uInt16 nPrefix,
const OUString& rLocalName,
const uno::Reference< xml::sax::XAttributeList >& xAttrList )
{
SvXMLImportContextRef xContext;
if ( IsXMLToken( rLocalName, XML_P ) && nPrefix == XML_NAMESPACE_TEXT )
xContext = new SchXMLDataLabelParaContext(GetImport(), rLocalName, mrLabels);
if (!xContext)
xContext = SvXMLImportContext::CreateChildContext( nPrefix, rLocalName, xAttrList );
return xContext;
}
SchXMLDataPointContext::SchXMLDataPointContext( SchXMLImportHelper& rImportHelper,
SvXMLImport& rImport, const OUString& rLocalName,
::std::vector< DataRowPointStyle >& rStyleVector,
const css::uno::Reference< css::chart2::XDataSeries >& xSeries,
sal_Int32& rIndex,
bool bSymbolSizeForSeriesIsMissingInFile ) :
SvXMLImportContext( rImport, XML_NAMESPACE_CHART, rLocalName ),
mrImportHelper( rImportHelper ),
mrStyleVector( rStyleVector ),
m_xSeries( xSeries ),
mrIndex( rIndex ),
mbSymbolSizeForSeriesIsMissingInFile( bSymbolSizeForSeriesIsMissingInFile )
mDataPoint(DataRowPointStyle::DATA_POINT, xSeries, rIndex, 1, OUString{})
{
mDataPoint.mbSymbolSizeForSeriesIsMissingInFile = bSymbolSizeForSeriesIsMissingInFile;
}
SvXMLImportContextRef SchXMLDataPointContext::CreateChildContext(
sal_uInt16 nPrefix,
const OUString& rLocalName,
const uno::Reference< xml::sax::XAttributeList >& /*xAttrList*/ )
{
SvXMLImportContext* pContext = nullptr;
const SvXMLTokenMap& rTokenMap = mrImportHelper.GetSeriesElemTokenMap();
switch( rTokenMap.Get( nPrefix, rLocalName ))
{
case XML_TOK_SERIES_DATA_LABEL:
mbHasLabelParagraph = true;
pContext = new SchXMLDataLabelContext( GetImport(), rLocalName, mDataPoint.mCustomLabels);
break;
}
return pContext;
}
SchXMLDataPointContext::~SchXMLDataPointContext()
@@ -629,31 +699,37 @@
if( nPrefix == XML_NAMESPACE_CHART )
{
if( IsXMLToken( aLocalName, XML_STYLE_NAME ) )
{
sAutoStyleName = xAttrList->getValueByIndex( i );
mDataPoint.msStyleName = sAutoStyleName;
}
else if( IsXMLToken( aLocalName, XML_REPEATED ) )
{
nRepeat = xAttrList->getValueByIndex( i ).toInt32();
mDataPoint.m_nPointRepeat = nRepeat;
}
}
else if( nPrefix == XML_NAMESPACE_LO_EXT)
{
if( IsXMLToken( aLocalName, XML_CUSTOM_LABEL_FIELD))
if( IsXMLToken( aLocalName, XML_CUSTOM_LABEL_FIELD) && !mbHasLabelParagraph)
{
sCustomLabelField = xAttrList->getValueByIndex( i );
mDataPoint.mCustomLabels.push_back(sCustomLabelField);
}
}
}
if( !sAutoStyleName.isEmpty())
{
DataRowPointStyle aStyle(
DataRowPointStyle::DATA_POINT,
m_xSeries, mrIndex, nRepeat, sAutoStyleName );
aStyle.mbSymbolSizeForSeriesIsMissingInFile = mbSymbolSizeForSeriesIsMissingInFile;
aStyle.msCustomLabelField = sCustomLabelField;
mrStyleVector.push_back( aStyle );
}
mrIndex += nRepeat;
}
void SchXMLDataPointContext::EndElement()
{
if( !mDataPoint.msStyleName.isEmpty() || mDataPoint.mCustomLabels.size() > 0)
{
mrStyleVector.push_back( mDataPoint );
}
}
SchXMLPositionAttributesHelper::SchXMLPositionAttributesHelper( SvXMLImport& rImporter )
: m_rImport( rImporter )
, m_aPosition(0,0)
diff --git a/xmloff/source/chart/SchXMLPlotAreaContext.hxx b/xmloff/source/chart/SchXMLPlotAreaContext.hxx
index a357231..4e8a20d 100644
--- a/xmloff/source/chart/SchXMLPlotAreaContext.hxx
+++ b/xmloff/source/chart/SchXMLPlotAreaContext.hxx
@@ -136,16 +136,51 @@
css::awt::Size const maChartSize;
};
class SchXMLDataLabelSpanContext: public SvXMLImportContext
{
private:
::std::vector<OUString>& mrLabels;
public:
SchXMLDataLabelSpanContext( SvXMLImport& rImport, const OUString& rLocalName, ::std::vector<OUString>& rLabels);
virtual void Characters( const OUString& rChars ) override;
};
class SchXMLDataLabelParaContext: public SvXMLImportContext
{
private:
::std::vector<OUString>& mrLabels;
public:
SchXMLDataLabelParaContext( SvXMLImport& rImport, const OUString& rLocalName, ::std::vector<OUString>& rLabels);
virtual SvXMLImportContextRef CreateChildContext(
sal_uInt16 nPrefix,
const OUString& rLocalName,
const css::uno::Reference< css::xml::sax::XAttributeList >& xAttrList ) override;
};
class SchXMLDataLabelContext: public SvXMLImportContext
{
private:
::std::vector<OUString>& mrLabels;
public:
SchXMLDataLabelContext( SvXMLImport& rImport, const OUString& rLocalName, ::std::vector<OUString>& rLabels);
virtual SvXMLImportContextRef CreateChildContext(
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;
css::uno::Reference< css::chart2::XDataSeries > m_xSeries;
bool mbHasLabelParagraph = false;
sal_Int32& mrIndex;
bool const mbSymbolSizeForSeriesIsMissingInFile;
DataRowPointStyle mDataPoint;
public:
SchXMLDataPointContext( SvXMLImport& rImport, const OUString& rLocalName,
SchXMLDataPointContext( SchXMLImportHelper& rImportHelper,
SvXMLImport& rImport, const OUString& rLocalName,
::std::vector< DataRowPointStyle >& rStyleVector,
const css::uno::Reference< css::chart2::XDataSeries >& xSeries,
sal_Int32& rIndex,
@@ -153,6 +188,12 @@
virtual ~SchXMLDataPointContext() override;
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;
virtual void EndElement() override;
};
class SchXMLCoordinateRegionContext : public SvXMLImportContext
diff --git a/xmloff/source/chart/SchXMLSeries2Context.cxx b/xmloff/source/chart/SchXMLSeries2Context.cxx
index ba4379f..6f718f0 100644
--- a/xmloff/source/chart/SchXMLSeries2Context.cxx
+++ b/xmloff/source/chart/SchXMLSeries2Context.cxx
@@ -693,7 +693,7 @@
break;
case XML_TOK_SERIES_DATA_POINT:
pContext = new SchXMLDataPointContext( GetImport(), rLocalName,
pContext = new SchXMLDataPointContext( mrImportHelper, GetImport(), rLocalName,
mrStyleVector, m_xSeries, mnDataPointIndex, mbSymbolSizeIsMissingInFile );
break;
case XML_TOK_SERIES_PROPERTY_MAPPING:
@@ -1095,14 +1095,18 @@
lcl_resetSymbolSizeForPointsIfNecessary( xPointProp, rImport, pPropStyleContext, pStylesCtxt );
}
if(!seriesStyle.msCustomLabelField.isEmpty())
// Custom labels might be passed as property
if(auto nLabelCount = seriesStyle.mCustomLabels.size(); nLabelCount > 0)
{
Sequence< Reference<chart2::XDataPointCustomLabelField>> xLabels(1);
Sequence< Reference<chart2::XDataPointCustomLabelField>> xLabels(nLabelCount);
Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
Reference< chart2::XDataPointCustomLabelField > xCustomLabel = chart2::DataPointCustomLabelField::create(xContext);
xLabels[0] = xCustomLabel;
xCustomLabel->setString(seriesStyle.msCustomLabelField);
xCustomLabel->setFieldType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT);
for( auto j = 0; j< xLabels.getLength(); ++j )
{
Reference< chart2::XDataPointCustomLabelField > xCustomLabel = chart2::DataPointCustomLabelField::create(xContext);
xLabels[j] = xCustomLabel;
xCustomLabel->setString(seriesStyle.mCustomLabels[j]);
xCustomLabel->setFieldType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT);
}
xPointProp->setPropertyValue("CustomLabelFields", uno::Any(xLabels));
}
}
diff --git a/xmloff/source/chart/transporttypes.hxx b/xmloff/source/chart/transporttypes.hxx
index 6481cfb..04fbe70 100644
--- a/xmloff/source/chart/transporttypes.hxx
+++ b/xmloff/source/chart/transporttypes.hxx
@@ -145,6 +145,10 @@
{}
};
struct CustomLabelField {
std::vector<OUString> sRuns;
};
struct DataRowPointStyle
{
enum StyleType
@@ -167,8 +171,9 @@
sal_Int32 m_nPointIndex;
sal_Int32 m_nPointRepeat;
OUString msStyleName;
::std::vector<OUString> mCustomLabels;
OUString msSeriesStyleNameForDonuts;
OUString msCustomLabelField;
sal_Int32 mnAttachedAxis;
bool mbSymbolSizeForSeriesIsMissingInFile;
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index deaf010..8a07ad5 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -549,6 +549,7 @@
TOKEN( "data-bar", XML_DATA_BAR ),
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-number", XML_DATA_LABEL_NUMBER ),
TOKEN( "data-label-symbol", XML_DATA_LABEL_SYMBOL ),
TOKEN( "data-label-text", XML_DATA_LABEL_TEXT ),
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index 0441c34..c1d021e 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -472,6 +472,7 @@
data-bar
data-bar-entry
data-cell-range-address
data-label
data-label-number
data-label-symbol
data-label-text