/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice :
*
* Licensed to the Apache Software Foundation ( ASF ) under one or more
* contributor license agreements . See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership . The ASF licenses this file to you under the Apache
* License , Version 2 . 0 ( the " License " ) ; you may not use this file
* except in compliance with the License . You may obtain a copy of
* the License at http : //www.apache.org/licenses/LICENSE-2.0 .
*/
#include <memory>
#include <sal/config.h>
#include <sal/log.hxx>
#include <sax/tools/converter.hxx>
#include <utility>
#include <xmloff/xmlprmap.hxx>
#include <SchXMLExport.hxx>
#include <XMLChartPropertySetMapper.hxx>
#include "ColorPropertySet.hxx"
#include "SchXMLTools.hxx"
#include "SchXMLEnumConverter.hxx"
#include <comphelper/processfactory.hxx>
#include <tools/globname.hxx>
#include <comphelper/classids.hxx>
#include <comphelper/sequence.hxx>
#include <xmloff/namespacemap.hxx>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/xmltoken.hxx>
#include <xmloff/families.hxx>
#include <xmloff/xmlaustp.hxx>
#include <xmloff/xmluconv.hxx>
#include <xmloff/SchXMLSeriesHelper.hxx>
#include <rtl/math.hxx>
#include <o3tl/sorted_vector.hxx>
#include <o3tl/string_view.hxx>
#include <limits>
#include <vector>
#include <algorithm>
#include <queue>
#include <iterator>
#include <numeric>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XServiceName.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/util/XRefreshable.hpp>
#include <com/sun/star/chart/XAxis.hpp>
#include <com/sun/star/chart/XAxisSupplier.hpp>
#include <com/sun/star/chart/XChartDocument.hpp>
#include <com/sun/star/chart/ChartLegendExpansion.hpp>
#include <com/sun/star/chart/ChartDataRowSource.hpp>
#include <com/sun/star/chart/ChartAxisAssign.hpp>
#include <com/sun/star/chart/ErrorBarStyle.hpp>
#include <com/sun/star/chart/DataLabelPlacement.hpp>
#include <com/sun/star/chart/TimeIncrement.hpp>
#include <com/sun/star/chart/TimeInterval.hpp>
#include <com/sun/star/chart/TimeUnit.hpp>
#include <com/sun/star/chart/X3DDisplay.hpp>
#include <com/sun/star/chart/XStatisticDisplay.hpp>
#include <com/sun/star/chart/XDiagramPositioning.hpp>
#include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
#include <com/sun/star/chart2/AxisType.hpp>
#include <com/sun/star/chart2/XChartDocument.hpp>
#include <com/sun/star/chart2/XDiagram.hpp>
#include <com/sun/star/chart2/RelativePosition.hpp>
#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
#include <com/sun/star/chart2/XChartTypeContainer.hpp>
#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
#include <com/sun/star/chart2/data/XDataSource.hpp>
#include <com/sun/star/chart2/data/XDataSink.hpp>
#include <com/sun/star/chart2/data/XDataProvider.hpp>
#include <com/sun/star/chart2/data/XDatabaseDataProvider.hpp>
#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
#include <com/sun/star/chart2/data/XRangeXMLConversion.hpp>
#include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
#include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
#include <com/sun/star/util/MeasureUnit.hpp>
#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
#include <com/sun/star/drawing/XShapes.hpp>
#include <com/sun/star/embed/Aspects.hpp>
#include <com/sun/star/embed/XVisualObject.hpp>
#include <com/sun/star/container/XChild.hpp>
#include <comphelper/diagnose_ex.hxx>
#include "MultiPropertySetHandler.hxx"
#include "PropertyMap.hxx"
using namespace com::sun::star;
using namespace ::xmloff::token;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::Any;
using ::std::vector;
namespace
{
/**
* Used to store a data point custom - label ' s fields and also the helper members that
* indicates whether this label ' s contents are sourced from a cell [ range ] and
* the address of the cell [ range ] with guid of the CELLRANGE field .
*/
struct CustomLabelData
{
CustomLabelData():
mbDataLabelsRange( false )
{
}
/// Label fields.
Sequence<Reference<chart2::XDataPointCustomLabelField>> maFields;
/// Are label's contents sourced from a cell[range] ?
bool mbDataLabelsRange;
/// cell[range] from which label's contents are sourced.
OUString maRange;
/// GUID of the CELLRANGE field.
OUString maGuid;
};
struct SchXMLDataPointStruct
{
OUString maStyleName;
sal_Int32 mnRepeat;
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.
CustomLabelData mCustomLabel; // <text:p> child element in <chart:data-label>
OUString msDataLabelStyleName; // chart:style-name attribute in <chart:data-label>
SchXMLDataPointStruct() : mnRepeat( 1 ) {}
};
}
class SchXMLExportHelper_Impl
{
public :
// first: data sequence for label, second: data sequence for values.
typedef ::std::pair< css::uno::Reference< css::chart2::data::XDataSequence >,
css::uno::Reference< css::chart2::data::XDataSequence > > tLabelValuesDataPair;
typedef ::std::vector< tLabelValuesDataPair > tDataSequenceCont;
public :
SchXMLExportHelper_Impl( SvXMLExport& rExport,
SvXMLAutoStylePoolP& rASPool );
SchXMLExportHelper_Impl(const SchXMLExportHelper_Impl&) = delete ;
SchXMLExportHelper_Impl& operator =(const SchXMLExportHelper_Impl&) = delete ;
// auto-styles
/// parse chart and collect all auto-styles used in current pool
void collectAutoStyles( css::uno::Reference< css::chart::XChartDocument > const & rChartDoc );
/// write the styles collected into the current pool as <style:style> elements
void exportAutoStyles();
/** export the <chart:chart> element corresponding to rChartDoc
if bIncludeTable is true , the chart data is exported as < table : table >
element ( inside the chart element ) .
Otherwise the external references stored in the chart document are used
for writing the corresponding attributes at series
All attributes contained in xAttrList are written at the chart element ,
which is the outer element of a chart . So these attributes can easily
be parsed again by the container
*/
void exportChart( css::uno::Reference< css::chart::XChartDocument > const & rChartDoc,
bool bIncludeTable );
const rtl::Reference<XMLPropertySetMapper>& GetPropertySetMapper() const ;
void SetChartRangeAddress( const OUString& rAddress )
{ msChartAddress = rAddress; }
void InitRangeSegmentationProperties(
const css::uno::Reference< css::chart2::XChartDocument > & xChartDoc );
static css::awt::Size getPageSize(
const css::uno::Reference< css::chart2::XChartDocument > & xChartDoc );
/** first parseDocument: collect autostyles and store names in this queue
second parseDocument : export content and use names from this queue
*/
::std::queue< OUString > maAutoStyleNameQueue;
void CollectAutoStyle(
std::vector< XMLPropertyState >&& aStates );
void CollectAutoTextStyle(
const css::uno::Reference< css::beans::XPropertySet >& xTitlePropSet );
void AddAutoStyleAttribute(
const std::vector< XMLPropertyState >& aStates );
/// if bExportContent is false the auto-styles are collected
void parseDocument( css::uno::Reference< css::chart::XChartDocument > const & rChartDoc,
bool bExportContent,
bool bIncludeTable = false );
void exportTable();
void exportPlotArea(
const css::uno::Reference< css::chart::XDiagram >& xDiagram,
const css::uno::Reference< css::chart2::XDiagram >& xNewDiagram,
const css::awt::Size & rPageSize,
bool bExportContent,
bool bIncludeTable );
void exportCoordinateRegion( const css::uno::Reference< css::chart::XDiagram >& xDiagram );
void exportAxes( const css::uno::Reference< css::chart::XDiagram > & xDiagram,
const css::uno::Reference< css::chart2::XDiagram > & xNewDiagram,
bool bExportContent );
void exportAxis( enum XMLTokenEnum eDimension, enum XMLTokenEnum eAxisName,
const Reference< beans::XPropertySet >& rAxisProps, const Reference< chart2::XAxis >& rChart2Axis,
const OUString& rCategoriesRanges,
bool bHasTitle, bool bHasMajorGrid, bool bHasMinorGrid, bool bExportContent, std::u16string_view sChartType );
void exportGrid( const Reference< beans::XPropertySet >& rGridProperties, bool bMajor, bool bExportContent );
void exportDateScale( const Reference< beans::XPropertySet >& rAxisProps );
void exportAxisTitle( const Reference< beans::XPropertySet >& rTitleProps, bool bExportContent );
void exportSeries(
const css::uno::Reference< css::chart2::XDiagram > & xNewDiagram,
const css::awt::Size & rPageSize,
bool bExportContent,
bool bHasTwoYAxes );
void exportPropertyMapping(
const css::uno::Reference< css::chart2::data::XDataSource > & xSource,
const Sequence< OUString >& rSupportedMappings );
void exportCandleStickSeries(
const css::uno::Sequence<
css::uno::Reference< css::chart2::XDataSeries > > & aSeriesSeq,
const css::uno::Reference< css::chart2::XDiagram > & xDiagram,
bool bJapaneseCandleSticks,
bool bExportContent );
void exportDataPoints(
const css::uno::Reference< css::beans::XPropertySet > & xSeriesProperties,
sal_Int32 nSeriesLength,
const css::uno::Reference< css::chart2::XDiagram > & xDiagram,
bool bExportContent );
void exportCustomLabel(const SchXMLDataPointStruct& rPoint);
void exportCustomLabelPosition(const chart2::RelativePosition& xCustomLabelPosition);
void exportRegressionCurve(
const css::uno::Reference<css::chart2::XDataSeries>& xSeries,
const css::awt::Size& rPageSize,
bool bExportContent );
void exportErrorBar (
const css::uno::Reference<beans::XPropertySet> &xSeriesProp, bool bYError,
bool bExportContent );
/// add svg position as attribute for current element
void addPosition( const css::awt::Point & rPosition );
void addPosition( const css::uno::Reference< css::drawing::XShape >& xShape );
/// add svg size as attribute for current element
void addSize( const css::awt::Size & rSize, bool bIsOOoNamespace = false );
void addSize( const css::uno::Reference< css::drawing::XShape >& xShape );
/// exports a string as a paragraph element
void exportText( const OUString& rText );
void exportFormattedText( const css::uno::Reference< beans::XPropertySet >& xTitleProps );
public :
SvXMLExport& mrExport;
SvXMLAutoStylePoolP& mrAutoStylePool;
rtl::Reference< XMLPropertySetMapper > mxPropertySetMapper;
rtl::Reference< XMLChartExportPropertyMapper > mxExpPropMapper;
static constexpr OUString gsTableName = u"local-table" _ustr;
OUStringBuffer msStringBuffer;
OUString msString;
// members filled by InitRangeSegmentationProperties (retrieved from DataProvider)
bool mbHasCategoryLabels; //if the categories are only automatically generated this will be false
bool mbRowSourceColumns;
OUString msChartAddress;
css::uno::Sequence< sal_Int32 > maSequenceMapping;
OUString msCLSID;
OUString maSrcShellID;
OUString maDestShellID;
css::uno::Reference< css::drawing::XShapes > mxAdditionalShapes;
tDataSequenceCont m_aDataSequencesToExport;
OUString maCategoriesRange;
};
namespace
{
CustomLabelData lcl_getCustomLabelField(SvXMLExport const & rExport,
sal_Int32 nDataPointIndex,
const uno::Reference< chart2::XDataSeries >& rSeries)
{
if (!rSeries.is())
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 CustomLabelData();
if (Reference<beans::XPropertySet> xLabels = rSeries->getDataPointByIndex(nDataPointIndex); xLabels.is())
{
if (Any aAny = xLabels->getPropertyValue(u"CustomLabelFields" _ustr); aAny.hasValue())
{
CustomLabelData aData;
Sequence<uno::Reference<chart2::XDataPointCustomLabelField>> aCustomLabels;
aAny >>= aCustomLabels;
for (const auto & rField : aCustomLabels)
{
if (rField->getFieldType() == chart2::DataPointCustomLabelFieldType_CELLRANGE)
{
if (rField->getDataLabelsRange())
aData.mbDataLabelsRange = true ;
aData.maRange = rField->getCellRange();
aData.maGuid = rField->getGuid();
}
}
aData.maFields = std::move(aCustomLabels);
return aData;
}
}
return CustomLabelData();
}
css::chart2::RelativePosition lcl_getCustomLabelPosition(
SvXMLExport const & rExport,
sal_Int32 const nDataPointIndex,
const uno::Reference< chart2::XDataSeries >& rSeries)
{
if (!rSeries.is())
return chart2::RelativePosition();
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
rExport.getSaneDefaultVersion());
if ((nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) == 0 ) // do not export to ODF 1.3 or older
return chart2::RelativePosition();
if (Reference<beans::XPropertySet> xLabels = rSeries->getDataPointByIndex(nDataPointIndex); xLabels.is())
{
if (Any aAny = xLabels->getPropertyValue(u"CustomLabelPosition" _ustr); aAny.hasValue())
{
chart2::RelativePosition aCustomLabelPos;
aAny >>= aCustomLabelPos;
return aCustomLabelPos;
}
}
return chart2::RelativePosition();
}
class lcl_MatchesRole
{
public :
explicit lcl_MatchesRole( OUString aRole ) :
m_aRole(std::move( aRole ))
{}
bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const
{
if ( !xSeq.is() )
return false ;
Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY );
OUString aRole;
return ( xProp.is() &&
(xProp->getPropertyValue( u"Role" _ustr ) >>= aRole ) &&
m_aRole == aRole );
}
private :
OUString m_aRole;
};
Reference< chart2::data::XLabeledDataSequence > lcl_getCategories( const Reference< chart2::XDiagram > & xDiagram )
{
Reference< chart2::data::XLabeledDataSequence > xResult;
try
{
Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
xDiagram, uno::UNO_QUERY_THROW );
const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
xCooSysCnt->getCoordinateSystems());
for ( const auto & rCooSys : aCooSysSeq )
{
Reference< chart2::XCoordinateSystem > xCooSys( rCooSys );
SAL_WARN_IF( !xCooSys.is(), "xmloff.chart" , "xCooSys is NULL" );
for ( sal_Int32 nN = xCooSys->getDimension(); nN--; )
{
const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nN);
for (sal_Int32 nI=0 ; nI<=nMaxAxisIndex; ++nI)
{
Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( nN, nI );
SAL_WARN_IF( !xAxis.is(), "xmloff.chart" , "xAxis is NULL" );
if ( xAxis.is())
{
chart2::ScaleData aScaleData = xAxis->getScaleData();
if ( aScaleData.Categories.is())
{
xResult.set( aScaleData.Categories );
break ;
}
}
}
}
}
}
catch ( const uno::Exception & )
{
DBG_UNHANDLED_EXCEPTION("xmloff.chart" );
}
return xResult;
}
Reference< chart2::data::XDataSource > lcl_createDataSource(
const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aData )
{
const Reference< uno::XComponentContext >& xContext(
comphelper::getProcessComponentContext() );
Reference< chart2::data::XDataSink > xSink(
xContext->getServiceManager()->createInstanceWithContext(
u"com.sun.star.chart2.data.DataSource" _ustr, xContext ),
uno::UNO_QUERY_THROW );
xSink->setData( aData );
return Reference< chart2::data::XDataSource >( xSink, uno::UNO_QUERY );
}
Sequence< Reference< chart2::data::XLabeledDataSequence > > lcl_getAllSeriesSequences( const Reference< chart2::XChartDocument >& xChartDoc )
{
::std::vector< Reference< chart2::data::XLabeledDataSequence > > aContainer;
if ( xChartDoc.is() )
{
Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram());
::std::vector< Reference< chart2::XDataSeries > > aSeriesVector( SchXMLSeriesHelper::getDataSeriesFromDiagram( xDiagram ));
for ( const auto & rSeries : aSeriesVector )
{
Reference< chart2::data::XDataSource > xDataSource( rSeries, uno::UNO_QUERY );
if ( !xDataSource.is() )
continue ;
const uno::Sequence< Reference< chart2::data::XLabeledDataSequence > > aDataSequences( xDataSource->getDataSequences() );
aContainer.insert( aContainer.end(), aDataSequences.begin(), aDataSequences.end() );
}
}
return comphelper::containerToSequence( aContainer );
}
Reference< chart2::data::XLabeledDataSequence >
lcl_getDataSequenceByRole(
const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aLabeledSeq,
const OUString & rRole )
{
Reference< chart2::data::XLabeledDataSequence > aNoResult;
const Reference< chart2::data::XLabeledDataSequence > * pBegin = aLabeledSeq.getConstArray();
const Reference< chart2::data::XLabeledDataSequence > * pEnd = pBegin + aLabeledSeq.getLength();
const Reference< chart2::data::XLabeledDataSequence > * pMatch =
::std::find_if( pBegin, pEnd, lcl_MatchesRole( rRole ));
if ( pMatch != pEnd )
return *pMatch;
return aNoResult;
}
Reference< chart2::data::XDataSource > lcl_pressUsedDataIntoRectangularFormat( const Reference< chart2::XChartDocument >& xChartDoc, bool & rOutSourceHasCategoryLabels )
{
::std::vector< Reference< chart2::data::XLabeledDataSequence > > aLabeledSeqVector;
//categories are always the first sequence
Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram());
Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xDiagram ) );
if ( xCategories.is() )
aLabeledSeqVector.push_back( xCategories );
rOutSourceHasCategoryLabels = xCategories.is();
const Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeriesSeqVector(
lcl_getAllSeriesSequences( xChartDoc ) );
//the first x-values is always the next sequence //todo ... other x-values get lost for old format
Reference< chart2::data::XLabeledDataSequence > xXValues(
lcl_getDataSequenceByRole( aSeriesSeqVector, u"values-x" _ustr ) );
if ( xXValues.is() )
aLabeledSeqVector.push_back( xXValues );
//add all other sequences now without x-values
lcl_MatchesRole aHasXValues( u"values-x" _ustr );
std::copy_if(aSeriesSeqVector.begin(), aSeriesSeqVector.end(), std::back_inserter(aLabeledSeqVector),
[&aHasXValues](const auto & rSeriesSeq) { return !aHasXValues( rSeriesSeq ); });
Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeq( comphelper::containerToSequence(aLabeledSeqVector) );
return lcl_createDataSource( aSeq );
}
bool lcl_isSeriesAttachedToFirstAxis(
const Reference< chart2::XDataSeries > & xDataSeries )
{
bool bResult=true ;
try
{
sal_Int32 nAxisIndex = 0 ;
Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW );
xProp->getPropertyValue(u"AttachedAxisIndex" _ustr) >>= nAxisIndex;
bResult = (0 ==nAxisIndex);
}
catch ( const uno::Exception & )
{
DBG_UNHANDLED_EXCEPTION("xmloff.chart" );
}
return bResult;
}
OUString lcl_ConvertRange( const OUString & rRange, const Reference< chart2::XChartDocument > & xDoc )
{
OUString aResult = rRange;
if ( !xDoc.is() )
return aResult;
Reference< chart2::data::XRangeXMLConversion > xConversion(
xDoc->getDataProvider(), uno::UNO_QUERY );
if ( xConversion.is())
aResult = xConversion->convertRangeToXML( rRange );
return aResult;
}
typedef ::std::pair< OUString, OUString > tLabelAndValueRange;
tLabelAndValueRange lcl_getLabelAndValueRangeByRole(
const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aSeqCnt,
const OUString & rRole,
const Reference< chart2::XChartDocument > & xDoc,
SchXMLExportHelper_Impl::tDataSequenceCont & rOutSequencesToExport )
{
tLabelAndValueRange aResult;
Reference< chart2::data::XLabeledDataSequence > xLabeledSeq(
lcl_getDataSequenceByRole( aSeqCnt, rRole ));
if ( xLabeledSeq.is())
{
Reference< chart2::data::XDataSequence > xLabelSeq( xLabeledSeq->getLabel());
if ( xLabelSeq.is())
aResult.first = lcl_ConvertRange( xLabelSeq->getSourceRangeRepresentation(), xDoc );
Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues());
if ( xValueSeq.is())
aResult.second = lcl_ConvertRange( xValueSeq->getSourceRangeRepresentation(), xDoc );
if ( xLabelSeq.is() || xValueSeq.is())
rOutSequencesToExport.emplace_back( xLabelSeq, xValueSeq );
}
return aResult;
}
sal_Int32 lcl_getSequenceLengthByRole(
const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aSeqCnt,
const OUString & rRole )
{
Reference< chart2::data::XLabeledDataSequence > xLabeledSeq(
lcl_getDataSequenceByRole( aSeqCnt, rRole ));
if ( xLabeledSeq.is())
{
Reference< chart2::data::XDataSequence > xSeq( xLabeledSeq->getValues());
return xSeq->getData().getLength();
}
return 0 ;
}
OUString lcl_flattenStringSequence( const Sequence< OUString > & rSequence )
{
OUStringBuffer aResult;
bool bPrecedeWithSpace = false ;
for ( const auto & rString : rSequence )
{
if ( !rString.isEmpty())
{
if ( bPrecedeWithSpace )
aResult.append( ' ' );
aResult.append( rString );
bPrecedeWithSpace = true ;
}
}
return aResult.makeStringAndClear();
}
void lcl_getLabelStringSequence( Sequence< OUString >& rOutLabels, const Reference< chart2::data::XDataSequence > & xLabelSeq )
{
uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xLabelSeq, uno::UNO_QUERY );
if ( xTextualDataSequence.is())
{
rOutLabels = xTextualDataSequence->getTextualData();
}
else if ( xLabelSeq.is())
{
Sequence< uno::Any > aAnies( xLabelSeq->getData());
rOutLabels.realloc( aAnies.getLength());
auto pOutLabels = rOutLabels.getArray();
for ( sal_Int32 i=0 ; i<aAnies.getLength(); ++i )
aAnies[i] >>= pOutLabels[i];
}
}
sal_Int32 lcl_getMaxSequenceLength(
const SchXMLExportHelper_Impl::tDataSequenceCont & rContainer )
{
sal_Int32 nResult = 0 ;
for ( const auto & rDataSequence : rContainer )
{
if ( rDataSequence.second.is())
{
sal_Int32 nSeqLength = rDataSequence.second->getData().getLength();
if ( nSeqLength > nResult )
nResult = nSeqLength;
}
}
return nResult;
}
uno::Sequence< OUString > lcl_DataSequenceToStringSequence(
const uno::Reference< chart2::data::XDataSequence >& xDataSequence )
{
uno::Sequence< OUString > aResult;
if (!xDataSequence.is())
return aResult;
uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xDataSequence, uno::UNO_QUERY );
if ( xTextualDataSequence.is() )
{
aResult = xTextualDataSequence->getTextualData();
}
else
{
uno::Sequence< uno::Any > aValues = xDataSequence->getData();
aResult.realloc(aValues.getLength());
auto pResult = aResult.getArray();
for (sal_Int32 nN=aValues.getLength();nN--;)
aValues[nN] >>= pResult[nN];
}
return aResult;
}
::std::vector< double > lcl_getAllValuesFromSequence( const Reference< chart2::data::XDataSequence > & xSeq )
{
::std::vector< double > aResult;
if (!xSeq.is())
return aResult;
uno::Sequence< double > aValuesSequence;
Reference< chart2::data::XNumericalDataSequence > xNumSeq( xSeq, uno::UNO_QUERY );
if ( xNumSeq.is() )
{
aValuesSequence = xNumSeq->getNumericalData();
}
else
{
Sequence< uno::Any > aAnies( xSeq->getData() );
aValuesSequence.realloc( aAnies.getLength() );
auto pValuesSequence = aValuesSequence.getArray();
for ( sal_Int32 i=0 ; i<aAnies.getLength(); ++i )
aAnies[i] >>= pValuesSequence[i];
}
//special handling for x-values (if x-values do point to categories, indices are used instead )
Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY );
if ( xProp.is() )
{
OUString aRole;
xProp->getPropertyValue(u"Role" _ustr) >>= aRole;
if ( aRole.match("values-x" ) )
{
//lcl_clearIfNoValuesButTextIsContained - replace by indices if the values are not appropriate
bool bHasValue = std::any_of(std::cbegin(aValuesSequence), std::cend(aValuesSequence),
[](double fValue) { return !std::isnan( fValue ); });
if (!bHasValue)
{
//no double value is contained
//is there any text?
const uno::Sequence< OUString > aStrings( lcl_DataSequenceToStringSequence( xSeq ) );
bool bHasText = std::any_of(aStrings.begin(), aStrings.end(),
[](const OUString& rString) { return !rString.isEmpty(); });
if ( bHasText )
{
auto [begin, end] = asNonConstRange(aValuesSequence);
std::iota(begin, end, 1 );
}
}
}
}
aResult.insert( aResult.end(), std::cbegin(aValuesSequence), std::cend(aValuesSequence) );
return aResult;
}
bool lcl_SequenceHasUnhiddenData( const uno::Reference< chart2::data::XDataSequence >& xDataSequence )
{
if ( !xDataSequence.is() )
return false ;
uno::Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY );
if ( xProp.is() )
{
uno::Sequence< sal_Int32 > aHiddenValues;
try
{
xProp->getPropertyValue(u"HiddenValues" _ustr) >>= aHiddenValues;
if ( !aHiddenValues.hasElements() )
return true ;
}
catch ( const uno::Exception& )
{
return true ;
}
}
return xDataSequence->getData().hasElements();
}
typedef vector< OUString > tStringVector;
typedef vector< vector< double > > t2DNumberContainer;
struct lcl_TableData
{
t2DNumberContainer aDataInRows;
tStringVector aDataRangeRepresentations;
tStringVector aColumnDescriptions;
tStringVector aColumnDescriptions_Ranges;
tStringVector aRowDescriptions;
tStringVector aRowDescriptions_Ranges;
Sequence< Sequence< uno::Any > > aComplexColumnDescriptions;//outer index is columns - inner index is level
Sequence< Sequence< uno::Any > > aComplexRowDescriptions;//outer index is rows - inner index is level
::std::vector< sal_Int32 > aHiddenColumns;
};
typedef ::std::map< sal_Int32, SchXMLExportHelper_Impl::tLabelValuesDataPair >
lcl_DataSequenceMap;
struct lcl_SequenceToMapElement
{
std::pair<const sal_Int32, SchXMLExportHelper_Impl::tLabelValuesDataPair>
operator () (const SchXMLExportHelper_Impl::tLabelValuesDataPair& rContent)
{
sal_Int32 nIndex = -1 ;
if ( rContent.second.is()) //has values
{
OUString aRangeRep( rContent.second->getSourceRangeRepresentation());
nIndex = aRangeRep.toInt32();
}
else if ( rContent.first.is()) //has labels
nIndex = o3tl::toInt32(rContent.first->getSourceRangeRepresentation().subView( sizeof ("label " )));
return std::make_pair(nIndex, rContent);
}
};
void lcl_ReorderInternalSequencesAccordingToTheirRangeName(
SchXMLExportHelper_Impl::tDataSequenceCont & rInOutSequences )
{
lcl_DataSequenceMap aIndexSequenceMap;
::std::transform( rInOutSequences.begin(), rInOutSequences.end(),
::std::inserter( aIndexSequenceMap, aIndexSequenceMap.begin()),
lcl_SequenceToMapElement());
rInOutSequences.clear();
sal_Int32 nIndex = 0 ;
for ( const auto & rEntry : aIndexSequenceMap )
{
if ( rEntry.first >= 0 )
{
// fill empty columns
rInOutSequences.insert(
rInOutSequences.end(),
rEntry.first - nIndex,
SchXMLExportHelper_Impl::tDataSequenceCont::value_type(
uno::Reference< chart2::data::XDataSequence >(),
uno::Reference< chart2::data::XDataSequence >() ));
nIndex = rEntry.first;
rInOutSequences.push_back( rEntry.second );
}
++nIndex;
}
}
lcl_TableData lcl_getDataForLocalTable(
const SchXMLExportHelper_Impl::tDataSequenceCont & aSequencesToExport,
const Reference< chart2::XAnyDescriptionAccess >& xAnyDescriptionAccess,
const OUString& rCategoriesRange,
bool bSeriesFromColumns,
const Reference< chart2::data::XRangeXMLConversion > & xRangeConversion )
{
lcl_TableData aResult;
try
{
Sequence< OUString > aSimpleCategories;
if ( xAnyDescriptionAccess.is() )
{
//categories
if ( bSeriesFromColumns )
{
aSimpleCategories = xAnyDescriptionAccess->getRowDescriptions();
aResult.aComplexRowDescriptions = xAnyDescriptionAccess->getAnyRowDescriptions();
}
else
{
aSimpleCategories = xAnyDescriptionAccess->getColumnDescriptions();
aResult.aComplexColumnDescriptions = xAnyDescriptionAccess->getAnyColumnDescriptions();
}
}
//series values and series labels
SchXMLExportHelper_Impl::tDataSequenceCont::size_type nNumSequences = aSequencesToExport.size();
auto nMaxSequenceLength( lcl_getMaxSequenceLength( aSequencesToExport ));
if ( aSimpleCategories.getLength() > nMaxSequenceLength )
{
aSimpleCategories.realloc(nMaxSequenceLength);//#i110617#
}
size_t nNumColumns( bSeriesFromColumns ? nNumSequences : nMaxSequenceLength );
size_t nNumRows( bSeriesFromColumns ? nMaxSequenceLength : nNumSequences );
// resize data
aResult.aDataInRows.resize( nNumRows );
for (auto & aData: aResult.aDataInRows)
aData.resize(nNumColumns, std::numeric_limits<double >::quiet_NaN());
aResult.aColumnDescriptions.resize( nNumColumns );
aResult.aComplexColumnDescriptions.realloc( nNumColumns );
aResult.aRowDescriptions.resize( nNumRows );
aResult.aComplexRowDescriptions.realloc( nNumRows );
tStringVector& rCategories = bSeriesFromColumns ? aResult.aRowDescriptions : aResult.aColumnDescriptions;
tStringVector& rLabels = bSeriesFromColumns ? aResult.aColumnDescriptions : aResult.aRowDescriptions;
//categories
rCategories.clear();
rCategories.insert( rCategories.begin(), std::cbegin(aSimpleCategories), std::cend(aSimpleCategories) );
if ( !rCategoriesRange.isEmpty() )
{
OUString aRange(rCategoriesRange);
if ( xRangeConversion.is())
aRange = xRangeConversion->convertRangeToXML( aRange );
if ( bSeriesFromColumns )
aResult.aRowDescriptions_Ranges.push_back( aRange );
else
aResult.aColumnDescriptions_Ranges.push_back( aRange );
}
// iterate over all sequences
size_t nSeqIdx = 0 ;
Sequence< Sequence< OUString > > aComplexLabels(nNumSequences);
auto aComplexLabelsRange = asNonConstRange(aComplexLabels);
for ( const auto & rDataSequence : aSequencesToExport )
{
OUString aRange;
Sequence< OUString >& rCurrentComplexLabel = aComplexLabelsRange[nSeqIdx];
if ( rDataSequence.first.is())
{
lcl_getLabelStringSequence( rCurrentComplexLabel, rDataSequence.first );
rLabels[nSeqIdx] = lcl_flattenStringSequence( rCurrentComplexLabel );
aRange = rDataSequence.first->getSourceRangeRepresentation();
if ( xRangeConversion.is())
aRange = xRangeConversion->convertRangeToXML( aRange );
}
else if ( rDataSequence.second.is())
{
rCurrentComplexLabel.realloc(1 );
rLabels[nSeqIdx] = rCurrentComplexLabel.getArray()[0 ] = lcl_flattenStringSequence(
rDataSequence.second->generateLabel( chart2::data::LabelOrigin_SHORT_SIDE ));
}
if ( bSeriesFromColumns )
aResult.aColumnDescriptions_Ranges.push_back( aRange );
else
aResult.aRowDescriptions_Ranges.push_back( aRange );
::std::vector< double > aNumbers( lcl_getAllValuesFromSequence( rDataSequence.second ));
if ( bSeriesFromColumns )
{
const sal_Int32 nSize( static_cast < sal_Int32 >( aNumbers.size()));
for ( sal_Int32 nIdx=0 ; nIdx<nSize; ++nIdx )
aResult.aDataInRows[nIdx][nSeqIdx] = aNumbers[nIdx];
}
else
aResult.aDataInRows[nSeqIdx] = std::move(aNumbers);
if ( rDataSequence.second.is())
{
aRange = rDataSequence.second->getSourceRangeRepresentation();
if ( xRangeConversion.is())
aRange = xRangeConversion->convertRangeToXML( aRange );
}
aResult.aDataRangeRepresentations.push_back( aRange );
//is column hidden?
if ( !lcl_SequenceHasUnhiddenData(rDataSequence.first) && !lcl_SequenceHasUnhiddenData(rDataSequence.second) )
aResult.aHiddenColumns.push_back(nSeqIdx);
++nSeqIdx;
}
Sequence< Sequence< Any > >& rComplexAnyLabels = bSeriesFromColumns ? aResult.aComplexColumnDescriptions : aResult.aComplexRowDescriptions;//#i116544#
rComplexAnyLabels.realloc(aComplexLabels.getLength());
auto pComplexAnyLabels = rComplexAnyLabels.getArray();
for ( sal_Int32 nN=0 ; nN<aComplexLabels.getLength();nN++ )
{
Sequence< OUString >& rSource = aComplexLabelsRange[nN];
Sequence< Any >& rTarget = pComplexAnyLabels[nN];
rTarget.realloc( rSource.getLength() );
auto pTarget = rTarget.getArray();
for ( sal_Int32 i=0 ; i<rSource.getLength(); i++ )
pTarget[i] <<= rSource[i];
}
}
catch ( const uno::Exception & )
{
TOOLS_INFO_EXCEPTION("xmloff.chart" , "something went wrong during table data collection" );
}
return aResult;
}
void lcl_exportNumberFormat( const OUString& rPropertyName, const Reference< beans::XPropertySet >& xPropSet,
SvXMLExport& rExport )
{
if ( xPropSet.is())
{
sal_Int32 nNumberFormat = 0 ;
Any aNumAny = xPropSet->getPropertyValue( rPropertyName );
if ( (aNumAny >>= nNumberFormat) && (nNumberFormat != -1 ) )
rExport.addDataStyle( nNumberFormat );
}
}
::std::vector< Reference< chart2::data::XDataSequence > >
lcl_getErrorBarSequences( const Reference< beans::XPropertySet > & xErrorBarProp )
{
::std::vector< Reference< chart2::data::XDataSequence > > aResult;
Reference< chart2::data::XDataSource > xErrorBarDataSource( xErrorBarProp, uno::UNO_QUERY );
if ( !xErrorBarDataSource.is())
return aResult;
const Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences(
xErrorBarDataSource->getDataSequences());
for ( const auto & rSequence : aSequences )
{
try
{
if ( rSequence.is())
{
Reference< chart2::data::XDataSequence > xSequence( rSequence->getValues());
Reference< beans::XPropertySet > xSeqProp( xSequence, uno::UNO_QUERY_THROW );
OUString aRole;
if ( ( xSeqProp->getPropertyValue( u"Role" _ustr ) >>= aRole ) &&
aRole.match( "error-bars-" ))
{
aResult.push_back( xSequence );
}
}
}
catch ( const uno::Exception & )
{
TOOLS_INFO_EXCEPTION("xmloff.chart" , "chart:exporting error bar ranges" );
}
}
return aResult;
}
bool lcl_exportDomainForThisSequence( const Reference< chart2::data::XDataSequence >& rValues, OUString& rFirstRangeForThisDomainIndex, SvXMLExport& rExport )
{
bool bDomainExported = false ;
if ( rValues.is())
{
Reference< chart2::XChartDocument > xNewDoc( rExport.GetModel(), uno::UNO_QUERY );
OUString aRange( lcl_ConvertRange( rValues->getSourceRangeRepresentation(), xNewDoc ) );
//work around error in OOo 2.0 (problems with multiple series having a domain element)
if ( rFirstRangeForThisDomainIndex.isEmpty() || aRange != rFirstRangeForThisDomainIndex )
{
rExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, aRange);
SvXMLElementExport aDomain( rExport, XML_NAMESPACE_CHART, XML_DOMAIN, true , true );
bDomainExported = true ;
}
if ( rFirstRangeForThisDomainIndex.isEmpty() )
rFirstRangeForThisDomainIndex = aRange;
}
return bDomainExported;
}
} // anonymous namespace
SchXMLExportHelper::SchXMLExportHelper( SvXMLExport& rExport, SvXMLAutoStylePoolP& rASPool )
: m_pImpl( new SchXMLExportHelper_Impl( rExport, rASPool ) )
{
}
SchXMLExportHelper::~SchXMLExportHelper()
{
}
const OUString& SchXMLExportHelper::getChartCLSID() const
{
return m_pImpl->msCLSID;
}
void SchXMLExportHelper::SetSourceShellID( const OUString& rShellID )
{
m_pImpl->maSrcShellID = rShellID;
}
void SchXMLExportHelper::SetDestinationShellID( const OUString& rShellID )
{
m_pImpl->maDestShellID = rShellID;
}
const rtl::Reference< XMLPropertySetMapper >& SchXMLExportHelper_Impl::GetPropertySetMapper() const
{
return mxPropertySetMapper;
}
void SchXMLExportHelper_Impl::exportAutoStyles()
{
if ( !mxExpPropMapper.is())
return ;
//ToDo: when embedded in calc/writer this is not necessary because the
// numberformatter is shared between both documents
mrExport.exportAutoDataStyles();
// export chart auto styles
mrAutoStylePool.exportXML( XmlStyleFamily::SCH_CHART_ID );
// export auto styles for additional shapes
mrExport.GetShapeExport()->exportAutoStyles();
// and for text in additional shapes
mrExport.GetTextParagraphExport()->exportTextAutoStyles();
}
// private methods
SchXMLExportHelper_Impl::SchXMLExportHelper_Impl(
SvXMLExport& rExport,
SvXMLAutoStylePoolP& rASPool ) :
mrExport( rExport ),
mrAutoStylePool( rASPool ),
mxPropertySetMapper( new XMLChartPropertySetMapper(&rExport) ),
mxExpPropMapper( new XMLChartExportPropertyMapper( mxPropertySetMapper, rExport ) ),
mbHasCategoryLabels( false ),
mbRowSourceColumns( true ),
msCLSID( SvGlobalName( SO3_SCH_CLASSID ).GetHexName() )
{
// register chart auto-style family
mrAutoStylePool.AddFamily(
XmlStyleFamily::SCH_CHART_ID,
XML_STYLE_FAMILY_SCH_CHART_NAME,
mxExpPropMapper.get(),
XML_STYLE_FAMILY_SCH_CHART_PREFIX);
// register shape family
mrAutoStylePool.AddFamily(
XmlStyleFamily::SD_GRAPHICS_ID,
XML_STYLE_FAMILY_SD_GRAPHICS_NAME,
mxExpPropMapper.get(),
XML_STYLE_FAMILY_SD_GRAPHICS_PREFIX);
// register paragraph family also for shapes
mrAutoStylePool.AddFamily(
XmlStyleFamily::TEXT_PARAGRAPH,
GetXMLToken( XML_PARAGRAPH ),
mxExpPropMapper.get(),
OUString( 'P' ));
// register text family also for shapes
mrAutoStylePool.AddFamily(
XmlStyleFamily::TEXT_TEXT,
GetXMLToken( XML_TEXT ),
mxExpPropMapper.get(),
OUString( 'T' ));
}
void SchXMLExportHelper_Impl::collectAutoStyles( Reference< chart::XChartDocument > const & rChartDoc )
{
parseDocument( rChartDoc, false );
}
void SchXMLExportHelper_Impl::exportChart( Reference< chart::XChartDocument > const & rChartDoc,
bool bIncludeTable )
{
parseDocument( rChartDoc, true , bIncludeTable );
SAL_WARN_IF( !maAutoStyleNameQueue.empty(), "xmloff.chart" , "There are still remaining autostyle names in the queue" );
}
static OUString lcl_GetStringFromNumberSequence( const css::uno::Sequence< sal_Int32 >& rSequenceMapping, bool bRemoveOneFromEachIndex /*should be true if having categories*/ )
{
OUStringBuffer aBuf;
bool bHasPredecessor = false ;
for ( sal_Int32 nIndex : rSequenceMapping )
{
if ( bRemoveOneFromEachIndex )
--nIndex;
if (nIndex>=0 )
{
if (bHasPredecessor)
aBuf.append( ' ' );
aBuf.append( nIndex );
bHasPredecessor = true ;
}
}
return aBuf.makeStringAndClear();
}
/// if bExportContent is false the auto-styles are collected
void SchXMLExportHelper_Impl::parseDocument( Reference< chart::XChartDocument > const & rChartDoc,
bool bExportContent,
bool bIncludeTable )
{
Reference< chart2::XChartDocument > xNewDoc( rChartDoc, uno::UNO_QUERY );
if ( !rChartDoc.is() || !xNewDoc.is() )
{
SAL_WARN("xmloff.chart" , "No XChartDocument was given for export." );
return ;
}
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(mrExport.getSaneDefaultVersion());
mxExpPropMapper->setChartDoc(xNewDoc);
awt::Size aPageSize( getPageSize( xNewDoc ));
if ( bExportContent )
addSize( aPageSize );
Reference< chart::XDiagram > xDiagram = rChartDoc->getDiagram();
Reference< chart2::XDiagram > xNewDiagram;
if ( xNewDoc.is())
xNewDiagram.set( xNewDoc->getFirstDiagram());
//todo remove if model changes are notified and view is updated automatically
if ( bExportContent )
{
Reference< util::XRefreshable > xRefreshable( xNewDoc, uno::UNO_QUERY );
if ( xRefreshable.is() )
xRefreshable->refresh();
}
// get Properties of ChartDocument
bool bHasMainTitle = false ;
bool bHasSubTitle = false ;
bool bHasLegend = false ;
util::DateTime aNullDate(0 ,0 ,0 ,0 ,30 ,12 ,1899 , false );
std::vector< XMLPropertyState > aPropertyStates;
Reference< beans::XPropertySet > xDocPropSet( rChartDoc, uno::UNO_QUERY );
if ( xDocPropSet.is())
{
try
{
Any aAny = xDocPropSet->getPropertyValue(u"HasMainTitle" _ustr);
aAny >>= bHasMainTitle;
aAny = xDocPropSet->getPropertyValue(u"HasSubTitle" _ustr);
aAny >>= bHasSubTitle;
aAny = xDocPropSet->getPropertyValue(u"HasLegend" _ustr);
aAny >>= bHasLegend;
if ( bIncludeTable )
{
aAny = xDocPropSet->getPropertyValue(u"NullDate" _ustr);
if ( !aAny.hasValue() )
{
Reference<container::XChild> xChild(rChartDoc, uno::UNO_QUERY );
if ( xChild.is() )
{
Reference< beans::XPropertySet > xParentDoc( xChild->getParent(),uno::UNO_QUERY);
if ( xParentDoc.is() && xParentDoc->getPropertySetInfo()->hasPropertyByName(u"NullDate" _ustr) )
aAny = xParentDoc->getPropertyValue(u"NullDate" _ustr);
}
}
aAny >>= aNullDate;
}
}
catch ( const beans::UnknownPropertyException & )
{
SAL_WARN("xmloff.chart" , "Required property not found in ChartDocument" );
}
}
if ( bIncludeTable && (aNullDate.Day != 30 || aNullDate.Month != 12 || aNullDate.Year != 1899 ) )
{
SvXMLElementExport aSet( mrExport, XML_NAMESPACE_TABLE, XML_CALCULATION_SETTINGS, true , true );
{
OUStringBuffer sBuffer;
::sax::Converter::convertDateTime(sBuffer, aNullDate, nullptr);
mrExport.AddAttribute( XML_NAMESPACE_TABLE,XML_DATE_VALUE,sBuffer.makeStringAndClear());
SvXMLElementExport aNull( mrExport, XML_NAMESPACE_TABLE, XML_NULL_DATE, true , true );
}
}
// chart element
std::unique_ptr<SvXMLElementExport> xElChart;
// get property states for autostyles
if ( mxExpPropMapper.is())
{
Reference< beans::XPropertySet > xPropSet = rChartDoc->getArea();
if ( xPropSet.is())
aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
}
if ( bExportContent )
{
//export data provider in xlink:href attribute
if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
{
OUString aDataProviderURL( u".." _ustr );
if ( xNewDoc->hasInternalDataProvider() )
aDataProviderURL = "." ;
else //special handling for data base data provider necessary
{
Reference< chart2::data::XDatabaseDataProvider > xDBDataProvider( xNewDoc->getDataProvider(), uno::UNO_QUERY );
if ( xDBDataProvider.is() )
aDataProviderURL = "." ;
}
mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, aDataProviderURL );
mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE );
}
Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xNewDoc->getDataProvider(), uno::UNO_QUERY);
if (xPivotTableDataProvider.is() && nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED)
{
OUString sPivotTableName = xPivotTableDataProvider->getPivotTableName();
mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_PILOT_SOURCE, sPivotTableName);
}
OUString sChartType( xDiagram->getDiagramType() );
// attributes
// determine class
if ( !sChartType.isEmpty())
{
enum XMLTokenEnum eXMLChartType = SchXMLTools::getTokenByChartType( sChartType, true /* bUseOldNames */ );
SAL_WARN_IF( eXMLChartType == XML_TOKEN_INVALID, "xmloff.chart" , "invalid chart class" );
if ( eXMLChartType == XML_TOKEN_INVALID )
eXMLChartType = XML_BAR;
if ( eXMLChartType == XML_ADD_IN )
{
// sChartType is the service-name of the add-in
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS,
mrExport.GetNamespaceMap().GetQNameByKey(
XML_NAMESPACE_OOO, sChartType) );
}
else if ( eXMLChartType != XML_TOKEN_INVALID )
{
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS,
mrExport.GetNamespaceMap().GetQNameByKey(
XML_NAMESPACE_CHART, GetXMLToken(eXMLChartType )) );
}
bool bIsOfPie = false ;
// Handle subtype for of-pie charts
if (sChartType == u"com.sun.star.chart.BarOfPieDiagram" ) {
mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_SUB_BAR, OUString::boolean(true ));
bIsOfPie = true ;
} else if (sChartType == u"com.sun.star.chart.PieOfPieDiagram" ) {
mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_SUB_PIE, OUString::boolean(true ));
bIsOfPie = true ;
}
if (bIsOfPie) {
// Find the split position. We have to dig deep into the
// structure tree to get it, which is awkward. Part of the
// problem is that the split position is sort of a series-level
// parameter, but is generally handled at the chart level since
// of-pie charts have only a single series.
double fSplitPos = 2 .0 ;
Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( xNewDiagram, uno::UNO_QUERY );
if (xBCooSysCnt.is()) {
const Sequence< Reference< chart2::XCoordinateSystem > >
aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
for (const auto & rCooSys : aCooSysSeq ) {
Reference< chart2::XChartTypeContainer > xCTCnt( rCooSys, uno::UNO_QUERY );
if ( ! xCTCnt.is())
continue ;
const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
for (const auto & rChartType : aCTSeq ) {
Reference< beans::XPropertySet > xCTProp( rChartType, uno::UNO_QUERY );
if (xCTProp.is()) {
xCTProp->getPropertyValue(u"SplitPos" _ustr) >>= fSplitPos;
}
}
}
}
// Insert split position for of-pie chart
mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_SPLIT_POSITION,
OUString::number(fSplitPos));
}
// The attribute table:cell-range-address was removed from the standard in ODF 1.4.
// The associated attributes chart:column-mapping and chart:row-mapping are deprecated.
//column-mapping or row-mapping
if ( maSequenceMapping.hasElements() && nCurrentODFVersion < SvtSaveOptions::ODFSVER_014)
{
enum XMLTokenEnum eTransToken = ::xmloff::token::XML_ROW_MAPPING;
if ( mbRowSourceColumns )
eTransToken = ::xmloff::token::XML_COLUMN_MAPPING;
OUString aSequenceMappingStr( lcl_GetStringFromNumberSequence(
maSequenceMapping, mbHasCategoryLabels && !xNewDoc->hasInternalDataProvider() ) );
mrExport.AddAttribute( XML_NAMESPACE_CHART,
::xmloff::token::GetXMLToken( eTransToken ),
aSequenceMappingStr );
}
}
// write style name
AddAutoStyleAttribute( aPropertyStates );
//element
xElChart.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_CHART, true , true ));
}
else // autostyles
{
CollectAutoStyle( std::move(aPropertyStates) );
}
// remove property states for autostyles
aPropertyStates.clear();
// title element
if ( bHasMainTitle )
{
// get property states for autostyles
Reference< drawing::XShape > xShape = rChartDoc->getTitle();
Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY);
if ( mxExpPropMapper.is() && xPropSet.is())
{
aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
}
if ( bExportContent )
{
if ( xShape.is()) // && "hasTitleBeenMoved"
addPosition( xShape );
// write style name
AddAutoStyleAttribute( aPropertyStates );
// element
SvXMLElementExport aElTitle( mrExport, XML_NAMESPACE_CHART, XML_TITLE, true , true );
// content (text:p)
exportFormattedText(xPropSet);
}
else // autostyles
{
CollectAutoStyle( std::move(aPropertyStates) );
CollectAutoTextStyle( xPropSet );
}
// remove property states for autostyles
aPropertyStates.clear();
}
// subtitle element
if ( bHasSubTitle )
{
// get property states for autostyles
Reference< drawing::XShape > xShape = rChartDoc->getSubTitle();
Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY);
if ( mxExpPropMapper.is() && xPropSet.is())
{
aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
}
if ( bExportContent )
{
if ( xShape.is())
addPosition( xShape );
// write style name
AddAutoStyleAttribute( aPropertyStates );
// element (has no subelements)
SvXMLElementExport aElSubTitle( mrExport, XML_NAMESPACE_CHART, XML_SUBTITLE, true , true );
// content (text:p)
exportFormattedText(xPropSet);
}
else // autostyles
{
CollectAutoStyle( std::move(aPropertyStates) );
CollectAutoTextStyle(xPropSet);
}
// remove property states for autostyles
aPropertyStates.clear();
}
// legend element
if ( bHasLegend )
{
// get property states for autostyles
if ( mxExpPropMapper.is())
{
Reference< beans::XPropertySet > xPropSet( rChartDoc->getLegend(), uno::UNO_QUERY );
if ( xPropSet.is())
aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
}
if ( bExportContent )
{
Reference< beans::XPropertySet > xProp( rChartDoc->getLegend(), uno::UNO_QUERY );
if ( xProp.is())
{
// export legend anchor position
try
{
Any aAny( xProp->getPropertyValue(u"Alignment" _ustr));
if ( SchXMLEnumConverter::getLegendPositionConverter().exportXML( msString, aAny, mrExport.GetMM100UnitConverter() ) )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LEGEND_POSITION, msString );
}
catch ( const beans::UnknownPropertyException & )
{
SAL_WARN("xmloff.chart" , "Property Align not found in ChartLegend" );
}
// export legend overlay
try
{
if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED)
{
Any aAny( xProp->getPropertyValue(u"Overlay" _ustr));
if (aAny.get<bool >())
mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_OVERLAY, OUString::boolean(true ));
}
}
catch ( const beans::UnknownPropertyException & )
{
SAL_WARN("xmloff.chart" , "Property Overlay not found in ChartLegend" );
}
// export absolute legend position
Reference< drawing::XShape > xLegendShape( xProp, uno::UNO_QUERY );
addPosition( xLegendShape );
// export legend size
if (xLegendShape.is() && nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
{
try
{
chart::ChartLegendExpansion nLegendExpansion = chart::ChartLegendExpansion_HIGH;
OUString aExpansionString;
Any aAny( xProp->getPropertyValue(u"Expansion" _ustr));
bool bHasExpansion = (aAny >>= nLegendExpansion);
if ( bHasExpansion && SchXMLEnumConverter::getLegendExpansionConverter().exportXML( aExpansionString, aAny, mrExport.GetMM100UnitConverter() ) )
{
mrExport.AddAttribute( XML_NAMESPACE_STYLE, XML_LEGEND_EXPANSION, aExpansionString );
if ( nLegendExpansion == chart::ChartLegendExpansion_CUSTOM)
{
awt::Size aSize( xLegendShape->getSize() );
// tdf#131966: chart legend attributes width and height shouldn't be exported to ODF 1.2 (strict)
if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_013)
{ // ODF 1.3 OFFICE-3883
addSize( aSize, false );
}
else if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED)
{
addSize( aSize, true );
}
OUStringBuffer aAspectRatioString;
::sax::Converter::convertDouble(
aAspectRatioString,
(aSize.Height == 0
? 1 .0
: double (aSize.Width)/double (aSize.Height)));
mrExport.AddAttribute( XML_NAMESPACE_STYLE, XML_LEGEND_EXPANSION_ASPECT_RATIO, aAspectRatioString.makeStringAndClear() );
}
}
}
catch ( const beans::UnknownPropertyException & )
{
SAL_WARN("xmloff.chart" , "Property Expansion not found in ChartLegend" );
}
}
}
// write style name
AddAutoStyleAttribute( aPropertyStates );
// element
SvXMLElementExport aLegend( mrExport, XML_NAMESPACE_CHART, XML_LEGEND, true , true );
}
else // autostyles
{
CollectAutoStyle( std::move(aPropertyStates) );
}
// remove property states for autostyles
aPropertyStates.clear();
}
// Export data table element and properties
if (xNewDiagram.is() && nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED)
{
auto xDataTable = xNewDiagram->getDataTable();
if (xDataTable.is())
{
// get property states for autostyles
if (mxExpPropMapper.is())
{
uno::Reference<beans::XPropertySet> xPropSet(xDataTable, uno::UNO_QUERY);
if (xPropSet.is())
aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
}
if (bExportContent)
{
// add style name attribute
AddAutoStyleAttribute(aPropertyStates);
SvXMLElementExport aDataTableElement(mrExport, XML_NAMESPACE_LO_EXT, XML_DATA_TABLE, true , true );
}
else
{
CollectAutoStyle(std::move(aPropertyStates));
}
}
// remove property states for autostyles
aPropertyStates.clear();
}
// plot-area element
if ( xDiagram.is())
exportPlotArea( xDiagram, xNewDiagram, aPageSize, bExportContent, bIncludeTable );
// export additional shapes
if ( xDocPropSet.is() )
{
if ( bExportContent )
{
if ( mxAdditionalShapes.is())
{
// can't call exportShapes with all shapes because the
// initialisation happened with the complete draw page and not
// the XShapes object used here. Thus the shapes have to be
// exported one by one
rtl::Reference< XMLShapeExport > rShapeExport = mrExport.GetShapeExport();
Reference< drawing::XShape > xShape;
const sal_Int32 nShapeCount( mxAdditionalShapes->getCount());
for ( sal_Int32 nShapeId = 0 ; nShapeId < nShapeCount; nShapeId++ )
{
mxAdditionalShapes->getByIndex( nShapeId ) >>= xShape;
SAL_WARN_IF( !xShape.is(), "xmloff.chart" , "Shape without an XShape?" );
if ( ! xShape.is())
continue ;
rShapeExport->exportShape( xShape );
}
// this would be the easier way if it worked:
//mrExport.GetShapeExport()->exportShapes( mxAdditionalShapes );
}
}
else
{
// get a sequence of non-chart shapes (inserted via clipboard)
try
{
Any aShapesAny = xDocPropSet->getPropertyValue(u"AdditionalShapes" _ustr);
aShapesAny >>= mxAdditionalShapes;
}
catch ( const uno::Exception & )
{
TOOLS_INFO_EXCEPTION("xmloff.chart" , "AdditionalShapes not found" );
}
if ( mxAdditionalShapes.is())
{
// seek shapes has to be called for the whole page because in
// the shape export the vector of shapes is accessed via the
// ZOrder which might be (actually is) larger than the number of
// shapes in mxAdditionalShapes
Reference< drawing::XDrawPageSupplier > xSupplier( rChartDoc, uno::UNO_QUERY );
SAL_WARN_IF( !xSupplier.is(), "xmloff.chart" , "Cannot retrieve draw page to initialize shape export" );
if ( xSupplier.is() )
{
Reference< drawing::XShapes > xDrawPage = xSupplier->getDrawPage();
SAL_WARN_IF( !xDrawPage.is(), "xmloff.chart" , "Invalid draw page for initializing shape export" );
if ( xDrawPage.is())
mrExport.GetShapeExport()->seekShapes( xDrawPage );
}
// can't call collectShapesAutoStyles with all shapes because
// the initialisation happened with the complete draw page and
// not the XShapes object used here. Thus the shapes have to be
// exported one by one
rtl::Reference< XMLShapeExport > rShapeExport = mrExport.GetShapeExport();
Reference< drawing::XShape > xShape;
const sal_Int32 nShapeCount( mxAdditionalShapes->getCount());
for ( sal_Int32 nShapeId = 0 ; nShapeId < nShapeCount; nShapeId++ )
{
mxAdditionalShapes->getByIndex( nShapeId ) >>= xShape;
SAL_WARN_IF( !xShape.is(), "xmloff.chart" , "Shape without an XShape?" );
if ( ! xShape.is())
continue ;
rShapeExport->collectShapeAutoStyles( xShape );
}
}
}
}
// table element
// (is included as subelement of chart)
if ( bExportContent )
{
// #85929# always export table, otherwise clipboard may lose data
exportTable();
}
}
static void lcl_exportComplexLabel( const Sequence< uno::Any >& rComplexLabel, SvXMLExport& rExport )
{
sal_Int32 nLength = rComplexLabel.getLength();
if ( nLength<=1 )
return ;
SvXMLElementExport aTextList( rExport, XML_NAMESPACE_TEXT, XML_LIST, true , true );
for (const auto & rElem : rComplexLabel)
{
SvXMLElementExport aListItem( rExport, XML_NAMESPACE_TEXT, XML_LIST_ITEM, true , true );
OUString aString;
if ( !(rElem >>= aString) )
{
double aNum;
if (rElem >>= aNum)
{
aString = OUString::number(aNum);
}
}
SchXMLTools::exportText( rExport, aString, false /*bConvertTabsLFs*/ );
}
}
void SchXMLExportHelper_Impl::exportTable()
{
// table element
mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NAME, gsTableName );
try
{
bool bProtected = false ;
Reference< beans::XPropertySet > xProps( mrExport.GetModel(), uno::UNO_QUERY );
if ( xProps &&
( xProps->getPropertyValue(u"DisableDataTableDialog" _ustr) >>= bProtected ) &&
bProtected )
{
mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_PROTECTED, XML_TRUE );
}
}
catch ( const uno::Exception& )
{
}
SvXMLElementExport aTable( mrExport, XML_NAMESPACE_TABLE, XML_TABLE, true , true );
bool bHasOwnData = false ;
Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
Reference< chart2::data::XRangeXMLConversion > xRangeConversion;
if ( xNewDoc.is())
{
bHasOwnData = xNewDoc->hasInternalDataProvider();
xRangeConversion.set( xNewDoc->getDataProvider(), uno::UNO_QUERY );
}
Reference< chart2::XAnyDescriptionAccess > xAnyDescriptionAccess;
{
Reference< chart::XChartDocument > xChartDoc( mrExport.GetModel(), uno::UNO_QUERY );
if ( xChartDoc.is() )
xAnyDescriptionAccess.set( xChartDoc->getData(), uno::UNO_QUERY );
}
if ( bHasOwnData )
lcl_ReorderInternalSequencesAccordingToTheirRangeName( m_aDataSequencesToExport );
lcl_TableData aData( lcl_getDataForLocalTable( m_aDataSequencesToExport
, xAnyDescriptionAccess, maCategoriesRange
, mbRowSourceColumns, xRangeConversion ));
tStringVector::const_iterator aDataRangeIter( aData.aDataRangeRepresentations.begin());
const tStringVector::const_iterator aDataRangeEndIter( aData.aDataRangeRepresentations.end());
tStringVector::const_iterator aRowDescriptions_RangeIter( aData.aRowDescriptions_Ranges.begin());
const tStringVector::const_iterator aRowDescriptions_RangeEnd( aData.aRowDescriptions_Ranges.end());
// declare columns
{
SvXMLElementExport aHeaderColumns( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_HEADER_COLUMNS, true , true );
SvXMLElementExport aHeaderColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true , true );
}
{
SvXMLElementExport aColumns( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMNS, true , true );
sal_Int32 nNextIndex = 0 ;
for (sal_Int32 nHiddenIndex : aData.aHiddenColumns)
{
//i91578 display of hidden values (copy paste scenario; export hidden flag thus it can be used during migration to locale table upon paste )
if ( nHiddenIndex > nNextIndex )
{
sal_Int64 nRepeat = static_cast < sal_Int64 >( nHiddenIndex - nNextIndex );
if (nRepeat>1 )
mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
OUString::number( nRepeat ));
SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true , true );
}
mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_VISIBILITY, GetXMLToken( XML_COLLAPSE ) );
SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true , true );
nNextIndex = nHiddenIndex+1 ;
}
sal_Int32 nEndIndex = aData.aColumnDescriptions.size()-1 ;
if ( nEndIndex >= nNextIndex )
{
sal_Int64 nRepeat = static_cast < sal_Int64 >( nEndIndex - nNextIndex + 1 );
if (nRepeat>1 )
mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
OUString::number( nRepeat ));
SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true , true );
}
}
// export rows with content
//export header row
{
SvXMLElementExport aHeaderRows( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_HEADER_ROWS, true , true );
SvXMLElementExport aRow( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true , true );
//first one empty cell for the row descriptions
{
SvXMLElementExport aEmptyCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true , true );
SvXMLElementExport aEmptyParagraph( mrExport, XML_NAMESPACE_TEXT, XML_P, true , true );
}
//export column descriptions
tStringVector::const_iterator aColumnDescriptions_RangeIter( aData.aColumnDescriptions_Ranges.begin());
const tStringVector::const_iterator aColumnDescriptions_RangeEnd( aData.aColumnDescriptions_Ranges.end());
const Sequence< Sequence< uno::Any > >& rComplexColumnDescriptions = aData.aComplexColumnDescriptions;
sal_Int32 nComplexCount = rComplexColumnDescriptions.getLength();
sal_Int32 nC = 0 ;
for ( const auto & rDesc : aData.aColumnDescriptions )
{
bool bExportString = true ;
if ( nC < nComplexCount )
{
const Sequence< uno::Any >& rComplexLabel = rComplexColumnDescriptions[nC];
if ( rComplexLabel.hasElements() )
{
double fValue=0 .0 ;
if ( rComplexLabel[0 ] >>=fValue )
{
bExportString = false ;
::sax::Converter::convertDouble(
msStringBuffer, fValue);
msString = msStringBuffer.makeStringAndClear();
mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT );
mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE, msString );
}
}
}
if ( bExportString )
{
mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING );
}
SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true , true );
exportText( rDesc );
if ( nC < nComplexCount )
lcl_exportComplexLabel( rComplexColumnDescriptions[nC], mrExport );
if ( !bHasOwnData && aColumnDescriptions_RangeIter != aColumnDescriptions_RangeEnd )
{
// remind the original range to allow a correct re-association when copying via clipboard
if (!(*aColumnDescriptions_RangeIter).isEmpty())
SchXMLTools::exportRangeToSomewhere( mrExport, *aColumnDescriptions_RangeIter );
++aColumnDescriptions_RangeIter;
}
nC++;
}
SAL_WARN_IF( !bHasOwnData && (aColumnDescriptions_RangeIter != aColumnDescriptions_RangeEnd), "xmloff.chart" , "bHasOwnData == false && aColumnDescriptions_RangeIter != aColumnDescriptions_RangeEnd" );
} // closing row and header-rows elements
// export value rows
{
SvXMLElementExport aRows( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROWS, true , true );
tStringVector::const_iterator aRowDescriptionsIter( aData.aRowDescriptions.begin());
const Sequence< Sequence< uno::Any > >& rComplexRowDescriptions = aData.aComplexRowDescriptions;
sal_Int32 nComplexCount = rComplexRowDescriptions.getLength();
sal_Int32 nC = 0 ;
for ( const auto & rRow : aData.aDataInRows )
{
SvXMLElementExport aRow( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true , true );
//export row descriptions
{
bool bExportString = true ;
if ( nC < nComplexCount )
{
const Sequence< uno::Any >& rComplexLabel = rComplexRowDescriptions[nC];
if ( rComplexLabel.hasElements() )
{
double fValue=0 .0 ;
if ( rComplexLabel[0 ] >>=fValue )
{
bExportString = false ;
::sax::Converter::convertDouble(msStringBuffer, fValue);
msString = msStringBuffer.makeStringAndClear();
mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT );
mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE, msString );
}
}
}
if ( bExportString )
{
mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING );
}
SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true , true );
if ( aRowDescriptionsIter != aData.aRowDescriptions.end())
{
exportText( *aRowDescriptionsIter );
if ( nC < nComplexCount )
lcl_exportComplexLabel( rComplexRowDescriptions[nC], mrExport );
if ( !bHasOwnData && aRowDescriptions_RangeIter != aRowDescriptions_RangeEnd )
{
// remind the original range to allow a correct re-association when copying via clipboard
SchXMLTools::exportRangeToSomewhere( mrExport, *aRowDescriptions_RangeIter );
++aRowDescriptions_RangeIter;
}
++aRowDescriptionsIter;
}
}
//export row values
for ( t2DNumberContainer::value_type::const_iterator aColIt( rRow.begin());
aColIt != rRow.end(); ++aColIt )
{
::sax::Converter::convertDouble( msStringBuffer, *aColIt );
msString = msStringBuffer.makeStringAndClear();
mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT );
mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE, msString );
SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true , true );
exportText( msString ); // do not convert tabs and lfs
if ( ( !bHasOwnData && aDataRangeIter != aDataRangeEndIter ) &&
( mbRowSourceColumns || (aColIt == rRow.begin()) ) )
{
// remind the original range to allow a correct re-association when copying via clipboard
if (!(*aDataRangeIter).isEmpty())
SchXMLTools::exportRangeToSomewhere( mrExport, *aDataRangeIter );
++aDataRangeIter;
}
}
++nC;
}
}
// if range iterator was used it should have reached its end
SAL_WARN_IF( !bHasOwnData && (aDataRangeIter != aDataRangeEndIter), "xmloff.chart" , "bHasOwnData == false && aDataRangeIter != aDataRangeEndIter" );
SAL_WARN_IF( !bHasOwnData && (aRowDescriptions_RangeIter != aRowDescriptions_RangeEnd), "xmloff.chart" , "bHasOwnData == false && aRowDescriptions_RangeIter != aRowDescriptions_RangeEnd" );
}
namespace
{
Reference< chart2::XCoordinateSystem > lcl_getCooSys( const Reference< chart2::XDiagram > & xNewDiagram )
{
Reference< chart2::XCoordinateSystem > xCooSys;
Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xNewDiagram, uno::UNO_QUERY );
if (xCooSysCnt.is())
{
Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems() );
if (aCooSysSeq.hasElements())
xCooSys = aCooSysSeq[0 ];
}
return xCooSys;
}
Reference< chart2::XAxis > lcl_getAxis( const Reference< chart2::XCoordinateSystem >& xCooSys,
enum XMLTokenEnum eDimension, bool bPrimary=true )
{
Reference< chart2::XAxis > xNewAxis;
try
{
if ( xCooSys.is() )
{
sal_Int32 nDimensionIndex=0 ;
switch ( eDimension )
{
case XML_X:
nDimensionIndex=0 ;
break ;
case XML_Y:
nDimensionIndex=1 ;
break ;
case XML_Z:
nDimensionIndex=2 ;
break ;
default :
break ;
}
xNewAxis = xCooSys->getAxisByDimension( nDimensionIndex, bPrimary ? 0 : 1 );
}
}
catch ( const uno::Exception & )
{
}
return xNewAxis;
}
}
void SchXMLExportHelper_Impl::exportPlotArea(
const Reference< chart::XDiagram >& xDiagram,
const Reference< chart2::XDiagram >& xNewDiagram,
const awt::Size & rPageSize,
bool bExportContent,
bool bIncludeTable )
{
SAL_WARN_IF( !xDiagram.is(), "xmloff.chart" , "Invalid XDiagram as parameter" );
if ( ! xDiagram.is())
return ;
// variables for autostyles
Reference< beans::XPropertySet > xPropSet;
std::vector< XMLPropertyState > aPropertyStates;
msStringBuffer.setLength( 0 );
// plot-area element
std::unique_ptr<SvXMLElementExport> xElPlotArea;
// get property states for autostyles
xPropSet.set( xDiagram, uno::UNO_QUERY );
if ( xPropSet.is())
{
if ( mxExpPropMapper.is())
aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
}
if ( bExportContent )
{
rtl::Reference< XMLShapeExport > rShapeExport;
// write style name
AddAutoStyleAttribute( aPropertyStates );
// The attribute table:cell-range-address was removed from the standard in ODF 1.4.
// The associated attribute chart:data-source-has-labels is deprecated.
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(mrExport.getSaneDefaultVersion());
if ( !msChartAddress.isEmpty() && nCurrentODFVersion < SvtSaveOptions::ODFSVER_014)
{
if ( !bIncludeTable )
mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, msChartAddress );
Reference< chart::XChartDocument > xDoc( mrExport.GetModel(), uno::UNO_QUERY );
if ( xDoc.is() )
{
Reference< beans::XPropertySet > xDocProp( xDoc, uno::UNO_QUERY );
if ( xDocProp.is())
{
Any aAny;
try
{
bool bFirstCol = false , bFirstRow = false ;
aAny = xDocProp->getPropertyValue( u"DataSourceLabelsInFirstColumn" _ustr );
aAny >>= bFirstCol;
aAny = xDocProp->getPropertyValue( u"DataSourceLabelsInFirstRow" _ustr );
aAny >>= bFirstRow;
if ( bFirstCol || bFirstRow )
{
mrExport.AddAttribute( XML_NAMESPACE_CHART,
::xmloff::token::GetXMLToken( ::xmloff::token::XML_DATA_SOURCE_HAS_LABELS ),
( bFirstCol
? ( bFirstRow
? ::xmloff::token::GetXMLToken( ::xmloff::token::XML_BOTH )
: ::xmloff::token::GetXMLToken( ::xmloff::token::XML_COLUMN ))
: ::xmloff::token::GetXMLToken( ::xmloff::token::XML_ROW )));
}
}
catch ( const beans::UnknownPropertyException & )
{
SAL_WARN("xmloff.chart" , "Properties missing" );
}
}
}
}
// attributes
if ( xDiagram.is())
{
addPosition( xDiagram );
addSize( xDiagram );
}
bool bIs3DChart = false ;
if ( xPropSet.is())
{
Any aAny;
// 3d attributes
try
{
aAny = xPropSet->getPropertyValue(u"Dim3D" _ustr);
aAny >>= bIs3DChart;
if ( bIs3DChart )
{
rShapeExport = mrExport.GetShapeExport();
if ( rShapeExport.is())
rShapeExport->export3DSceneAttributes( xPropSet );
}
}
catch ( const uno::Exception & )
{
TOOLS_INFO_EXCEPTION("xmloff.chart" , "chart:exportPlotAreaException caught" );
}
}
// plot-area element
xElPlotArea.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_PLOT_AREA, true , true ));
//inner position rectangle element
exportCoordinateRegion( xDiagram );
// light sources (inside plot area element)
if ( bIs3DChart &&
rShapeExport.is())
rShapeExport->export3DLamps( xPropSet );
}
else // autostyles
{
CollectAutoStyle( std::move(aPropertyStates) );
}
// remove property states for autostyles
aPropertyStates.clear();
// axis elements
exportAxes( xDiagram, xNewDiagram, bExportContent );
// series elements
Reference< chart2::XAxis > xSecondYAxis = lcl_getAxis( lcl_getCooSys( xNewDiagram ), XML_Y, false );
exportSeries( xNewDiagram, rPageSize, bExportContent, xSecondYAxis.is() );
// stock-chart elements
OUString sChartType ( xDiagram->getDiagramType());
if ( sChartType == "com.sun.star.chart.StockDiagram" )
{
Reference< chart::XStatisticDisplay > xStockPropProvider( xDiagram, uno::UNO_QUERY );
if ( xStockPropProvider.is())
{
// stock-gain-marker
Reference< beans::XPropertySet > xStockPropSet = xStockPropProvider->getUpBar();
if ( xStockPropSet.is())
{
aPropertyStates.clear();
aPropertyStates = mxExpPropMapper->Filter(mrExport, xStockPropSet);
if ( !aPropertyStates.empty() )
{
if ( bExportContent )
{
AddAutoStyleAttribute( aPropertyStates );
SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_GAIN_MARKER, true , true );
}
else
{
CollectAutoStyle( std::move(aPropertyStates) );
}
}
}
// stock-loss-marker
xStockPropSet = xStockPropProvider->getDownBar();
if ( xStockPropSet.is())
{
aPropertyStates.clear();
aPropertyStates = mxExpPropMapper->Filter(mrExport, xStockPropSet);
if ( !aPropertyStates.empty() )
{
if ( bExportContent )
{
AddAutoStyleAttribute( aPropertyStates );
SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_LOSS_MARKER, true , true );
}
else
{
CollectAutoStyle( std::move(aPropertyStates) );
}
}
}
// stock-range-line
xStockPropSet = xStockPropProvider->getMinMaxLine();
if ( xStockPropSet.is())
{
aPropertyStates.clear();
aPropertyStates = mxExpPropMapper->Filter(mrExport, xStockPropSet);
if ( !aPropertyStates.empty() )
{
if ( bExportContent )
{
AddAutoStyleAttribute( aPropertyStates );
SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_RANGE_LINE, true , true );
}
else
{
CollectAutoStyle( std::move(aPropertyStates) );
}
}
}
}
}
// wall and floor element
Reference< chart::X3DDisplay > xWallFloorSupplier( xDiagram, uno::UNO_QUERY );
if ( !(mxExpPropMapper.is() &&
xWallFloorSupplier.is()))
return ;
// remove property states for autostyles
aPropertyStates.clear();
Reference< beans::XPropertySet > xWallPropSet = xWallFloorSupplier->getWall();
if ( xWallPropSet.is())
{
aPropertyStates = mxExpPropMapper->Filter(mrExport, xWallPropSet);
if ( !aPropertyStates.empty() )
{
// write element
if ( bExportContent )
{
// add style name attribute
AddAutoStyleAttribute( aPropertyStates );
SvXMLElementExport aWall( mrExport, XML_NAMESPACE_CHART, XML_WALL, true , true );
}
else // autostyles
{
CollectAutoStyle( std::move(aPropertyStates) );
}
}
}
// floor element
// remove property states for autostyles
aPropertyStates.clear();
Reference< beans::XPropertySet > xFloorPropSet = xWallFloorSupplier->getFloor();
if ( !xFloorPropSet.is())
return ;
aPropertyStates = mxExpPropMapper->Filter(mrExport, xFloorPropSet);
if ( aPropertyStates.empty() )
return ;
// write element
if ( bExportContent )
{
// add style name attribute
AddAutoStyleAttribute( aPropertyStates );
SvXMLElementExport aFloor( mrExport, XML_NAMESPACE_CHART, XML_FLOOR, true , true );
}
else // autostyles
{
CollectAutoStyle( std::move(aPropertyStates) );
}
}
void SchXMLExportHelper_Impl::exportCoordinateRegion( const uno::Reference< chart::XDiagram >& xDiagram )
{
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
mrExport.getSaneDefaultVersion());
if (nCurrentODFVersion <= SvtSaveOptions::ODFSVER_012) //do not export to ODF 1.2 or older
return ;
Reference< chart::XDiagramPositioning > xDiaPos( xDiagram, uno::UNO_QUERY );
SAL_WARN_IF( !xDiaPos.is(), "xmloff.chart" , "Invalid xDiaPos as parameter" );
if ( !xDiaPos.is() )
return ;
awt::Rectangle aRect( xDiaPos->calculateDiagramPositionExcludingAxes() );
addPosition( awt::Point(aRect.X,aRect.Y) );
addSize( awt::Size(aRect.Width,aRect.Height) );
// ODF 1.3 OFFICE-3928
SvXMLElementExport aCoordinateRegion( mrExport,
(SvtSaveOptions::ODFSVER_013 <= nCurrentODFVersion) ? XML_NAMESPACE_CHART : XML_NAMESPACE_CHART_EXT,
XML_COORDINATE_REGION, true , true );
}
namespace
{
XMLTokenEnum lcl_getTimeUnitToken( sal_Int32 nTimeUnit )
{
XMLTokenEnum eToken = XML_DAYS;
switch ( nTimeUnit )
{
case css::chart::TimeUnit::YEAR:
eToken = XML_YEARS;
break ;
case css::chart::TimeUnit::MONTH:
eToken = XML_MONTHS;
break ;
default ://days
break ;
}
return eToken;
}
}
void SchXMLExportHelper_Impl::exportDateScale( const Reference< beans::XPropertySet >& rAxisProps )
{
if ( !rAxisProps.is() )
return ;
chart::TimeIncrement aIncrement;
if ( !(rAxisProps->getPropertyValue(u"TimeIncrement" _ustr) >>= aIncrement) )
return ;
sal_Int32 nTimeResolution = css::chart::TimeUnit::DAY;
if ( aIncrement.TimeResolution >>= nTimeResolution )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_BASE_TIME_UNIT, lcl_getTimeUnitToken( nTimeResolution ) );
chart::TimeInterval aInterval;
if ( aIncrement.MajorTimeInterval >>= aInterval )
{
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MAJOR_INTERVAL_VALUE, OUString::number(aInterval.Number) );
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MAJOR_INTERVAL_UNIT, lcl_getTimeUnitToken( aInterval.TimeUnit ) );
}
if ( aIncrement.MinorTimeInterval >>= aInterval )
{
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MINOR_INTERVAL_VALUE, OUString::number(aInterval.Number) );
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MINOR_INTERVAL_UNIT, lcl_getTimeUnitToken( aInterval.TimeUnit ) );
}
SvXMLElementExport aDateScale( mrExport, XML_NAMESPACE_CHART_EXT, XML_DATE_SCALE, true , true );//#i25706#todo: change namespace for next ODF version
}
void SchXMLExportHelper_Impl::exportAxisTitle( const Reference< beans::XPropertySet >& rTitleProps, bool bExportContent )
{
if ( !rTitleProps.is() )
return ;
std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, rTitleProps);
if ( bExportContent )
{
Reference< drawing::XShape > xShape( rTitleProps, uno::UNO_QUERY );
if ( xShape.is())
addPosition( xShape );
AddAutoStyleAttribute( aPropertyStates );
SvXMLElementExport aTitle( mrExport, XML_NAMESPACE_CHART, XML_TITLE, true , true );
// paragraph containing title
exportFormattedText( rTitleProps );
}
else
{
CollectAutoStyle( std::move(aPropertyStates) );
CollectAutoTextStyle( rTitleProps );
}
aPropertyStates.clear();
}
void SchXMLExportHelper_Impl::exportGrid( const Reference< beans::XPropertySet >& rGridProperties, bool bMajor, bool bExportContent )
{
if ( !rGridProperties.is() )
return ;
std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, rGridProperties);
if ( bExportContent )
{
AddAutoStyleAttribute( aPropertyStates );
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, bMajor ? XML_MAJOR : XML_MINOR );
SvXMLElementExport aGrid( mrExport, XML_NAMESPACE_CHART, XML_GRID, true , true );
}
else
{
CollectAutoStyle( std::move(aPropertyStates) );
}
aPropertyStates.clear();
}
namespace
{
//returns true if a date scale needs to be exported
bool lcl_exportAxisType( const Reference< chart2::XAxis >& rChart2Axis, SvXMLExport& rExport)
{
bool bExportDateScale = false ;
if ( !rChart2Axis.is() )
return bExportDateScale;
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
rExport.getSaneDefaultVersion());
if ((nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) == 0 ) //do not export to ODF 1.3 or older
return bExportDateScale;
chart2::ScaleData aScale( rChart2Axis->getScaleData() );
//#i25706#todo: change namespace for next ODF version
sal_uInt16 nNameSpace = XML_NAMESPACE_CHART_EXT;
switch (aScale.AxisType)
{
case chart2::AxisType::CATEGORY:
if ( aScale.AutoDateAxis )
{
rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_AUTO );
bExportDateScale = true ;
}
else
rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_TEXT );
break ;
case chart2::AxisType::DATE:
rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_DATE );
bExportDateScale = true ;
break ;
default : //AUTOMATIC
rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_AUTO );
break ;
}
return bExportDateScale;
}
void disableLinkedNumberFormat(
std::vector<XMLPropertyState>& rPropStates, const rtl::Reference<XMLPropertySetMapper>& rMapper )
{
for (XMLPropertyState & rState : rPropStates)
{
if (rState.mnIndex < 0 || rMapper->GetEntryCount() <= rState.mnIndex)
continue ;
OUString aXMLName = rMapper->GetEntryXMLName(rState.mnIndex);
if (aXMLName != "link-data-style-to-source" )
continue ;
// Entry found. Set the value to false and bail out.
rState.maValue <<= false ;
return ;
}
// Entry not found. Insert a new entry for this.
sal_Int32 nIndex = rMapper->GetEntryIndex(XML_NAMESPACE_CHART, u"link-data-style-to-source" , 0 );
XMLPropertyState aState(nIndex);
aState.maValue <<= false ;
rPropStates.push_back(aState);
}
}
void SchXMLExportHelper_Impl::exportAxis(
enum XMLTokenEnum eDimension,
enum XMLTokenEnum eAxisName,
const Reference< beans::XPropertySet >& rAxisProps,
const Reference< chart2::XAxis >& rChart2Axis,
const OUString& rCategoriesRange,
bool bHasTitle, bool bHasMajorGrid, bool bHasMinorGrid,
bool bExportContent, std::u16string_view sChartType )
{
std::vector< XMLPropertyState > aPropertyStates;
std::unique_ptr<SvXMLElementExport> pAxis;
// get property states for autostyles
if ( rAxisProps.is() && mxExpPropMapper.is() )
{
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
mrExport.getSaneDefaultVersion());
if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED
&& eDimension == XML_X)
{
chart2::ScaleData aScaleData(rChart2Axis->getScaleData());
bool bShiftedCatPos = aScaleData.ShiftedCategoryPosition;
if (sChartType == u"com.sun.star.chart.BarDiagram" || sChartType == u"com.sun.star.chart.StockDiagram" )
{
if (!bShiftedCatPos)
rAxisProps->setPropertyValue(u"MajorOrigin" _ustr, uno::Any(0 .0 ));
}
else if (bShiftedCatPos)
rAxisProps->setPropertyValue(u"MajorOrigin" _ustr, uno::Any(0 .5 ));
}
lcl_exportNumberFormat( u"NumberFormat" _ustr, rAxisProps, mrExport );
aPropertyStates = mxExpPropMapper->Filter(mrExport, rAxisProps);
if (!maDestShellID.isEmpty() && (!maSrcShellID.isEmpty() || maSrcShellID != maDestShellID))
{
// Disable link to source number format property when pasting to
// a different doc shell. These shell ID's should be both empty
// during real ODF export.
disableLinkedNumberFormat(aPropertyStates, mxExpPropMapper->getPropertySetMapper());
}
}
bool bExportDateScale = false ;
if ( bExportContent )
{
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DIMENSION, eDimension );
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_NAME, eAxisName );
AddAutoStyleAttribute( aPropertyStates ); // write style name
if ( !rCategoriesRange.isEmpty() )
bExportDateScale = lcl_exportAxisType( rChart2Axis, mrExport );
// open axis element
pAxis.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_AXIS, true , true ));
}
else
{
CollectAutoStyle( std::move(aPropertyStates) );
}
aPropertyStates.clear();
//date scale
if ( bExportDateScale )
exportDateScale( rAxisProps );
Reference< beans::XPropertySet > xTitleProps;
Reference< beans::XPropertySet > xMajorGridProps;
Reference< beans::XPropertySet > xMinorGridProps;
Reference< chart::XAxis > xAxis( rAxisProps, uno::UNO_QUERY );
if ( xAxis.is() )
{
xTitleProps = bHasTitle ? xAxis->getAxisTitle() : nullptr;
xMajorGridProps = bHasMajorGrid ? xAxis->getMajorGrid() : nullptr;
xMinorGridProps = bHasMinorGrid ? xAxis->getMinorGrid() : nullptr;
}
// axis-title
exportAxisTitle( xTitleProps , bExportContent );
// categories if we have a categories chart
if ( bExportContent && !rCategoriesRange.isEmpty() )
{
mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, rCategoriesRange );
SvXMLElementExport aCategories( mrExport, XML_NAMESPACE_CHART, XML_CATEGORIES, true , true );
}
// grid
exportGrid( xMajorGridProps, true , bExportContent );
exportGrid( xMinorGridProps, false , bExportContent );
}
void SchXMLExportHelper_Impl::exportAxes(
const Reference< chart::XDiagram > & xDiagram,
const Reference< chart2::XDiagram > & xNewDiagram,
bool bExportContent )
{
SAL_WARN_IF( !xDiagram.is(), "xmloff.chart" , "Invalid XDiagram as parameter" );
if ( ! xDiagram.is())
return ;
// get some properties from document first
bool bHasXAxis = false ,
bHasYAxis = false ,
bHasZAxis = false ,
bHasSecondaryXAxis = false ,
bHasSecondaryYAxis = false ;
bool bHasXAxisTitle = false ,
bHasYAxisTitle = false ,
bHasZAxisTitle = false ,
bHasSecondaryXAxisTitle = false ,
bHasSecondaryYAxisTitle = false ;
bool bHasXAxisMajorGrid = false ,
bHasXAxisMinorGrid = false ,
bHasYAxisMajorGrid = false ,
bHasYAxisMinorGrid = false ,
bHasZAxisMajorGrid = false ,
bHasZAxisMinorGrid = false ;
// get multiple properties using XMultiPropertySet
MultiPropertySetHandler aDiagramProperties (xDiagram);
aDiagramProperties.Add (u"HasXAxis" _ustr, bHasXAxis);
aDiagramProperties.Add (u"HasYAxis" _ustr, bHasYAxis);
aDiagramProperties.Add (u"HasZAxis" _ustr, bHasZAxis);
aDiagramProperties.Add (u"HasSecondaryXAxis" _ustr, bHasSecondaryXAxis);
aDiagramProperties.Add (u"HasSecondaryYAxis" _ustr, bHasSecondaryYAxis);
aDiagramProperties.Add (u"HasXAxisTitle" _ustr, bHasXAxisTitle);
aDiagramProperties.Add (u"HasYAxisTitle" _ustr, bHasYAxisTitle);
aDiagramProperties.Add (u"HasZAxisTitle" _ustr, bHasZAxisTitle);
aDiagramProperties.Add (u"HasSecondaryXAxisTitle" _ustr, bHasSecondaryXAxisTitle);
aDiagramProperties.Add (u"HasSecondaryYAxisTitle" _ustr, bHasSecondaryYAxisTitle);
aDiagramProperties.Add (u"HasXAxisGrid" _ustr, bHasXAxisMajorGrid);
aDiagramProperties.Add (u"HasYAxisGrid" _ustr, bHasYAxisMajorGrid);
aDiagramProperties.Add (u"HasZAxisGrid" _ustr, bHasZAxisMajorGrid);
aDiagramProperties.Add (u"HasXAxisHelpGrid" _ustr, bHasXAxisMinorGrid);
aDiagramProperties.Add (u"HasYAxisHelpGrid" _ustr, bHasYAxisMinorGrid);
aDiagramProperties.Add (u"HasZAxisHelpGrid" _ustr, bHasZAxisMinorGrid);
if ( ! aDiagramProperties.GetProperties ())
{
SAL_INFO("xmloff.chart" , "Required properties not found in Chart diagram" );
}
Reference< chart2::XCoordinateSystem > xCooSys( lcl_getCooSys(xNewDiagram) );
// write an axis element also if the axis itself is not visible, but a grid or a title
OUString aCategoriesRange;
Reference< chart::XAxisSupplier > xAxisSupp( xDiagram, uno::UNO_QUERY );
OUString sChartType = xDiagram->getDiagramType();
// x axis
Reference< css::chart2::XAxis > xNewAxis = lcl_getAxis( xCooSys, XML_X );
if ( xNewAxis.is() )
{
Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getAxis(0 ) : nullptr, uno::UNO_QUERY );
if ( mbHasCategoryLabels && bExportContent )
{
Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xNewDiagram ) );
if ( xCategories.is() )
{
Reference< chart2::data::XDataSequence > xValues( xCategories->getValues() );
if ( xValues.is() )
{
Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
maCategoriesRange = xValues->getSourceRangeRepresentation();
aCategoriesRange = lcl_ConvertRange( maCategoriesRange, xNewDoc );
}
}
}
exportAxis( XML_X, XML_PRIMARY_X, xAxisProps, xNewAxis, aCategoriesRange, bHasXAxisTitle, bHasXAxisMajorGrid, bHasXAxisMinorGrid, bExportContent, sChartType );
aCategoriesRange.clear();
}
// secondary x axis
xNewAxis = lcl_getAxis( xCooSys, XML_X, false );
if ( xNewAxis.is() )
{
Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getSecondaryAxis(0 ) : nullptr, uno::UNO_QUERY );
exportAxis( XML_X, XML_SECONDARY_X, xAxisProps, xNewAxis, aCategoriesRange, bHasSecondaryXAxisTitle, false , false , bExportContent, sChartType );
}
// y axis
xNewAxis = lcl_getAxis( xCooSys, XML_Y );
if ( xNewAxis.is() )
{
Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getAxis(1 ) : nullptr, uno::UNO_QUERY );
exportAxis( XML_Y, XML_PRIMARY_Y, xAxisProps, xNewAxis, aCategoriesRange, bHasYAxisTitle, bHasYAxisMajorGrid, bHasYAxisMinorGrid, bExportContent, sChartType );
}
// secondary y axis
xNewAxis = lcl_getAxis( xCooSys, XML_Y, false );
if ( xNewAxis.is() )
{
Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getSecondaryAxis(1 ) : nullptr, uno::UNO_QUERY );
exportAxis( XML_Y, XML_SECONDARY_Y, xAxisProps, xNewAxis, aCategoriesRange, bHasSecondaryYAxisTitle, false , false , bExportContent, sChartType );
}
// z axis
xNewAxis = lcl_getAxis( xCooSys, XML_Z );
if ( xNewAxis.is() )
{
Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getAxis(2 ) : nullptr, uno::UNO_QUERY );
exportAxis( XML_Z, XML_PRIMARY_Z, xAxisProps, xNewAxis, aCategoriesRange, bHasZAxisTitle, bHasZAxisMajorGrid, bHasZAxisMinorGrid, bExportContent, sChartType );
}
}
namespace
{
bool lcl_hasNoValuesButText( const uno::Reference< chart2::data::XDataSequence >& xDataSequence )
{
if ( !xDataSequence.is() )
return false ;//have no data
Sequence< uno::Any > aData;
Reference< chart2::data::XNumericalDataSequence > xNumericalDataSequence( xDataSequence, uno::UNO_QUERY );
if ( xNumericalDataSequence.is() )
{
const Sequence< double > aDoubles( xNumericalDataSequence->getNumericalData() );
if (std::any_of(aDoubles.begin(), aDoubles.end(), [](double fDouble) { return !std::isnan( fDouble ); }))
return false ;//have double value
}
else
{
aData = xDataSequence->getData();
double fDouble = 0 .0 ;
bool bHaveDouble = std::any_of(std::cbegin(aData), std::cend(aData),
[&fDouble](const uno::Any& rData) { return (rData >>= fDouble) && !std::isnan( fDouble ); });
if (bHaveDouble)
return false ;//have double value
}
//no values found
Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xDataSequence, uno::UNO_QUERY );
if ( xTextualDataSequence.is() )
{
const uno::Sequence< OUString > aStrings( xTextualDataSequence->getTextualData() );
if (std::any_of(aStrings.begin(), aStrings.end(), [](const OUString& rString) { return !rString.isEmpty(); }))
return true ;//have text
}
else
{
if ( !aData.hasElements() )
aData = xDataSequence->getData();
OUString aString;
bool bHaveText = std::any_of(std::cbegin(aData), std::cend(aData),
[&aString](const uno::Any& rData) { return (rData >>= aString) && !aString.isEmpty(); });
if (bHaveText)
return true ;//have text
}
//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(OUString sAPI, const sal_uInt16 nNS, OUString sLocal)
: sAPIName(std::move(sAPI))
, nNameSpace(nNS)
, sLocalName(std::move(sLocal))
{
}
};
const API2ODFMapItem aLabelFoo2ODFArray[]
= { API2ODFMapItem(u"LabelBorderStyle" _ustr, XML_NAMESPACE_DRAW, u"stroke" _ustr),
API2ODFMapItem(u"LabelBorderWidth" _ustr, XML_NAMESPACE_SVG, u"stroke-width" _ustr),
API2ODFMapItem(u"LabelBorderColor" _ustr, XML_NAMESPACE_SVG, u"stroke-color" _ustr),
API2ODFMapItem(u"LabelBorderDashName" _ustr, XML_NAMESPACE_DRAW, u"stroke-dash" _ustr),
API2ODFMapItem(u"LabelBorderTransparency" _ustr, XML_NAMESPACE_SVG, u"stroke-opacity" _ustr),
API2ODFMapItem(u"LabelFillStyle" _ustr, XML_NAMESPACE_DRAW, u"fill" _ustr),
API2ODFMapItem(u"LabelFillBackground" _ustr, XML_NAMESPACE_DRAW, u"fill-hatch-solid" _ustr),
API2ODFMapItem(u"LabelFillHatchName" _ustr, XML_NAMESPACE_DRAW, u"fill-hatch-name" _ustr),
API2ODFMapItem(u"LabelFillColor" _ustr, XML_NAMESPACE_DRAW, u"fill-color" _ustr) };
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,
const awt::Size & rPageSize,
bool bExportContent,
bool bHasTwoYAxes )
{
Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( xNewDiagram, uno::UNO_QUERY );
if ( ! xBCooSysCnt.is())
return ;
Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
OUString aFirstXDomainRange;
OUString aFirstYDomainRange;
std::vector< XMLPropertyState > aPropertyStates;
std::vector< XMLPropertyState > aDataLabelPropertyStates;
const Sequence< Reference< chart2::XCoordinateSystem > >
aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
for ( const auto & rCooSys : aCooSysSeq )
{
Reference< chart2::XChartTypeContainer > xCTCnt( rCooSys, uno::UNO_QUERY );
if ( ! xCTCnt.is())
continue ;
const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
for ( const auto & rChartType : aCTSeq )
{
Reference< chart2::XDataSeriesContainer > xDSCnt( rChartType, uno::UNO_QUERY );
if ( ! xDSCnt.is())
continue ;
// note: if xDSCnt.is() then also aCTSeq[nCTIdx]
OUString aChartType( rChartType->getChartType());
OUString aLabelRole = rChartType->getRoleOfSequenceForSeriesLabel();
// special export for stock charts
if ( aChartType == "com.sun.star.chart2.CandleStickChartType" )
{
bool bJapaneseCandleSticks = false ;
Reference< beans::XPropertySet > xCTProp( rChartType, uno::UNO_QUERY );
if ( xCTProp.is())
xCTProp->getPropertyValue(u"Japanese" _ustr) >>= bJapaneseCandleSticks;
exportCandleStickSeries(
xDSCnt->getDataSeries(), xNewDiagram, bJapaneseCandleSticks, bExportContent );
continue ;
}
// export dataseries for current chart-type
Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries());
for ( sal_Int32 nSeriesIdx=0 ; nSeriesIdx<aSeriesSeq.getLength(); ++nSeriesIdx )
{
// export series
Reference< chart2::data::XDataSource > xSource( aSeriesSeq[nSeriesIdx], uno::UNO_QUERY );
if ( xSource.is())
{
std::unique_ptr<SvXMLElementExport> pSeries;
Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
xSource->getDataSequences());
sal_Int32 nMainSequenceIndex = -1 ;
sal_Int32 nSeriesLength = 0 ;
bool bHasMeanValueLine = false ;
Reference< beans::XPropertySet > xPropSet;
tLabelValuesDataPair aSeriesLabelValuesPair;
// search for main sequence and create a series element
{
Reference< chart2::data::XDataSequence > xValuesSeq;
Reference< chart2::data::XDataSequence > xLabelSeq;
sal_Int32 nSeqIdx=0 ;
for ( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx )
{
Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() );
if ( nMainSequenceIndex==-1 )
{
OUString aRole;
Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY );
if ( xSeqProp.is())
xSeqProp->getPropertyValue(u"Role" _ustr) >>= aRole;
// "main" sequence
if ( aRole == aLabelRole )
{
xValuesSeq.set( xTempValueSeq );
xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel());
nMainSequenceIndex = nSeqIdx;
}
}
sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0 ));
if ( nSeriesLength < nSequenceLength )
nSeriesLength = nSequenceLength;
}
// have found the main sequence, then xValuesSeq and
// xLabelSeq contain those. Otherwise both are empty
{
sal_Int32 nAttachedAxis = chart::ChartAxisAssign::PRIMARY_Y;
// get property states for autostyles
try
{
xPropSet = SchXMLSeriesHelper::createOldAPISeriesPropertySet(
aSeriesSeq[nSeriesIdx], mrExport.GetModel() );
}
catch ( const uno::Exception & )
{
TOOLS_INFO_EXCEPTION("xmloff.chart" , "Series not found or no XPropertySet" );
continue ;
}
if ( xPropSet.is())
{
// determine attached axis
try
{
Any aAny( xPropSet->getPropertyValue( u"Axis" _ustr ));
aAny >>= nAttachedAxis;
aAny = xPropSet->getPropertyValue( u"MeanValue" _ustr );
aAny >>= bHasMeanValueLine;
}
catch ( const beans::UnknownPropertyException & )
{
TOOLS_INFO_EXCEPTION("xmloff.chart" , "Required property not found in DataRowProperties" );
}
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
mrExport.getSaneDefaultVersion());
if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
{
lcl_exportNumberFormat( u"NumberFormat" _ustr, xPropSet, mrExport );
lcl_exportNumberFormat( u"PercentageNumberFormat" _ustr, xPropSet, mrExport );
}
if ( mxExpPropMapper.is())
aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
if (!maDestShellID.isEmpty() && (!maSrcShellID.isEmpty() || maSrcShellID != maDestShellID))
{
// Disable link to source number format property when pasting to
// a different doc shell. These shell ID's should be both empty
// during real ODF export.
disableLinkedNumberFormat(aPropertyStates, mxExpPropMapper->getPropertySetMapper());
}
}
if ( bExportContent )
{
if ( bHasTwoYAxes )
{
if ( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
else
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
}
// write style name
AddAutoStyleAttribute( aPropertyStates );
if ( xValuesSeq.is())
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS,
lcl_ConvertRange(
xValuesSeq->getSourceRangeRepresentation(),
xNewDoc ));
else
// #i75297# allow empty series, export empty range to have all ranges on import
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, OUString());
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
mrExport.getSaneDefaultVersion());
if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older
{
if (xPropSet.is())
{
Any aAny = xPropSet->getPropertyValue(u"ShowLegendEntry" _ustr);
if (!aAny.get<bool >())
{
mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDE_LEGEND, OUString::boolean(true ));
}
}
}
if (xLabelSeq.is())
{
// Check if the label is direct string value rather than a reference.
bool bHasString = false ;
uno::Reference<beans::XPropertySet> xLSProp(xLabelSeq, uno::UNO_QUERY);
if (xLSProp.is())
{
try
{
xLSProp->getPropertyValue(u"HasStringLabel" _ustr) >>= bHasString;
}
catch (const beans::UnknownPropertyException&) {}
}
OUString aRange = xLabelSeq->getSourceRangeRepresentation();
if (bHasString)
{
mrExport.AddAttribute(
XML_NAMESPACE_LO_EXT, XML_LABEL_STRING, aRange);
}
else
{
mrExport.AddAttribute(
XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS,
lcl_ConvertRange(
xLabelSeq->getSourceRangeRepresentation(), xNewDoc));
}
}
if ( xLabelSeq.is() || xValuesSeq.is() )
aSeriesLabelValuesPair = tLabelValuesDataPair( xLabelSeq, xValuesSeq );
// chart-type for mixed types
enum XMLTokenEnum eCTToken(
SchXMLTools::getTokenByChartType( aChartType, false /* bUseOldNames */ ));
//@todo: get token for current charttype
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS,
mrExport.GetNamespaceMap().GetQNameByKey(
XML_NAMESPACE_CHART, GetXMLToken( eCTToken )));
// open series element until end of for loop
pSeries.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true , true ));
}
else // autostyles
{
CollectAutoStyle( std::move(aPropertyStates) );
}
// remove property states for autostyles
aPropertyStates.clear();
}
}
// export domain elements if we have a series parent element
if ( pSeries )
{
// domain elements
if ( bExportContent )
{
bool bIsScatterChart = aChartType == "com.sun.star.chart2.ScatterChartType" ;
bool bIsBubbleChart = aChartType == "com.sun.star.chart2.BubbleChartType" ;
Reference< chart2::data::XDataSequence > xYValuesForBubbleChart;
if ( bIsBubbleChart )
{
Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, u"values-y" _ustr ) );
if ( xSequence.is() )
{
xYValuesForBubbleChart = xSequence->getValues();
if ( !lcl_exportDomainForThisSequence( xYValuesForBubbleChart, aFirstYDomainRange, mrExport ) )
xYValuesForBubbleChart = nullptr;
}
}
if ( bIsScatterChart || bIsBubbleChart )
{
Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, u"values-x" _ustr ) );
if ( xSequence.is() )
{
Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
if ( lcl_exportDomainForThisSequence( xValues, aFirstXDomainRange, mrExport ) )
m_aDataSequencesToExport.emplace_back(
uno::Reference< chart2::data::XDataSequence >(), xValues );
}
else if ( nSeriesIdx==0 )
{
//might be that the categories are used as x-values (e.g. for date axis) -> export them accordingly
Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xNewDiagram ) );
if ( xCategories.is() )
{
Reference< chart2::data::XDataSequence > xValues( xCategories->getValues() );
if ( !lcl_hasNoValuesButText( xValues ) )
lcl_exportDomainForThisSequence( xValues, aFirstXDomainRange, mrExport );
}
}
}
if ( xYValuesForBubbleChart.is() )
m_aDataSequencesToExport.emplace_back(
uno::Reference< chart2::data::XDataSequence >(), xYValuesForBubbleChart );
}
}
// add sequences for main sequence after domain sequences,
// so that the export of the local table has the correct order
if ( bExportContent &&
(aSeriesLabelValuesPair.first.is() || aSeriesLabelValuesPair.second.is()))
m_aDataSequencesToExport.push_back( aSeriesLabelValuesPair );
// statistical objects:
// regression curves and mean value lines
if ( bHasMeanValueLine &&
xPropSet.is() &&
mxExpPropMapper.is() )
{
Reference< beans::XPropertySet > xStatProp;
try
{
Any aPropAny( xPropSet->getPropertyValue( u"DataMeanValueProperties" _ustr ));
aPropAny >>= xStatProp;
}
catch ( const uno::Exception & )
{
TOOLS_INFO_EXCEPTION("xmloff.chart" , "Exception caught during Export of series - optional DataMeanValueProperties not available" );
}
if ( xStatProp.is() )
{
aPropertyStates = mxExpPropMapper->Filter(mrExport, xStatProp);
if ( !aPropertyStates.empty() )
{
// write element
if ( bExportContent )
{
// add style name attribute
AddAutoStyleAttribute( aPropertyStates );
SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_MEAN_VALUE, true , true );
}
else // autostyles
{
CollectAutoStyle( std::move(aPropertyStates) );
}
}
}
}
if ( xPropSet.is() &&
mxExpPropMapper.is() )
{
exportRegressionCurve( aSeriesSeq[nSeriesIdx], rPageSize, bExportContent );
}
exportErrorBar( xPropSet,false , bExportContent ); // X ErrorBar
exportErrorBar( xPropSet,true , bExportContent ); // Y ErrorBar
exportDataPoints(
uno::Reference< beans::XPropertySet >( aSeriesSeq[nSeriesIdx], uno::UNO_QUERY ),
nSeriesLength, xNewDiagram, bExportContent );
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
mrExport.getSaneDefaultVersion());
// 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(std::move(aDataLabelPropertyStates));
}
aDataLabelPropertyStates.clear();
if (bExportContent && nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older
{
Sequence< OUString > aSupportedMappings = rChartType->getSupportedPropertyRoles();
exportPropertyMapping( xSource, aSupportedMappings );
}
// close series element
pSeries.reset();
}
}
aPropertyStates.clear();
aDataLabelPropertyStates.clear();
}
}
}
void SchXMLExportHelper_Impl::exportPropertyMapping(
const Reference< chart2::data::XDataSource > & xSource, const Sequence< OUString >& rSupportedMappings )
{
Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
xSource->getDataSequences());
for (const auto & rSupportedMapping : rSupportedMappings)
{
Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, rSupportedMapping ) );
if (xSequence.is())
{
Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
if ( xValues.is())
{
mrExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_PROPERTY, rSupportedMapping);
mrExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_CELL_RANGE_ADDRESS,
lcl_ConvertRange(
xValues->getSourceRangeRepresentation(),
xNewDoc ));
SvXMLElementExport( mrExport, XML_NAMESPACE_LO_EXT, XML_PROPERTY_MAPPING, true , true );
// register range for data table export
m_aDataSequencesToExport.emplace_back(
uno::Reference< chart2::data::XDataSequence >(), xValues );
}
}
}
}
void SchXMLExportHelper_Impl::exportRegressionCurve(
const Reference< chart2::XDataSeries >& xSeries,
const awt::Size& rPageSize,
bool bExportContent )
{
OSL_ASSERT( mxExpPropMapper.is());
Reference< chart2::XRegressionCurveContainer > xRegressionCurveContainer( xSeries, uno::UNO_QUERY );
if ( !xRegressionCurveContainer.is() )
return ;
const Sequence< Reference< chart2::XRegressionCurve > > aRegCurveSeq = xRegressionCurveContainer->getRegressionCurves();
for ( const auto & xRegCurve : aRegCurveSeq )
{
std::vector< XMLPropertyState > aEquationPropertyStates;
if (!xRegCurve.is())
continue ;
Reference< beans::XPropertySet > xProperties( xRegCurve , uno::UNO_QUERY );
if ( !xProperties.is() )
continue ;
Reference< lang::XServiceName > xServiceName( xProperties, uno::UNO_QUERY );
if ( !xServiceName.is() )
continue ;
bool bShowEquation = false ;
bool bShowRSquared = false ;
bool bExportEquation = false ;
OUString aService = xServiceName->getServiceName();
std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, xProperties);
// Add service name (which is regression type)
sal_Int32 nIndex = GetPropertySetMapper()->FindEntryIndex(XML_SCH_CONTEXT_SPECIAL_REGRESSION_TYPE);
XMLPropertyState property(nIndex, uno::Any(aService));
aPropertyStates.push_back(property);
Reference< beans::XPropertySet > xEquationProperties;
xEquationProperties.set( xRegCurve->getEquationProperties() );
if ( xEquationProperties.is())
{
xEquationProperties->getPropertyValue( u"ShowEquation" _ustr) >>= bShowEquation;
xEquationProperties->getPropertyValue( u"ShowCorrelationCoefficient" _ustr) >>= bShowRSquared;
bExportEquation = ( bShowEquation || bShowRSquared );
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion(
mrExport.getSaneDefaultVersion());
if (nCurrentVersion < SvtSaveOptions::ODFSVER_012)
{
bExportEquation=false ;
}
if ( bExportEquation )
{
// number format
sal_Int32 nNumberFormat = 0 ;
if ( (xEquationProperties->getPropertyValue(u"NumberFormat" _ustr) >>= nNumberFormat ) &&
nNumberFormat != -1 )
{
mrExport.addDataStyle( nNumberFormat );
}
aEquationPropertyStates = mxExpPropMapper->Filter(mrExport, xEquationProperties);
}
}
// write element
if ( bExportContent )
{
// add style name attribute
AddAutoStyleAttribute( aPropertyStates );
SvXMLElementExport aRegressionExport( mrExport, XML_NAMESPACE_CHART, XML_REGRESSION_CURVE, true , true );
if ( bExportEquation )
{
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DISPLAY_EQUATION, (bShowEquation ? XML_TRUE : XML_FALSE) );
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DISPLAY_R_SQUARE, (bShowRSquared ? XML_TRUE : XML_FALSE) );
// export position
chart2::RelativePosition aRelativePosition;
if ( xEquationProperties->getPropertyValue( u"RelativePosition" _ustr ) >>= aRelativePosition )
{
double fX = aRelativePosition.Primary * rPageSize.Width;
double fY = aRelativePosition.Secondary * rPageSize.Height;
awt::Point aPos;
aPos.X = static_cast < sal_Int32 >( ::rtl::math::round( fX ));
aPos.Y = static_cast < sal_Int32 >( ::rtl::math::round( fY ));
addPosition( aPos );
}
if ( !aEquationPropertyStates.empty())
{
AddAutoStyleAttribute( aEquationPropertyStates );
}
SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_EQUATION, true , true );
}
}
else // autostyles
{
CollectAutoStyle( std::move(aPropertyStates) );
if ( bExportEquation && !aEquationPropertyStates.empty())
{
CollectAutoStyle( std::move(aEquationPropertyStates) );
}
}
}
}
void SchXMLExportHelper_Impl::exportErrorBar( const Reference<beans::XPropertySet> &xSeriesProp,
bool bYError, bool bExportContent )
{
assert(mxExpPropMapper.is());
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion(
mrExport.getSaneDefaultVersion());
/// Don't export X ErrorBars for older ODF versions.
if (!bYError && nCurrentVersion < SvtSaveOptions::ODFSVER_012)
return ;
if (!xSeriesProp.is())
return ;
bool bNegative = false , bPositive = false ;
sal_Int32 nErrorBarStyle = chart::ErrorBarStyle::NONE;
Reference< beans::XPropertySet > xErrorBarProp;
try
{
Any aAny = xSeriesProp->getPropertyValue( bYError ? u"ErrorBarY" _ustr : u"ErrorBarX" _ustr );
aAny >>= xErrorBarProp;
if ( xErrorBarProp.is() )
{
aAny = xErrorBarProp->getPropertyValue(u"ShowNegativeError" _ustr );
aAny >>= bNegative;
aAny = xErrorBarProp->getPropertyValue(u"ShowPositiveError" _ustr );
aAny >>= bPositive;
aAny = xErrorBarProp->getPropertyValue(u"ErrorBarStyle" _ustr );
aAny >>= nErrorBarStyle;
}
}
catch ( const beans::UnknownPropertyException & )
{
TOOLS_INFO_EXCEPTION("xmloff.chart" , "Required property not found in DataRowProperties" );
}
if ( !(nErrorBarStyle != chart::ErrorBarStyle::NONE && (bNegative || bPositive)))
return ;
if ( bExportContent && nErrorBarStyle == chart::ErrorBarStyle::FROM_DATA )
{
// register data ranges for error bars for export in local table
::std::vector< Reference< chart2::data::XDataSequence > > aErrorBarSequences(
lcl_getErrorBarSequences( xErrorBarProp ));
for ( const auto & rErrorBarSequence : aErrorBarSequences )
{
m_aDataSequencesToExport.emplace_back(
uno::Reference< chart2::data::XDataSequence >(), rErrorBarSequence );
}
}
std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, xErrorBarProp);
if ( aPropertyStates.empty() )
return ;
// write element
if ( bExportContent )
{
// add style name attribute
AddAutoStyleAttribute( aPropertyStates );
if (nCurrentVersion >= SvtSaveOptions::ODFSVER_012)
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DIMENSION, bYError ? XML_Y : XML_X );//#i114149#
SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_ERROR_INDICATOR, true , true );
}
else // autostyles
{
CollectAutoStyle( std::move(aPropertyStates) );
}
}
void SchXMLExportHelper_Impl::exportCandleStickSeries(
const Sequence< Reference< chart2::XDataSeries > > & aSeriesSeq,
const Reference< chart2::XDiagram > & xDiagram,
bool bJapaneseCandleSticks,
bool bExportContent )
{
for ( const auto & xSeries : aSeriesSeq )
{
sal_Int32 nAttachedAxis = lcl_isSeriesAttachedToFirstAxis( xSeries )
? chart::ChartAxisAssign::PRIMARY_Y
: chart::ChartAxisAssign::SECONDARY_Y;
Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
if ( xSource.is())
{
// export series in correct order (as we don't store roles)
// with japanese candlesticks: open, low, high, close
// otherwise: low, high, close
Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
xSource->getDataSequences());
sal_Int32 nSeriesLength =
lcl_getSequenceLengthByRole( aSeqCnt, u"values-last" _ustr);
if ( bExportContent )
{
Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
//@todo: export data points
//TODO: moggi: same code three times
// open
if ( bJapaneseCandleSticks )
{
tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole(
aSeqCnt, u"values-first" _ustr, xNewDoc, m_aDataSequencesToExport ));
if ( !aRanges.second.isEmpty())
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second );
if ( !aRanges.first.isEmpty())
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first );
if ( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
else
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
SvXMLElementExport aOpenSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true , true );
// export empty data points
exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent );
}
// low
{
tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole(
aSeqCnt, u"values-min" _ustr, xNewDoc, m_aDataSequencesToExport ));
if ( !aRanges.second.isEmpty())
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second );
if ( !aRanges.first.isEmpty())
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first );
if ( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
else
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
SvXMLElementExport aLowSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true , true );
// export empty data points
exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent );
}
// high
{
tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole(
aSeqCnt, u"values-max" _ustr, xNewDoc, m_aDataSequencesToExport ));
if ( !aRanges.second.isEmpty())
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second );
if ( !aRanges.first.isEmpty())
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first );
if ( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
else
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
SvXMLElementExport aHighSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true , true );
// export empty data points
exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent );
}
// close
{
tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole(
aSeqCnt, u"values-last" _ustr, xNewDoc, m_aDataSequencesToExport ));
if ( !aRanges.second.isEmpty())
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second );
if ( !aRanges.first.isEmpty())
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first );
if ( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
else
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
SvXMLElementExport aCloseSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true , true );
// export empty data points
exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent );
}
}
else // autostyles
{
// for close series
}
// remove property states for autostyles
}
}
}
void SchXMLExportHelper_Impl::exportDataPoints(
const uno::Reference< beans::XPropertySet > & xSeriesProperties,
sal_Int32 nSeriesLength,
const uno::Reference< chart2::XDiagram > & xDiagram,
bool bExportContent )
{
// data-points
// write data-points only if they contain autostyles
// objects with equal autostyles are grouped using the attribute
// repeat="number"
// Note: if only the nth data-point has autostyles there is an element
// without style and repeat="n-1" attribute written in advance.
// the sequence aDataPointSeq contains indices of data-points that
// do have own attributes. This increases the performance substantially.
// more performant version for #93600#
if (!mxExpPropMapper.is())
return ;
uno::Reference< chart2::XDataSeries > xSeries( xSeriesProperties, uno::UNO_QUERY );
std::vector< XMLPropertyState > aPropertyStates;
std::vector<XMLPropertyState> aDataLabelPropertyStates;
bool bVaryColorsByPoint = false ;
Sequence< sal_Int32 > aDataPointSeq;
Sequence<sal_Int32> deletedLegendEntriesSeq;
if ( xSeriesProperties.is())
{
xSeriesProperties->getPropertyValue(u"AttributedDataPoints" _ustr) >>= aDataPointSeq;
xSeriesProperties->getPropertyValue(u"VaryColorsByPoint" _ustr) >>= bVaryColorsByPoint;
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
mrExport.getSaneDefaultVersion());
if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older
xSeriesProperties->getPropertyValue(u"DeletedLegendEntries" _ustr) >>= deletedLegendEntriesSeq;
}
sal_Int32 nSize = aDataPointSeq.getLength();
SAL_WARN_IF( nSize > nSeriesLength, "xmloff.chart" , "Too many point attributes" );
const sal_Int32 * pPoints = aDataPointSeq.getConstArray();
sal_Int32 nElement;
Reference< chart2::XColorScheme > xColorScheme;
if ( xDiagram.is())
xColorScheme.set( xDiagram->getDefaultColorScheme());
::std::vector< SchXMLDataPointStruct > aDataPointVector;
sal_Int32 nLastIndex = -1 ;
// collect elements
if ( bVaryColorsByPoint && xColorScheme.is() )
{
o3tl::sorted_vector< sal_Int32 > aAttrPointSet;
aAttrPointSet.reserve(aDataPointSeq.getLength());
for (auto p = pPoints; p < pPoints + aDataPointSeq.getLength(); ++p)
aAttrPointSet.insert( *p );
const auto aEndIt = aAttrPointSet.end();
for ( nElement = 0 ; nElement < nSeriesLength; ++nElement )
{
aPropertyStates.clear();
aDataLabelPropertyStates.clear();
uno::Reference< beans::XPropertySet > xPropSet;
bool bExportNumFmt = false ;
if ( aAttrPointSet.find( nElement ) != aEndIt )
{
try
{
xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
xSeries, nElement, mrExport.GetModel() );
bExportNumFmt = true ;
}
catch ( const uno::Exception & )
{
TOOLS_INFO_EXCEPTION("xmloff.chart" , "Exception caught during Export of data point" );
}
}
else
{
// property set only containing the color
xPropSet.set( new ::xmloff::chart::ColorPropertySet(
::Color(ColorTransparency, xColorScheme->getColorByIndex( nElement ))));
}
SAL_WARN_IF( !xPropSet.is(), "xmloff.chart" , "Pie Segments should have properties" );
if ( xPropSet.is())
{
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
mrExport.getSaneDefaultVersion());
if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012 && bExportNumFmt)
{
lcl_exportNumberFormat( u"NumberFormat" _ustr, xPropSet, mrExport );
lcl_exportNumberFormat( u"PercentageNumberFormat" _ustr, xPropSet, mrExport );
}
// Generate style for <chart:data-label> child element
if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
{
lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet,
mxExpPropMapper);
}
if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED)
{
sal_Int32 nPlacement = 0 ;
xPropSet->getPropertyValue(u"LabelPlacement" _ustr) >>= nPlacement;
if (nPlacement == chart::DataLabelPlacement::CUSTOM)
{
xPropSet->setPropertyValue(u"LabelPlacement" _ustr,
uno::Any(chart::DataLabelPlacement::OUTSIDE));
}
}
aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
if (!aPropertyStates.empty() || !aDataLabelPropertyStates.empty())
{
if (bExportContent)
{
// write data-point with style
SchXMLDataPointStruct aPoint;
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.mCustomLabel = lcl_getCustomLabelField(mrExport, nElement, xSeries);
aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(mrExport, nElement, xSeries);
aDataPointVector.push_back( aPoint );
}
else
{
if (!aPropertyStates.empty())
CollectAutoStyle(std::move(aPropertyStates));
if (!aDataLabelPropertyStates.empty())
CollectAutoStyle(std::move(aDataLabelPropertyStates));
}
}
}
}
SAL_WARN_IF( bExportContent && (static_cast <sal_Int32>(aDataPointVector.size()) != nSeriesLength), "xmloff.chart" , "not enough data points on content export" );
}
else
{
for (sal_Int32 nCurrIndex : aDataPointSeq)
{
aPropertyStates.clear();
aDataLabelPropertyStates.clear();
//assuming sorted indices in pPoints
if ( nCurrIndex<0 || nCurrIndex>=nSeriesLength )
break ;
// write leading empty data points
if ( nCurrIndex - nLastIndex > 1 )
{
SchXMLDataPointStruct aPoint;
aPoint.mnRepeat = nCurrIndex - nLastIndex - 1 ;
aDataPointVector.push_back( aPoint );
}
uno::Reference< beans::XPropertySet > xPropSet;
// get property states
try
{
xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
xSeries, nCurrIndex, mrExport.GetModel() );
}
catch ( const uno::Exception & )
{
TOOLS_INFO_EXCEPTION("xmloff.chart" , "Exception caught during Export of data point" );
}
if ( xPropSet.is())
{
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
mrExport.getSaneDefaultVersion());
if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
{
lcl_exportNumberFormat( u"NumberFormat" _ustr, xPropSet, mrExport );
lcl_exportNumberFormat( u"PercentageNumberFormat" _ustr, xPropSet, mrExport );
}
// Generate style for <chart:data-label> child element
if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
{
lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet,
mxExpPropMapper);
}
aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
if (!aPropertyStates.empty() || !aDataLabelPropertyStates.empty())
{
if ( bExportContent )
{
// write data-point with style
SchXMLDataPointStruct aPoint;
if (!aPropertyStates.empty())
{
SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart" ,
"Autostyle queue empty!" );
aPoint.maStyleName = maAutoStyleNameQueue.front();
maAutoStyleNameQueue.pop();
}
aPoint.mCustomLabel = lcl_getCustomLabelField(mrExport, nCurrIndex, xSeries);
aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(mrExport, nCurrIndex, xSeries);
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
{
if (!aPropertyStates.empty())
CollectAutoStyle(std::move(aPropertyStates));
if (!aDataLabelPropertyStates.empty())
CollectAutoStyle(std::move(aDataLabelPropertyStates));
}
continue ;
}
}
// if we get here the property states are empty
SchXMLDataPointStruct aPoint;
aDataPointVector.push_back( aPoint );
nLastIndex = nCurrIndex;
}
// final empty elements
sal_Int32 nRepeat = nSeriesLength - nLastIndex - 1 ;
if( nRepeat > 0 )
{
SchXMLDataPointStruct aPoint;
aPoint.mnRepeat = nRepeat;
aDataPointVector.push_back( aPoint );
}
}
if (!bExportContent)
return;
// write elements (merge equal ones)
SchXMLDataPointStruct aPoint;
SchXMLDataPointStruct aLastPoint;
// initialize so that it doesn't matter if
// the element is counted in the first iteration
aLastPoint.mnRepeat = 0 ;
sal_Int32 nIndex = 0 ;
for( const auto& rPoint : aDataPointVector )
{
aPoint = rPoint;
if (aPoint.maStyleName == aLastPoint.maStyleName
&& aLastPoint.mCustomLabel.maFields.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 )
{
// write last element
if( !aLastPoint.maStyleName.isEmpty() )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aLastPoint.maStyleName );
if( aLastPoint.mnRepeat > 1 )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_REPEATED,
OUString::number( aLastPoint.mnRepeat ));
for (const auto& deletedLegendEntry : deletedLegendEntriesSeq)
{
if (nIndex == deletedLegendEntry)
{
mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDE_LEGEND, OUString::boolean(true));
break;
}
}
nIndex++;
exportCustomLabelPosition(aLastPoint.mCustomLabelPos); // adds attributes
SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, true, true );
exportCustomLabel(aLastPoint);
}
aLastPoint = aPoint;
}
// write last element if it hasn't been written in last iteration
if( aPoint.maStyleName != aLastPoint.maStyleName )
return;
if( !aLastPoint.maStyleName.isEmpty() )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aLastPoint.maStyleName );
if( aLastPoint.mnRepeat > 1 )
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_REPEATED,
OUString::number( aLastPoint.mnRepeat ));
for (const auto& deletedLegendEntry : deletedLegendEntriesSeq)
{
if (nIndex == deletedLegendEntry)
{
mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDE_LEGEND, OUString::boolean(true));
break;
}
}
exportCustomLabelPosition(aLastPoint.mCustomLabelPos); // adds attributes
SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, true, true );
exportCustomLabel(aLastPoint);
}
void SchXMLExportHelper_Impl::exportCustomLabel(const SchXMLDataPointStruct& rPoint)
{
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.mCustomLabel.maFields)
{
// TODO add style
SvXMLElementExport aSpan( mrExport, XML_NAMESPACE_TEXT, XML_SPAN, true, false);
mrExport.GetDocHandler()->characters(label ->getString());
}
}
void SchXMLExportHelper_Impl::exportCustomLabelPosition( const chart2::RelativePosition & xCustomLabelPosition)
{
if( xCustomLabelPosition.Primary == 0 .0 && xCustomLabelPosition.Secondary == 0 .0 )
return; // nothing to export
OUStringBuffer aCustomLabelPosString;
::sax::Converter::convertDouble(aCustomLabelPosString, xCustomLabelPosition.Primary);
mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_CUSTOM_LABEL_POS_X, aCustomLabelPosString.makeStringAndClear());
::sax::Converter::convertDouble(aCustomLabelPosString, xCustomLabelPosition.Secondary);
mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_CUSTOM_LABEL_POS_Y, aCustomLabelPosString.makeStringAndClear());
}
void SchXMLExportHelper_Impl::addPosition( const awt::Point & rPosition )
{
mrExport.GetMM100UnitConverter().convertMeasureToXML(
msStringBuffer, rPosition.X );
msString = msStringBuffer.makeStringAndClear();
mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_X, msString );
mrExport.GetMM100UnitConverter().convertMeasureToXML(
msStringBuffer, rPosition.Y );
msString = msStringBuffer.makeStringAndClear();
mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_Y, msString );
}
void SchXMLExportHelper_Impl::addPosition( const Reference< drawing::XShape >& xShape )
{
if( xShape.is())
addPosition( xShape->getPosition());
}
void SchXMLExportHelper_Impl::addSize( const awt::Size & rSize, bool bIsOOoNamespace)
{
mrExport.GetMM100UnitConverter().convertMeasureToXML(
msStringBuffer, rSize.Width );
msString = msStringBuffer.makeStringAndClear();
mrExport.AddAttribute( bIsOOoNamespace ? XML_NAMESPACE_CHART_EXT : XML_NAMESPACE_SVG , XML_WIDTH, msString );
mrExport.GetMM100UnitConverter().convertMeasureToXML(
msStringBuffer, rSize.Height);
msString = msStringBuffer.makeStringAndClear();
mrExport.AddAttribute( bIsOOoNamespace ? XML_NAMESPACE_CHART_EXT : XML_NAMESPACE_SVG, XML_HEIGHT, msString );
}
void SchXMLExportHelper_Impl::addSize( const Reference< drawing::XShape >& xShape )
{
if( xShape.is())
addSize( xShape->getSize() );
}
awt::Size SchXMLExportHelper_Impl::getPageSize( const Reference< chart2::XChartDocument > & xChartDoc )
{
awt::Size aSize( 8000 , 7000 );
uno::Reference< embed ::XVisualObject > xVisualObject( xChartDoc, uno::UNO_QUERY );
SAL_WARN_IF( !xVisualObject.is(), "xmloff.chart" , "need XVisualObject for page size" );
if( xVisualObject.is() )
aSize = xVisualObject->getVisualAreaSize( embed ::Aspects::MSOLE_CONTENT );
return aSize;
}
void SchXMLExportHelper_Impl::CollectAutoStyle( std::vector< XMLPropertyState >&& aStates )
{
if( !aStates.empty() )
maAutoStyleNameQueue.push( mrAutoStylePool.Add( XmlStyleFamily::SCH_CHART_ID, std::move(aStates) ));
}
void SchXMLExportHelper_Impl::CollectAutoTextStyle( const css::uno::Reference< beans::XPropertySet >& xTitlePropSet )
{
if (xTitlePropSet.is())
{
Sequence< uno::Reference< chart2::XFormattedString > > xFormattedTitle;
OUString aTitle;
if ((xTitlePropSet->getPropertyValue(u"String" _ustr) >>= aTitle) && !aTitle.isEmpty())
xTitlePropSet->getPropertyValue(u"FormattedStrings" _ustr) >>= xFormattedTitle;
if (xFormattedTitle.hasElements())
{
for (const uno::Reference<chart2::XFormattedString>& rxFS : xFormattedTitle)
{
Reference< beans::XPropertySet > xRunPropSet(rxFS, uno::UNO_QUERY);
mrExport.GetTextParagraphExport()->Add(XmlStyleFamily::TEXT_TEXT, xRunPropSet);
}
}
}
}
void SchXMLExportHelper_Impl::AddAutoStyleAttribute( const std::vector< XMLPropertyState >& aStates )
{
if( !aStates.empty() )
{
SAL_WARN_IF( maAutoStyleNameQueue.empty(), "xmloff.chart" , "Autostyle queue empty!" );
mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, maAutoStyleNameQueue.front() );
maAutoStyleNameQueue.pop();
}
}
void SchXMLExportHelper_Impl::exportText( const OUString& rText )
{
SchXMLTools::exportText( mrExport, rText, false/*bConvertTabsLFs*/ );
}
void SchXMLExportHelper_Impl::exportFormattedText( const css::uno::Reference< beans::XPropertySet >& xTitleProps )
{
SchXMLTools::exportFormattedText( mrExport, xTitleProps );
}
SchXMLExport::SchXMLExport(const Reference<uno::XComponentContext>& xContext,
OUString const& implementationName, SvXMLExportFlags nExportFlags)
: SvXMLExport(xContext, implementationName, util::MeasureUnit::CM, ::xmloff::token::XML_CHART,
nExportFlags)
, maAutoStylePool(new SchXMLAutoStylePoolP(*this))
, maExportHelper(new SchXMLExportHelper(*this, *maAutoStylePool))
{
if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
GetNamespaceMap_().Add( GetXMLToken(XML_NP_CHART_EXT), GetXMLToken(XML_N_CHART_EXT), XML_NAMESPACE_CHART_EXT);
}
SchXMLExport::~SchXMLExport()
{
}
ErrCode SchXMLExport::exportDoc( enum ::xmloff::token::XMLTokenEnum eClass )
{
maExportHelper->SetSourceShellID(GetSourceShellID());
maExportHelper->SetDestinationShellID(GetDestinationShellID());
Reference< chart2::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
maExportHelper->m_pImpl->InitRangeSegmentationProperties( xChartDoc );
return SvXMLExport::exportDoc( eClass );
}
void SchXMLExport::ExportMasterStyles_()
{
// not available in chart
SAL_INFO("xmloff.chart" , "Master Style Export requested. Not available for Chart" );
}
void SchXMLExport::collectAutoStyles()
{
SvXMLExport::collectAutoStyles();
if (mbAutoStylesCollected)
return;
// there are no styles that require their own autostyles
if( getExportFlags() & SvXMLExportFlags::CONTENT )
{
Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
if( xChartDoc.is())
{
maExportHelper->m_pImpl->collectAutoStyles( xChartDoc );
}
else
{
SAL_WARN("xmloff.chart" , "Couldn't export chart due to wrong XModel (must be XChartDocument)" );
}
}
mbAutoStylesCollected = true;
}
void SchXMLExport::ExportAutoStyles_()
{
collectAutoStyles();
if( getExportFlags() & SvXMLExportFlags::CONTENT )
{
Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
if( xChartDoc.is())
{
maExportHelper->m_pImpl->exportAutoStyles();
}
else
{
SAL_WARN("xmloff.chart" , "Couldn't export chart due to wrong XModel (must be XChartDocument)" );
}
}
}
void SchXMLExport::ExportContent_()
{
Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
if( xChartDoc.is())
{
// determine if data comes from the outside
bool bIncludeTable = true;
Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
if( xNewDoc.is())
{
// check if we have own data. If so we must not export the complete
// range string, as this is our only indicator for having own or
// external data. @todo: fix this in the file format!
Reference< lang::XServiceInfo > xDPServiceInfo( xNewDoc->getDataProvider(), uno::UNO_QUERY );
if( ! (xDPServiceInfo.is() && xDPServiceInfo->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" ))
{
bIncludeTable = false;
}
}
else
{
Reference< lang::XServiceInfo > xServ( xChartDoc, uno::UNO_QUERY );
if( xServ.is())
{
if( xServ->supportsService( u"com.sun.star.chart.ChartTableAddressSupplier" _ustr ))
{
Reference< beans::XPropertySet > xProp( xServ, uno::UNO_QUERY );
if( xProp.is())
{
Any aAny;
try
{
OUString sChartAddress;
aAny = xProp->getPropertyValue( u"ChartRangeAddress" _ustr );
aAny >>= sChartAddress;
maExportHelper->m_pImpl->SetChartRangeAddress( sChartAddress );
// do not include own table if there are external addresses
bIncludeTable = sChartAddress.isEmpty();
}
catch( const beans::UnknownPropertyException & )
{
SAL_WARN("xmloff.chart" , "Property ChartRangeAddress not supported by ChartDocument" );
}
}
}
}
}
maExportHelper->m_pImpl->exportChart( xChartDoc, bIncludeTable );
}
else
{
SAL_WARN("xmloff.chart" , "Couldn't export chart due to wrong XModel" );
}
}
rtl::Reference< XMLPropertySetMapper > const & SchXMLExport::GetPropertySetMapper() const
{
return maExportHelper->m_pImpl->GetPropertySetMapper();
}
void SchXMLExportHelper_Impl::InitRangeSegmentationProperties( const Reference< chart2::XChartDocument > & xChartDoc )
{
if( !xChartDoc.is())
return;
try
{
Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
SAL_WARN_IF( !xDataProvider.is(), "xmloff.chart" , "No DataProvider" );
if( xDataProvider.is())
{
Reference< chart2::data::XDataSource > xDataSource( lcl_pressUsedDataIntoRectangularFormat( xChartDoc, mbHasCategoryLabels ));
const Sequence< beans::PropertyValue > aArgs( xDataProvider->detectArguments( xDataSource ));
OUString sCellRange, sBrokenRange;
bool bBrokenRangeAvailable = false;
for( const auto& rArg : aArgs )
{
if ( rArg.Name == "CellRangeRepresentation" )
rArg.Value >>= sCellRange;
else if ( rArg.Name == "BrokenCellRangeForExport" )
{
if( rArg.Value >>= sBrokenRange )
bBrokenRangeAvailable = true;
}
else if ( rArg.Name == "DataRowSource" )
{
chart::ChartDataRowSource eRowSource;
rArg.Value >>= eRowSource;
mbRowSourceColumns = ( eRowSource == chart::ChartDataRowSource_COLUMNS );
}
else if ( rArg.Name == "SequenceMapping" )
rArg.Value >>= maSequenceMapping;
}
// #i79009# For Writer we have to export a broken version of the
// range, where every row number is not too large, so that older
// version can correctly read those files.
msChartAddress = (bBrokenRangeAvailable ? sBrokenRange : sCellRange);
if( !msChartAddress.isEmpty() )
{
// convert format to XML-conform one
Reference< chart2::data::XRangeXMLConversion > xConversion( xDataProvider, uno::UNO_QUERY );
if( xConversion.is())
msChartAddress = xConversion->convertRangeToXML( msChartAddress );
}
}
}
catch( const uno::Exception & )
{
DBG_UNHANDLED_EXCEPTION("xmloff.chart" );
}
}
// first version: everything goes in one storage
extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
com_sun_star_comp_Chart_XMLExporter_get_implementation(uno::XComponentContext* pCtx,
uno::Sequence<uno::Any> const& /*rSeq*/)
{
return cppu::acquire(
new SchXMLExport(pCtx, u"SchXMLExport.Compact" _ustr,
SvXMLExportFlags::ALL
^ (SvXMLExportFlags::SETTINGS | SvXMLExportFlags::MASTERSTYLES
| SvXMLExportFlags::SCRIPTS)));
}
// Oasis format
extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
com_sun_star_comp_Chart_XMLOasisExporter_get_implementation(uno::XComponentContext* pCtx,
uno::Sequence<uno::Any> const& /*rSeq*/)
{
return cppu::acquire(
new SchXMLExport(pCtx, u"SchXMLExport.Oasis.Compact" _ustr,
(SvXMLExportFlags::ALL
^ (SvXMLExportFlags::SETTINGS | SvXMLExportFlags::MASTERSTYLES
| SvXMLExportFlags::SCRIPTS))
| SvXMLExportFlags::OASIS));
}
// multiple storage version: one for content / styles / meta
extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
com_sun_star_comp_Chart_XMLStylesExporter_get_implementation(
uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
{
return cppu::acquire(new SchXMLExport(pCtx, u"SchXMLExport.Styles" _ustr, SvXMLExportFlags::STYLES));
}
// Oasis format
extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
com_sun_star_comp_Chart_XMLOasisStylesExporter_get_implementation(
uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
{
return cppu::acquire(new SchXMLExport(pCtx, u"SchXMLExport.Oasis.Styles" _ustr,
SvXMLExportFlags::STYLES | SvXMLExportFlags::OASIS));
}
extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
com_sun_star_comp_Chart_XMLContentExporter_get_implementation(
uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
{
return cppu::acquire(new SchXMLExport(pCtx, u"SchXMLExport.Content" _ustr,
SvXMLExportFlags::AUTOSTYLES | SvXMLExportFlags::CONTENT
| SvXMLExportFlags::FONTDECLS));
}
extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
com_sun_star_comp_Chart_XMLOasisContentExporter_get_implementation(
uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
{
return cppu::acquire(new SchXMLExport(pCtx, u"SchXMLExport.Oasis.Content" _ustr,
SvXMLExportFlags::AUTOSTYLES | SvXMLExportFlags::CONTENT
| SvXMLExportFlags::FONTDECLS
| SvXMLExportFlags::OASIS));
}
// Oasis format
extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
com_sun_star_comp_Chart_XMLOasisMetaExporter_get_implementation(
uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
{
return cppu::acquire(new SchXMLExport(pCtx, u"SchXMLExport.Oasis.Meta" _ustr,
SvXMLExportFlags::META | SvXMLExportFlags::OASIS));
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=90 H=98 G=94
¤ Dauer der Verarbeitung: 0.122 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland