tdf#165742 Step 5.8: Add minimal support for layout props
Add framework for import/export of chartex layout properties (cx:layoutPr). Also support the child cx:binning property, used for clusteredColumn charts (and perhaps others). This allows the binning property to round-trip successfully MSO -> LO -> MSO. Change-Id: I54bd1692ce4fb59dd70c9d4ac68f108c33cbca8a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199553 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
This commit is contained in:
committed by
Tomaž Vajngerl
parent
1981c314ce
commit
c26dd0e1f2
@@ -288,6 +288,19 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testChartexAxes)
|
||||
assertXPath(pXmlDoc, "/cx:chartSpace/cx:chart/cx:plotArea/cx:axis/cx:tickLabels", 2);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testChartexBinningXLSX)
|
||||
{
|
||||
loadFromFile(u"xlsx/paretoLine.xlsx");
|
||||
save(TestFilter::XLSX);
|
||||
xmlDocUniquePtr pXmlDoc = parseExport(u"xl/charts/chartEx1.xml"_ustr);
|
||||
CPPUNIT_ASSERT(pXmlDoc);
|
||||
|
||||
assertXPath(
|
||||
pXmlDoc,
|
||||
"/cx:chartSpace/cx:chart/cx:plotArea/cx:plotAreaRegion/cx:series/cx:layoutPr/cx:binning",
|
||||
"intervalClosed", u"r");
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testAxisTitleRotationXLSX)
|
||||
{
|
||||
loadFromFile(u"xlsx/axis_title_rotation.xlsx");
|
||||
|
||||
@@ -36,7 +36,8 @@ namespace chart::DataSeriesProperties
|
||||
PROP_DATASERIES_SHOW_LEGEND_ENTRY,
|
||||
PROP_DATASERIES_DELETED_LEGEND_ENTRIES,
|
||||
PROP_DATASERIES_SHOW_CUSTOM_LEADERLINES,
|
||||
PROP_DATASERIES_INVERT_NEGATIVE
|
||||
PROP_DATASERIES_INVERT_NEGATIVE,
|
||||
PROP_DATASERIES_INTERVAL_CLOSED
|
||||
};
|
||||
|
||||
void AddPropertiesToVector(
|
||||
|
||||
@@ -81,6 +81,12 @@ void DataSeriesProperties::AddPropertiesToVector(
|
||||
beans::PropertyAttribute::BOUND
|
||||
| beans::PropertyAttribute::MAYBEDEFAULT );
|
||||
|
||||
rOutProperties.emplace_back( "IntervalClosed",
|
||||
PROP_DATASERIES_INTERVAL_CLOSED,
|
||||
cppu::UnoType<sal_uInt32>::get(),
|
||||
beans::PropertyAttribute::BOUND
|
||||
| beans::PropertyAttribute::MAYBEVOID );
|
||||
|
||||
// add properties of service DataPointProperties
|
||||
DataPointProperties::AddPropertiesToVector( rOutProperties );
|
||||
}
|
||||
|
||||
@@ -237,6 +237,36 @@ public:
|
||||
virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
|
||||
};
|
||||
|
||||
|
||||
struct BinningModel;
|
||||
|
||||
/** Handler for a chartex binning context (cx:binning element).
|
||||
*/
|
||||
class BinningContext final : public ContextBase< BinningModel >
|
||||
{
|
||||
public:
|
||||
explicit BinningContext( ::oox::core::ContextHandler2Helper& rParent, BinningModel& rModel );
|
||||
virtual ~BinningContext() override;
|
||||
|
||||
virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
|
||||
void onCharacters( const OUString& rChars ) override;
|
||||
};
|
||||
|
||||
|
||||
struct LayoutPropsModel;
|
||||
|
||||
/** Handler for a chartex layout properties context (cx:layoutPr element).
|
||||
*/
|
||||
class LayoutPropsContext final : public ContextBase< LayoutPropsModel >
|
||||
{
|
||||
public:
|
||||
explicit LayoutPropsContext( ::oox::core::ContextHandler2Helper& rParent, LayoutPropsModel& rModel );
|
||||
virtual ~LayoutPropsContext() override;
|
||||
|
||||
virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
|
||||
};
|
||||
|
||||
|
||||
/** Handler for a data series context for chartex chart types (cx:series element
|
||||
* for boxWhisker, clusteredColumn/histogram, funnel, paretoLine, waterfall,
|
||||
* sunburst, treemap, and regionMap).
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#ifndef INCLUDED_OOX_DRAWINGML_CHART_SERIESMODEL_HXX
|
||||
#define INCLUDED_OOX_DRAWINGML_CHART_SERIESMODEL_HXX
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include <oox/drawingml/chart/datasourcemodel.hxx>
|
||||
#include <drawingml/chart/titlemodel.hxx>
|
||||
|
||||
@@ -174,6 +176,27 @@ struct DataPointModel
|
||||
~DataPointModel();
|
||||
};
|
||||
|
||||
struct BinningModel
|
||||
{
|
||||
enum ClosedSide {
|
||||
L,
|
||||
R
|
||||
};
|
||||
|
||||
std::optional<std::variant<double, sal_uInt32>> maBinSizeOrCount;
|
||||
std::optional<ClosedSide> meIntervalClosed; // Which side of an interval is closed
|
||||
|
||||
explicit BinningModel();
|
||||
};
|
||||
|
||||
struct LayoutPropsModel
|
||||
{
|
||||
typedef ModelRef< BinningModel > BinningRef;
|
||||
// There's also a CT_Aggregation but no content given
|
||||
|
||||
BinningRef mxBinning; // Binning settings.
|
||||
};
|
||||
|
||||
enum class DataSourceType: sal_Int32
|
||||
{
|
||||
CATEGORIES, /// Data point categories.
|
||||
@@ -192,6 +215,7 @@ struct SeriesModel
|
||||
typedef ModelRef< PictureOptionsModel > PictureOptionsRef;
|
||||
typedef ModelRef< TextModel > TextRef;
|
||||
typedef ModelRef< DataLabelsModel > DataLabelsRef;
|
||||
typedef ModelRef< LayoutPropsModel > LayoutPropsRef;
|
||||
|
||||
DataSourceMap maSources; /// Series source ranges.
|
||||
ErrorBarVector maErrorBars; /// All error bars of this series.
|
||||
@@ -202,6 +226,7 @@ struct SeriesModel
|
||||
ShapeRef mxMarkerProp; /// Data point marker formatting.
|
||||
TextRef mxText; /// Series title source.
|
||||
DataLabelsRef mxLabels; /// Data point label settings for all points.
|
||||
LayoutPropsRef mxLayoutPr; /// Chartex CT_SeriesLayoutProperties
|
||||
std::optional< sal_Int32 > monShape; /// 3D bar shape type.
|
||||
sal_Int32 mnExplosion; /// Pie slice moved from pie center.
|
||||
sal_Int32 mnIndex; /// Series index used for automatic formatting.
|
||||
|
||||
@@ -762,6 +762,119 @@ ContextHandlerRef SurfaceSeriesContext::onCreateContext( sal_Int32 nElement, con
|
||||
return SeriesContextBase::onCreateContext( nElement, rAttribs );
|
||||
}
|
||||
|
||||
//======
|
||||
// BinningContext
|
||||
//======
|
||||
BinningContext::BinningContext( ContextHandler2Helper& rParent, BinningModel& rModel ) :
|
||||
ContextBase( rParent, rModel )
|
||||
{
|
||||
}
|
||||
|
||||
BinningContext::~BinningContext()
|
||||
{
|
||||
}
|
||||
|
||||
void BinningContext::onCharacters( const OUString& rChars )
|
||||
{
|
||||
switch (getCurrentElement())
|
||||
{
|
||||
case CX_TOKEN( binSize ) :
|
||||
mrModel.maBinSizeOrCount = rChars.toDouble();
|
||||
break;
|
||||
case CX_TOKEN(binCount) :
|
||||
mrModel.maBinSizeOrCount = rChars.toUInt32();
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
ContextHandlerRef BinningContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
|
||||
{
|
||||
switch( getCurrentElement() )
|
||||
{
|
||||
case CX_TOKEN( layoutPr ):
|
||||
switch( nElement )
|
||||
{
|
||||
case CX_TOKEN( binSize ):
|
||||
mrModel.maBinSizeOrCount = rAttribs.getDouble( XML_val, 0.0 );
|
||||
return nullptr;
|
||||
case CX_TOKEN( binCount ):
|
||||
mrModel.maBinSizeOrCount = std::variant<double, sal_uInt32>(
|
||||
static_cast<sal_uInt32>(rAttribs.getInteger( XML_val, 0 )));
|
||||
|
||||
// TODO
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return ContextBase::onCreateContext( nElement, rAttribs );
|
||||
}
|
||||
|
||||
//======
|
||||
// LayoutPropsContext
|
||||
//======
|
||||
LayoutPropsContext::LayoutPropsContext( ContextHandler2Helper& rParent, LayoutPropsModel& rModel ) :
|
||||
ContextBase( rParent, rModel )
|
||||
{
|
||||
}
|
||||
|
||||
LayoutPropsContext::~LayoutPropsContext()
|
||||
{
|
||||
}
|
||||
|
||||
ContextHandlerRef LayoutPropsContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
|
||||
{
|
||||
switch( getCurrentElement() )
|
||||
{
|
||||
case CX_TOKEN( layoutPr ):
|
||||
switch( nElement )
|
||||
{
|
||||
case CX_TOKEN( parentLabelLayout ):
|
||||
// TODO
|
||||
return nullptr;
|
||||
case CX_TOKEN( regionLabelLayout ):
|
||||
// TODO
|
||||
return nullptr;
|
||||
case CX_TOKEN( visibility ):
|
||||
// TODO
|
||||
return nullptr;
|
||||
case CX_TOKEN( aggregation ):
|
||||
// The schema gives an empty definition for this type??
|
||||
// TODO
|
||||
return nullptr;
|
||||
case CX_TOKEN( binning ):
|
||||
{
|
||||
BinningModel& rB = mrModel.mxBinning.create();
|
||||
if (rAttribs.getString( XML_intervalClosed ) == "l") {
|
||||
rB.meIntervalClosed = BinningModel::ClosedSide::L;
|
||||
} else if (rAttribs.getString( XML_intervalClosed ) == "r") {
|
||||
rB.meIntervalClosed = BinningModel::ClosedSide::R;
|
||||
} else {
|
||||
assert(false); // no other options
|
||||
}
|
||||
// TODO: Also handle attributes underflow, overflow
|
||||
return new BinningContext( *this, rB );
|
||||
}
|
||||
case CX_TOKEN( geography ):
|
||||
// TODO
|
||||
return nullptr;
|
||||
case CX_TOKEN( statistics ):
|
||||
// TODO
|
||||
return nullptr;
|
||||
case CX_TOKEN( subtotals ):
|
||||
// TODO
|
||||
return nullptr;
|
||||
case CX_TOKEN( extLst ):
|
||||
// TODO
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return ContextBase::onCreateContext( nElement, rAttribs );
|
||||
}
|
||||
|
||||
//======
|
||||
// ChartexSeriesContext
|
||||
//======
|
||||
ChartexSeriesContext::ChartexSeriesContext( ContextHandler2Helper& rParent, SeriesModel& rModel, sal_Int32 nIndex ) :
|
||||
SeriesContextBase( rParent, rModel )
|
||||
{
|
||||
@@ -793,8 +906,7 @@ ContextHandlerRef ChartexSeriesContext::onCreateContext( sal_Int32 nElement, con
|
||||
mrModel.mnDataId = rAttribs.getInteger(XML_val, -1);
|
||||
return nullptr;
|
||||
case CX_TOKEN( layoutPr ):
|
||||
// This looks complicated. TODO
|
||||
return nullptr;
|
||||
return new LayoutPropsContext( *this, mrModel.mxLayoutPr.create());
|
||||
case CX_TOKEN( axisId ):
|
||||
mrModel.maAxisIds.push_back( rAttribs.getInteger( XML_val, -1 ) );
|
||||
return nullptr;
|
||||
|
||||
@@ -962,6 +962,17 @@ Reference< XDataSeries > SeriesConverter::createDataSeries( const TypeGroupConve
|
||||
aLabelsConv.convertFromModel( xDataSeries, rTypeGroup );
|
||||
}
|
||||
|
||||
// Layout properties
|
||||
if (mrModel.mxLayoutPr.is() && mrModel.mxLayoutPr->mxBinning.is()) {
|
||||
ModelRef<BinningModel> xBinning = mrModel.mxLayoutPr->mxBinning;
|
||||
|
||||
if (xBinning->meIntervalClosed) {
|
||||
sal_uInt32 nBinSideChar = xBinning->meIntervalClosed.value() ==
|
||||
BinningModel::ClosedSide::L ? 'l' : 'r';
|
||||
aSeriesProp.setProperty(PROP_IntervalClosed, nBinSideChar);
|
||||
}
|
||||
}
|
||||
|
||||
return xDataSeries;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,12 @@ DataPointModel::~DataPointModel()
|
||||
{
|
||||
}
|
||||
|
||||
BinningModel::BinningModel() :
|
||||
maBinSizeOrCount(),
|
||||
meIntervalClosed()
|
||||
{
|
||||
}
|
||||
|
||||
SeriesModel::SeriesModel(bool bMSO2007Doc) :
|
||||
mnExplosion( 0 ),
|
||||
mnIndex( -1 ),
|
||||
|
||||
@@ -3932,6 +3932,25 @@ void ChartExport::exportSeries_chartex( const Reference<chart2::XChartType>& xCh
|
||||
OString::number(nSeriesCnt++));
|
||||
|
||||
// layoutPr
|
||||
PropertySet aSeriesProp(rSeries);
|
||||
sal_uInt32 nIntervalClosedChar = '\0';
|
||||
if (aSeriesProp.getProperty(nIntervalClosedChar, PROP_IntervalClosed)) {
|
||||
pFS->startElement(FSNS(XML_cx, XML_layoutPr));
|
||||
|
||||
switch (nIntervalClosedChar) {
|
||||
case 'l' :
|
||||
pFS->singleElement(FSNS(XML_cx, XML_binning), XML_intervalClosed, "l");
|
||||
break;
|
||||
case 'r' :
|
||||
pFS->singleElement(FSNS(XML_cx, XML_binning), XML_intervalClosed, "r");
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
pFS->endElement(FSNS(XML_cx, XML_layoutPr));
|
||||
}
|
||||
|
||||
// axisId
|
||||
|
||||
|
||||
@@ -292,6 +292,7 @@ IncludeHiddenCells
|
||||
InputMessage
|
||||
InputTitle
|
||||
InterceptValue
|
||||
IntervalClosed
|
||||
InvalidUrl
|
||||
InvertNegative
|
||||
IsActive
|
||||
|
||||
@@ -500,6 +500,7 @@ afterAutospacing
|
||||
afterEffect
|
||||
afterGroup
|
||||
afterLines
|
||||
aggregation
|
||||
ahLst
|
||||
ahPolar
|
||||
ahXY
|
||||
@@ -880,6 +881,9 @@ bidi
|
||||
bidiVisual
|
||||
bilevel
|
||||
billions
|
||||
binCount
|
||||
binSize
|
||||
binning
|
||||
birds
|
||||
birdsFlight
|
||||
bisque
|
||||
@@ -2516,6 +2520,7 @@ gear6
|
||||
gear9
|
||||
gems
|
||||
general
|
||||
geography
|
||||
gfxdata
|
||||
ghostCol
|
||||
ghostRow
|
||||
@@ -2917,6 +2922,7 @@ interactiveSeq
|
||||
intercept
|
||||
intermediate
|
||||
interval
|
||||
intervalClosed
|
||||
intraSp
|
||||
inv
|
||||
invGamma
|
||||
@@ -3939,6 +3945,7 @@ parameter
|
||||
parameterType
|
||||
parameters
|
||||
parent
|
||||
parentLabelLayout
|
||||
parentSet
|
||||
paretoLine
|
||||
parsePre
|
||||
@@ -4346,6 +4353,7 @@ refreshOnLoad
|
||||
refreshedBy
|
||||
refreshedDate
|
||||
refreshedVersion
|
||||
regionLabelLayout
|
||||
regionMap
|
||||
regroupid
|
||||
regrouptable
|
||||
@@ -5035,6 +5043,7 @@ startarrow
|
||||
startarrowlength
|
||||
startarrowwidth
|
||||
state
|
||||
statistics
|
||||
status
|
||||
statusBar
|
||||
statusText
|
||||
@@ -5116,6 +5125,7 @@ subject
|
||||
subscript
|
||||
subsetted
|
||||
subtotal
|
||||
subtotals
|
||||
subtotalCaption
|
||||
subtotalHiddenItems
|
||||
subtotalTop
|
||||
|
||||
Reference in New Issue
Block a user