tdf#165742 Step 5.12: Improve axis handling
Implement better handling of <cx:axisId> and <cx:axis> tags. In particular, ensure we output the correct number of axisId elements, based on the chart type, and ensure that corresponding axes (and only those) are captured in the appropriate <cx:axis> data. Signed-off-by: Kurt Nordback <kurt.nordback@collabora.com> Change-Id: Ifed17afc05c70dced89e75f18dcf375fdef97f6e Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/5775 Reviewed-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com> Tested-by: Jenkins CPCI <releng@collaboraoffice.com> (cherry picked from commit 833f782bca98c891097d887a45365bc7a5322c4d) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/209239 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
committed by
Xisco Fauli
parent
ad2cacedde
commit
c26599741c
@@ -304,10 +304,7 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testChartexPPTX)
|
||||
|
||||
assertXPath(pXmlDoc, "/cx:chartSpace/cx:chart/cx:plotArea/cx:plotAreaRegion/cx:series", 3, 0,
|
||||
"layoutId", u"funnel");
|
||||
// There should be only one axis, where currently there are multiple.
|
||||
// However, that's a separate problem from the gapWidth output. So just
|
||||
// reference the first for now.
|
||||
assertXPath(pXmlDoc, "/cx:chartSpace/cx:chart/cx:plotArea/cx:axis[1]/cx:catScaling", "gapWidth",
|
||||
assertXPath(pXmlDoc, "/cx:chartSpace/cx:chart/cx:plotArea/cx:axis/cx:catScaling", "gapWidth",
|
||||
u"2.19");
|
||||
}
|
||||
|
||||
@@ -318,7 +315,7 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testChartexGapWidth)
|
||||
xmlDocUniquePtr pXmlDoc = parseExport(u"xl/charts/chartEx1.xml"_ustr);
|
||||
CPPUNIT_ASSERT(pXmlDoc);
|
||||
|
||||
assertXPath(pXmlDoc, "/cx:chartSpace/cx:chart/cx:plotArea/cx:axis[1]/cx:catScaling", "gapWidth",
|
||||
assertXPath(pXmlDoc, "/cx:chartSpace/cx:chart/cx:plotArea/cx:axis/cx:catScaling", "gapWidth",
|
||||
u"2.47");
|
||||
}
|
||||
|
||||
@@ -329,7 +326,7 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testChartexGapWidth2)
|
||||
xmlDocUniquePtr pXmlDoc = parseExport(u"xl/charts/chartEx1.xml"_ustr);
|
||||
CPPUNIT_ASSERT(pXmlDoc);
|
||||
|
||||
assertXPath(pXmlDoc, "/cx:chartSpace/cx:chart/cx:plotArea/cx:axis[1]/cx:catScaling", "gapWidth",
|
||||
assertXPath(pXmlDoc, "/cx:chartSpace/cx:chart/cx:plotArea/cx:axis/cx:catScaling", "gapWidth",
|
||||
u"2.55");
|
||||
}
|
||||
|
||||
@@ -376,6 +373,52 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testChartexNoSpPr)
|
||||
0);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testChartexAxisIdPerSeries)
|
||||
{
|
||||
// Waterfall charts have two axes. Each series should reference both via
|
||||
// cx:axisId, and the plot area should contain exactly two cx:axis
|
||||
// elements.
|
||||
loadFromFile(u"xlsx/waterfall2.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[1]/cx:axisId", 2);
|
||||
assertXPath(pXmlDoc, "/cx:chartSpace/cx:chart/cx:plotArea/cx:axis", 2);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testFunnelCharts)
|
||||
{
|
||||
// Funnel charts have one axis. (At least as MSO produces them -- arguably
|
||||
// there are two dimensions, but we'll stick with the MSO convention for
|
||||
// now.) The series should reference it via
|
||||
// cx:axisId, and the plot area should contain exactly one cx:axis
|
||||
// elements.
|
||||
loadFromFile(u"xlsx/funnel1.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:axisId", 1);
|
||||
assertXPath(pXmlDoc, "/cx:chartSpace/cx:chart/cx:plotArea/cx:axis", 1);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testRegionMap)
|
||||
{
|
||||
// RegionMap charts have no axes. The series should have no cx:axisId and the plot
|
||||
// area should have no cx:axis.
|
||||
loadFromFile(u"xlsx/regionMap.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:axisId", 0);
|
||||
assertXPath(pXmlDoc, "/cx:chartSpace/cx:chart/cx:plotArea/cx:axis", 0);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testAxisTitleRotationXLSX)
|
||||
{
|
||||
loadFromFile(u"xlsx/axis_title_rotation.xlsx");
|
||||
|
||||
@@ -57,7 +57,8 @@ namespace chart::DataSeriesProperties
|
||||
PROP_DATASERIES_GEOCACHE_BINARY,
|
||||
PROP_DATASERIES_GEOCACHE_CLEAR_DATA,
|
||||
PROP_DATASERIES_HAS_EXPLICIT_FILL,
|
||||
PROP_DATASERIES_HAS_DATA_LABELS
|
||||
PROP_DATASERIES_HAS_DATA_LABELS,
|
||||
PROP_DATASERIES_CHARTEX_AXIS_IDS
|
||||
};
|
||||
|
||||
void AddPropertiesToVector(
|
||||
|
||||
@@ -79,7 +79,9 @@ enum
|
||||
PROP_AXIS_TRY_STAGGERING_FIRST,
|
||||
PROP_AXIS_MAJOR_ORIGIN,
|
||||
PROP_AXIS_HAS_EXPLICIT_SPPR,
|
||||
PROP_AXIS_HAS_EXPLICIT_TXPR
|
||||
PROP_AXIS_HAS_EXPLICIT_TXPR,
|
||||
PROP_AXIS_ID,
|
||||
PROP_AXIS_CAT_NOT_VAL
|
||||
};
|
||||
|
||||
void lcl_AddPropertiesToVector(
|
||||
@@ -217,6 +219,20 @@ void lcl_AddPropertiesToVector(
|
||||
beans::PropertyAttribute::BOUND
|
||||
| beans::PropertyAttribute::MAYBEVOID );
|
||||
|
||||
// Chartex only: the cx:axis id attribute value from import, preserved
|
||||
// so export can reuse the same id and so series-to-axis links survive
|
||||
// round-trip.
|
||||
rOutProperties.emplace_back( "AxisId",
|
||||
PROP_AXIS_ID,
|
||||
cppu::UnoType<sal_Int32>::get(),
|
||||
beans::PropertyAttribute::BOUND
|
||||
| beans::PropertyAttribute::MAYBEVOID );
|
||||
rOutProperties.emplace_back( "CatNotVal",
|
||||
PROP_AXIS_CAT_NOT_VAL,
|
||||
cppu::UnoType<bool>::get(),
|
||||
beans::PropertyAttribute::BOUND
|
||||
| beans::PropertyAttribute::MAYBEVOID );
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -208,6 +208,14 @@ void DataSeriesProperties::AddPropertiesToVector(
|
||||
beans::PropertyAttribute::BOUND
|
||||
| beans::PropertyAttribute::MAYBEVOID );
|
||||
|
||||
// Chartex only: the cx:axisId values listed on the cx:series element at
|
||||
// import. Empty for axis-less chartex types (regionMap, sunburst, treemap).
|
||||
rOutProperties.emplace_back( "ChartexAxisIds",
|
||||
PROP_DATASERIES_CHARTEX_AXIS_IDS,
|
||||
cppu::UnoType<uno::Sequence<sal_Int32>>::get(),
|
||||
beans::PropertyAttribute::BOUND
|
||||
| beans::PropertyAttribute::MAYBEVOID );
|
||||
|
||||
// add properties of service DataPointProperties
|
||||
DataPointProperties::AddPropertiesToVector( rOutProperties );
|
||||
}
|
||||
|
||||
@@ -77,21 +77,23 @@ namespace oox::drawingml {
|
||||
|
||||
struct LabelPlacementParam;
|
||||
|
||||
enum AxesType
|
||||
enum AxisType
|
||||
{
|
||||
AXIS_PRIMARY_X = 1,
|
||||
AXIS_PRIMARY_Y = 2,
|
||||
AXIS_PRIMARY_Z = 3,
|
||||
AXIS_SECONDARY_X = 4,
|
||||
AXIS_SECONDARY_Y = 5
|
||||
AXIS_PRIMARY_X,
|
||||
AXIS_PRIMARY_Y,
|
||||
AXIS_PRIMARY_Z,
|
||||
AXIS_SECONDARY_X,
|
||||
AXIS_SECONDARY_Y,
|
||||
AXIS_CATEGORY, // used for chartex
|
||||
AXIS_VALUE // used for chartex
|
||||
};
|
||||
|
||||
struct AxisIdPair{
|
||||
AxesType nAxisType;
|
||||
AxisType nAxisType;
|
||||
sal_Int32 nAxisId;
|
||||
sal_Int32 nCrossAx;
|
||||
|
||||
AxisIdPair(AxesType nType, sal_Int32 nId, sal_Int32 nAx)
|
||||
AxisIdPair(AxisType nType, sal_Int32 nId, sal_Int32 nAx)
|
||||
: nAxisType(nType)
|
||||
, nAxisId(nId)
|
||||
, nCrossAx(nAx)
|
||||
@@ -273,7 +275,8 @@ private:
|
||||
|
||||
void exportManualLayout(const css::chart2::RelativePosition& rPos, const css::chart2::RelativeSize& rSize, const bool bIsExcludingDiagramPositioning);
|
||||
|
||||
void exportAxes( bool bIsChartex );
|
||||
void exportAxes_chart();
|
||||
void exportAxes_chartex();
|
||||
void exportAxis(const AxisIdPair& rAxisIdPair,
|
||||
bool bIsChartex);
|
||||
void exportOneAxis_chart(
|
||||
@@ -291,7 +294,9 @@ private:
|
||||
const css::uno::Reference< css::beans::XPropertySet >& xMinorGrid,
|
||||
sal_Int32 nAxisType,
|
||||
const AxisIdPair& rAxisIdPair);
|
||||
void createAxes(bool bPrimaryAxes, bool bCheckCombinedAxes, bool bIsChartex);
|
||||
void createAxes_chart(bool bPrimaryAxes, bool bCheckCombinedAxes);
|
||||
void createAxes_chartex(
|
||||
const css::uno::Reference<css::chart2::XDataSeries>& xSeries);
|
||||
void exportView3D();
|
||||
bool isDeep3dChart();
|
||||
|
||||
|
||||
@@ -61,7 +61,8 @@ public:
|
||||
void convertFromModel(
|
||||
const css::uno::Reference<css::chart2::XCoordinateSystem>& rxCoordSystem,
|
||||
RefVector<TypeGroupConverter>& rTypeGroups, const AxisModel* pCrossingAxis,
|
||||
sal_Int32 nAxesSetIdx, sal_Int32 nAxisIdx, bool bUseFixedInnerSize);
|
||||
sal_Int32 nAxesSetIdx, sal_Int32 nAxisIdx, bool bUseFixedInnerSize,
|
||||
ChartType eCT);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ struct AxisModel
|
||||
ShapeRef mxMajorGridLines; /// Major grid lines formatting.
|
||||
ShapeRef mxMinorGridLines; /// Minor grid lines formatting.
|
||||
NumberFormat maNumberFormat; /// Number format for axis tick labels.
|
||||
std::optional<bool> mobCatNotVal; /// True if cat axis, false if val axis (for chartex)
|
||||
std::optional< double > mofCrossesAt; /// Position on this axis where another axis crosses.
|
||||
std::optional< double > mofMajorUnit; /// Unit for major tick marks on date/value axis.
|
||||
std::optional< double > mofMinorUnit; /// Unit for minor tick marks on date/value axis.
|
||||
|
||||
@@ -243,6 +243,7 @@ struct SeriesModel
|
||||
sal_Int32 mnMarkerSize; /// Size of the series line marker (2...72).
|
||||
sal_Int32 mnMarkerSymbol; /// Series line marker symbol.
|
||||
sal_Int32 mnOrder; /// Series order.
|
||||
sal_Int32 mnTypeId; /// Chart type
|
||||
sal_Int32 mnDataId; /// Reference to correct data chunk (chartex)
|
||||
bool mbBubble3d; /// True = show bubbles with 3D shade.
|
||||
bool mbInvertNeg; /// True = invert negative data points.
|
||||
@@ -251,7 +252,7 @@ struct SeriesModel
|
||||
maAxisIds; /// List of axis identifiers used
|
||||
// (Only used by chartex)
|
||||
|
||||
explicit SeriesModel(bool bMSO2007Doc);
|
||||
explicit SeriesModel(bool bMSO2007Doc, sal_Int32 nTypeId = -1);
|
||||
~SeriesModel();
|
||||
};
|
||||
|
||||
|
||||
@@ -309,9 +309,11 @@ ContextHandlerRef CxAxisContext::onCreateContext( sal_Int32 nElement, const Attr
|
||||
if( isRootElement() ) switch( nElement )
|
||||
{
|
||||
case CX_TOKEN(catScaling) :
|
||||
mrModel.mobCatNotVal = true;
|
||||
mrModel.mofGapWidth = rAttribs.getDouble(XML_gapWidth);
|
||||
return nullptr;
|
||||
case CX_TOKEN(valScaling) :
|
||||
mrModel.mobCatNotVal = false;
|
||||
mrModel.mofMax = rAttribs.getDouble(XML_max);
|
||||
mrModel.mofMin = rAttribs.getDouble(XML_min);
|
||||
mrModel.mofMajorUnit = rAttribs.getDouble(XML_majorUnit);
|
||||
|
||||
@@ -146,7 +146,8 @@ AxisConverter::~AxisConverter()
|
||||
void AxisConverter::convertFromModel(const Reference<XCoordinateSystem>& rxCoordSystem,
|
||||
RefVector<TypeGroupConverter>& rTypeGroups,
|
||||
const AxisModel* pCrossingAxis, sal_Int32 nAxesSetIdx,
|
||||
sal_Int32 nAxisIdx, bool bUseFixedInnerSize)
|
||||
sal_Int32 nAxisIdx, bool bUseFixedInnerSize,
|
||||
ChartType eCT)
|
||||
{
|
||||
if (rTypeGroups.empty())
|
||||
return;
|
||||
@@ -226,6 +227,15 @@ void AxisConverter::convertFromModel(const Reference<XCoordinateSystem>& rxCoord
|
||||
if (mrModel.mxTextProp.is())
|
||||
aAxisProp.setProperty(PROP_HasExplicitTxPr, true);
|
||||
|
||||
// Chartex only: preserve the cx:axis id so export can re-emit it and
|
||||
// so series-to-axis links can be reconstructed from the chart2 model.
|
||||
if (eCT == ChartType::CX && mrModel.mnAxisId >= 0) {
|
||||
aAxisProp.setProperty(PROP_AxisId, mrModel.mnAxisId);
|
||||
if (mrModel.mobCatNotVal) {
|
||||
aAxisProp.setProperty(PROP_CatNotVal, mrModel.mobCatNotVal.value());
|
||||
}
|
||||
}
|
||||
|
||||
// no X axis line in radar charts
|
||||
if( (nAxisIdx == API_X_AXIS) && (rTypeInfo.meTypeCategory == TYPECATEGORY_RADAR) )
|
||||
mrModel.mxShapeProp.getOrCreate().getLineProperties().maLineFill.moFillType = XML_noFill;
|
||||
|
||||
@@ -182,13 +182,6 @@ ContextHandlerRef PlotAreaContext::onCreateContext( sal_Int32 nElement, [[maybe_
|
||||
if (rAttribs.hasAttribute(XML_id)) {
|
||||
sal_Int32 nId = rAttribs.getInteger(XML_id, -1);
|
||||
// TODO: also handle attribute "hidden"
|
||||
|
||||
// Add the id to the axis id list in the (fictitious)
|
||||
// type group
|
||||
std::shared_ptr<TypeGroupModel> aTGM =
|
||||
mrModel.maTypeGroups.get(mrModel.maTypeGroups.size() - 1);
|
||||
aTGM->maAxisIds.push_back(nId);
|
||||
|
||||
return new CxAxisContext(*this, mrModel.maAxes.create(nElement, mrModel.meCT), nId);
|
||||
} else {
|
||||
return nullptr;
|
||||
@@ -240,7 +233,8 @@ ContextHandlerRef PlotAreaContext::onCreateContext( sal_Int32 nElement, [[maybe_
|
||||
mrModel.maTypeGroups.create(nTypeId, false);
|
||||
std::shared_ptr<TypeGroupModel> aTGM =
|
||||
mrModel.maTypeGroups.get(mrModel.maTypeGroups.size() - 1);
|
||||
return new ChartexSeriesContext(*this, aTGM->maSeries.create(false), mrModel.mnCurSeriesIdx++);
|
||||
return new ChartexSeriesContext(*this,
|
||||
aTGM->maSeries.create(false, nTypeId), mrModel.mnCurSeriesIdx++);
|
||||
}
|
||||
return nullptr;
|
||||
case CX_TOKEN(plotSurface) :
|
||||
|
||||
@@ -228,10 +228,10 @@ void AxesSetConverter::convertFromModel( const Reference< XDiagram >& rxDiagram,
|
||||
|
||||
AxisConverter aXAxisConv( *this, *xXAxis );
|
||||
aXAxisConv.convertFromModel(xCoordSystem, aTypeGroups, xYAxis.get(), nAxesSetIdx,
|
||||
API_X_AXIS, bUseFixedInnerSize);
|
||||
API_X_AXIS, bUseFixedInnerSize, eCT);
|
||||
AxisConverter aYAxisConv( *this, *xYAxis );
|
||||
aYAxisConv.convertFromModel(xCoordSystem, aTypeGroups, xXAxis.get(), nAxesSetIdx,
|
||||
API_Y_AXIS, bUseFixedInnerSize);
|
||||
API_Y_AXIS, bUseFixedInnerSize, eCT);
|
||||
|
||||
if( rFirstTypeGroup.isDeep3dChart() )
|
||||
{
|
||||
@@ -239,7 +239,7 @@ void AxesSetConverter::convertFromModel( const Reference< XDiagram >& rxDiagram,
|
||||
mrModel.maAxes, API_Z_AXIS, C_TOKEN( serAx ), eCT );
|
||||
AxisConverter aZAxisConv( *this, *xZAxis );
|
||||
aZAxisConv.convertFromModel(xCoordSystem, aTypeGroups, nullptr, nAxesSetIdx,
|
||||
API_Z_AXIS, bUseFixedInnerSize);
|
||||
API_Z_AXIS, bUseFixedInnerSize, eCT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -412,6 +412,115 @@ void PlotAreaConverter::convertFromModel( View3DModel& rView3DModel,
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer the series-level axis ids, given in chartex, to the
|
||||
// type group. This doesn't work if there are multiple series
|
||||
// per type group, but I don't think that should happen.
|
||||
for (auto const& typeGroup : mrModel.maTypeGroups)
|
||||
{
|
||||
if( !typeGroup->maSeries.empty() )
|
||||
{
|
||||
for (auto const& elemSeries : typeGroup->maSeries) {
|
||||
// First fill in any implicit axes. MS Office produces files
|
||||
// where some series axis ids are implicit. Make those explicit.
|
||||
switch (elemSeries->mnTypeId) {
|
||||
case CX_TOKEN(boxWhisker):
|
||||
case CX_TOKEN(clusteredColumn):
|
||||
case CX_TOKEN(paretoLine):
|
||||
case CX_TOKEN(waterfall):
|
||||
{
|
||||
bool bNeedCat = false, bNeedVal = false;
|
||||
// Should have two axes, one category and one value
|
||||
// (though they may be hidden)
|
||||
if (elemSeries->maAxisIds.size() == 0) {
|
||||
bNeedCat = bNeedVal = true;
|
||||
} else if (elemSeries->maAxisIds.size() == 1) {
|
||||
assert(aAxisMap[elemSeries->maAxisIds[0]]->mobCatNotVal.has_value());
|
||||
bool bCatNotVal = aAxisMap[elemSeries->maAxisIds[0]]->mobCatNotVal.value();
|
||||
if (bCatNotVal) {
|
||||
// have cat, need val
|
||||
bNeedVal = true;
|
||||
} else {
|
||||
// have val, need cat
|
||||
bNeedCat = true;
|
||||
}
|
||||
} else if (elemSeries->maAxisIds.size() == 2) {
|
||||
assert(aAxisMap[elemSeries->maAxisIds[0]]->mobCatNotVal.has_value());
|
||||
assert(aAxisMap[elemSeries->maAxisIds[1]]->mobCatNotVal.has_value());
|
||||
[[maybe_unused]] bool bCatNotVal0 = aAxisMap[elemSeries->maAxisIds[0]]->mobCatNotVal.value();
|
||||
[[maybe_unused]] bool bCatNotVal1 = aAxisMap[elemSeries->maAxisIds[1]]->mobCatNotVal.value();
|
||||
|
||||
// There should be one cat axis and one val axis
|
||||
assert((bCatNotVal0 && !bCatNotVal1) || (!bCatNotVal0 && bCatNotVal1));
|
||||
} else {
|
||||
// more than two axes: don't know what to do with this
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (bNeedCat) {
|
||||
// Add the first cat axis
|
||||
for (const auto& [nId, aAxisModel] : aAxisMap) {
|
||||
if (aAxisModel->mobCatNotVal.has_value() &&
|
||||
aAxisModel->mobCatNotVal.value()) {
|
||||
elemSeries->maAxisIds.push_back(nId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bNeedVal) {
|
||||
// Add the first cat axis
|
||||
for (const auto& [nId, aAxisModel] : aAxisMap) {
|
||||
if (aAxisModel->mobCatNotVal.has_value() &&
|
||||
!aAxisModel->mobCatNotVal.value()) {
|
||||
elemSeries->maAxisIds.push_back(nId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CX_TOKEN(funnel):
|
||||
{
|
||||
bool bNeedCat = false;
|
||||
// Should have one category axis (though it may be hidden)
|
||||
// Funnel never seems to have a value axis
|
||||
if (elemSeries->maAxisIds.size() == 0) {
|
||||
bNeedCat = true;
|
||||
} else if (elemSeries->maAxisIds.size() == 1) {
|
||||
assert(aAxisMap[elemSeries->maAxisIds[0]]->mobCatNotVal.has_value());
|
||||
[[maybe_unused]] bool bCatNotVal = aAxisMap[elemSeries->maAxisIds[0]]->mobCatNotVal.value();
|
||||
|
||||
assert(bCatNotVal);
|
||||
} else {
|
||||
// more than one axis: don't know what to do with this
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (bNeedCat) {
|
||||
// Add the first cat axis
|
||||
for (const auto& [nId, aAxisModel] : aAxisMap) {
|
||||
if (aAxisModel->mobCatNotVal.has_value() &&
|
||||
aAxisModel->mobCatNotVal.value()) {
|
||||
elemSeries->maAxisIds.push_back(nId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (typeGroup->maAxisIds.empty()) {
|
||||
if (!elemSeries->maAxisIds.empty()) {
|
||||
assert(typeGroup->maSeries.size() == 1);
|
||||
typeGroup->maAxisIds = elemSeries->maAxisIds;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// group the type group models into different axes sets
|
||||
typedef ModelVector< AxesSetModel > AxesSetVector;
|
||||
AxesSetVector aAxesSets;
|
||||
@@ -450,8 +559,9 @@ void PlotAreaConverter::convertFromModel( View3DModel& rView3DModel,
|
||||
pAxesSet->maTypeGroups.push_back( typeGroup );
|
||||
|
||||
// collect the maximum series index for automatic series formatting
|
||||
for (auto const& elemSeries : typeGroup->maSeries)
|
||||
for (auto const& elemSeries : typeGroup->maSeries) {
|
||||
nMaxSeriesIdx = ::std::max( nMaxSeriesIdx, elemSeries->mnIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
getFormatter().setMaxSeriesIndex( nMaxSeriesIdx );
|
||||
|
||||
@@ -1032,6 +1032,16 @@ Reference< XDataSeries > SeriesConverter::createDataSeries( const TypeGroupConve
|
||||
convertGeography(*rLPR->mxGeography, aSeriesProp);
|
||||
}
|
||||
}
|
||||
|
||||
// Chartex only: preserve the cx:axisId list from the series so export
|
||||
// can emit the matching cx:axisId children and the right number of
|
||||
// cx:axis elements.
|
||||
if (!mrModel.maAxisIds.empty())
|
||||
{
|
||||
aSeriesProp.setProperty(PROP_ChartexAxisIds,
|
||||
comphelper::containerToSequence(mrModel.maAxisIds));
|
||||
}
|
||||
|
||||
return xDataSeries;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,12 +112,13 @@ BinningModel::BinningModel() :
|
||||
{
|
||||
}
|
||||
|
||||
SeriesModel::SeriesModel(bool bMSO2007Doc) :
|
||||
SeriesModel::SeriesModel(bool bMSO2007Doc, sal_Int32 nTypeId) :
|
||||
mnExplosion( 0 ),
|
||||
mnIndex( -1 ),
|
||||
mnMarkerSize( 5 ),
|
||||
mnMarkerSymbol( XML_auto ),
|
||||
mnOrder( -1 ),
|
||||
mnTypeId(nTypeId),
|
||||
mnDataId(-1),
|
||||
mbBubble3d( !bMSO2007Doc ),
|
||||
mbInvertNeg( !bMSO2007Doc ),
|
||||
|
||||
@@ -2876,7 +2876,7 @@ void ChartExport::exportPlotArea(const Reference< css::chart::XChartDocument >&
|
||||
pFS->singleElement(FSNS(XML_c, XML_barDir), XML_val, "col");
|
||||
pFS->singleElement(FSNS(XML_c, XML_grouping), XML_val, "clustered");
|
||||
pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, "0");
|
||||
createAxes(true, false, false);
|
||||
createAxes_chart(true, false);
|
||||
pFS->endElement(FSNS(XML_c, XML_barChart));
|
||||
}
|
||||
|
||||
@@ -3043,7 +3043,11 @@ void ChartExport::exportPlotArea(const Reference< css::chart::XChartDocument >&
|
||||
}
|
||||
|
||||
//Axis Data
|
||||
exportAxes(bIsChartex);
|
||||
if (bIsChartex) {
|
||||
exportAxes_chartex();
|
||||
} else {
|
||||
exportAxes_chart();
|
||||
}
|
||||
|
||||
if (!bIsChartex) {
|
||||
// Data Table
|
||||
@@ -3451,7 +3455,7 @@ void ChartExport::exportAreaChart( const Reference< chart2::XChartType >& xChart
|
||||
if (splitDataSeries.hasElements())
|
||||
exportSeries_chart(xChartType, splitDataSeries, bPrimaryAxes);
|
||||
|
||||
createAxes(bPrimaryAxes, true, false);
|
||||
createAxes_chart(bPrimaryAxes, true);
|
||||
//exportAxesId(bPrimaryAxes);
|
||||
|
||||
pFS->endElement(FSNS(XML_c, nTypeId));
|
||||
@@ -3555,7 +3559,7 @@ void ChartExport::exportBarChart(const Reference< chart2::XChartType >& xChartTy
|
||||
}
|
||||
}
|
||||
|
||||
createAxes(bPrimaryAxes, true, false);
|
||||
createAxes_chart(bPrimaryAxes, true);
|
||||
|
||||
pFS->endElement(FSNS(XML_c, nTypeId));
|
||||
}
|
||||
@@ -3580,7 +3584,7 @@ void ChartExport::exportBubbleChart( const Reference< chart2::XChartType >& xCha
|
||||
if (splitDataSeries.hasElements())
|
||||
exportSeries_chart(xChartType, splitDataSeries, bPrimaryAxes);
|
||||
|
||||
createAxes(bPrimaryAxes, true, false);
|
||||
createAxes_chart(bPrimaryAxes, true);
|
||||
|
||||
pFS->endElement(FSNS(XML_c, XML_bubbleChart));
|
||||
}
|
||||
@@ -3598,8 +3602,6 @@ void ChartExport::exportChartex( const Reference< chart2::XChartType >& xChartTy
|
||||
if (!splitDataSeries.hasElements())
|
||||
continue;
|
||||
|
||||
createAxes(true, false, true);
|
||||
|
||||
//exportVaryColors(xChartType);
|
||||
|
||||
exportSeries_chartex(xChartType, splitDataSeries, sTypeName);
|
||||
@@ -3737,7 +3739,7 @@ void ChartExport::exportLineChart( const Reference< chart2::XChartType >& xChart
|
||||
pFS->singleElement(FSNS(XML_c, XML_marker), XML_val, marker);
|
||||
}
|
||||
|
||||
createAxes(bPrimaryAxes, true, false);
|
||||
createAxes_chart(bPrimaryAxes, true);
|
||||
|
||||
pFS->endElement( FSNS( XML_c, nTypeId ) );
|
||||
}
|
||||
@@ -3782,7 +3784,7 @@ void ChartExport::exportRadarChart( const Reference< chart2::XChartType >& xChar
|
||||
exportVaryColors(xChartType);
|
||||
bool bPrimaryAxes = true;
|
||||
exportAllSeries(xChartType, bPrimaryAxes);
|
||||
createAxes(bPrimaryAxes, true, false);
|
||||
createAxes_chart(bPrimaryAxes, true);
|
||||
|
||||
pFS->endElement( FSNS( XML_c, XML_radarChart ) );
|
||||
}
|
||||
@@ -3812,7 +3814,7 @@ void ChartExport::exportScatterChartSeries( const Reference< chart2::XChartType
|
||||
bool bPrimaryAxes = true;
|
||||
if (pSeries)
|
||||
exportSeries_chart(xChartType, *pSeries, bPrimaryAxes);
|
||||
createAxes(bPrimaryAxes, true, false);
|
||||
createAxes_chart(bPrimaryAxes, true);
|
||||
//exportAxesId(bPrimaryAxes);
|
||||
|
||||
pFS->endElement( FSNS( XML_c, XML_scatterChart ) );
|
||||
@@ -3860,7 +3862,7 @@ void ChartExport::exportStockChart( const Reference< chart2::XChartType >& xChar
|
||||
exportUpDownBars(xChartType);
|
||||
}
|
||||
|
||||
createAxes(bPrimaryAxes, true, false);
|
||||
createAxes_chart(bPrimaryAxes, true);
|
||||
|
||||
pFS->endElement(FSNS(XML_c, XML_stockChart));
|
||||
}
|
||||
@@ -3935,7 +3937,7 @@ void ChartExport::exportSurfaceChart( const Reference< chart2::XChartType >& xCh
|
||||
exportVaryColors(xChartType);
|
||||
bool bPrimaryAxes = true;
|
||||
exportAllSeries(xChartType, bPrimaryAxes);
|
||||
createAxes(bPrimaryAxes, true, false);
|
||||
createAxes_chart(bPrimaryAxes, true);
|
||||
|
||||
pFS->endElement( FSNS( XML_c, nTypeId ) );
|
||||
}
|
||||
@@ -4448,6 +4450,7 @@ void ChartExport::exportSeries_chartex( const Reference<chart2::XChartType>& xCh
|
||||
}
|
||||
|
||||
// axisId
|
||||
createAxes_chartex(rSeries);
|
||||
|
||||
// extLst
|
||||
|
||||
@@ -4812,7 +4815,7 @@ void ChartExport::InitPlotArea( )
|
||||
}
|
||||
}
|
||||
|
||||
void ChartExport::exportAxes( bool bIsChartex )
|
||||
void ChartExport::exportAxes_chart()
|
||||
{
|
||||
sal_Int32 nSize = maAxes.size();
|
||||
// let's export the axis types in the right order
|
||||
@@ -4821,11 +4824,18 @@ void ChartExport::exportAxes( bool bIsChartex )
|
||||
for ( sal_Int32 nIdx = 0; nIdx < nSize; nIdx++ )
|
||||
{
|
||||
if (nSortIdx == maAxes[nIdx].nAxisType)
|
||||
exportAxis( maAxes[nIdx], bIsChartex );
|
||||
exportAxis( maAxes[nIdx], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChartExport::exportAxes_chartex()
|
||||
{
|
||||
for (const AxisIdPair& aIdPair : maAxes) {
|
||||
exportAxis(aIdPair, true);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
sal_Int32 getXAxisTypeByChartType(sal_Int32 eChartType)
|
||||
@@ -4894,6 +4904,7 @@ void ChartExport::exportAxis(const AxisIdPair& rAxisIdPair, bool bIsChartex)
|
||||
switch( rAxisIdPair.nAxisType )
|
||||
{
|
||||
case AXIS_PRIMARY_X:
|
||||
case AXIS_CATEGORY:
|
||||
{
|
||||
Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY );
|
||||
if( xAxisXSupp.is())
|
||||
@@ -4905,16 +4916,21 @@ void ChartExport::exportAxis(const AxisIdPair& rAxisIdPair, bool bIsChartex)
|
||||
if( bHasXAxisMinorGrid )
|
||||
xMinorGrid = xAxisXSupp->getXHelpGrid();
|
||||
|
||||
nAxisType = lcl_getCategoryAxisType(mxNewDiagram, 0, 0);
|
||||
if( nAxisType != -1 )
|
||||
nAxisType = getRealXAxisType(nAxisType);
|
||||
else
|
||||
nAxisType = getXAxisTypeByChartType( getChartType() );
|
||||
if (rAxisIdPair.nAxisType == AXIS_CATEGORY) {
|
||||
nAxisType = XML_catAx;
|
||||
} else {
|
||||
nAxisType = lcl_getCategoryAxisType(mxNewDiagram, 0, 0);
|
||||
if( nAxisType != -1 )
|
||||
nAxisType = getRealXAxisType(nAxisType);
|
||||
else
|
||||
nAxisType = getXAxisTypeByChartType( getChartType() );
|
||||
}
|
||||
// FIXME: axPos, need to check axis direction
|
||||
sAxPos = "b";
|
||||
break;
|
||||
}
|
||||
case AXIS_PRIMARY_Y:
|
||||
case AXIS_VALUE:
|
||||
{
|
||||
Reference< css::chart::XAxisYSupplier > xAxisYSupp( mxDiagram, uno::UNO_QUERY );
|
||||
if( xAxisYSupp.is())
|
||||
@@ -6214,7 +6230,7 @@ void ChartExport::exportDataPoints(
|
||||
}
|
||||
|
||||
// Generalized axis output
|
||||
void ChartExport::createAxes(bool bPrimaryAxes, bool bCheckCombinedAxes, bool bIsChartex)
|
||||
void ChartExport::createAxes_chart(bool bPrimaryAxes, bool bCheckCombinedAxes)
|
||||
{
|
||||
sal_Int32 nAxisIdx = -1, nAxisIdy = -1;
|
||||
bool bCreateAxes = true;
|
||||
@@ -6222,8 +6238,8 @@ void ChartExport::createAxes(bool bPrimaryAxes, bool bCheckCombinedAxes, bool bI
|
||||
// tdf#114181 keep axes of combined charts - search for existing pairs
|
||||
if (bCheckCombinedAxes)
|
||||
{
|
||||
const AxesType eWantedX = bPrimaryAxes ? AXIS_PRIMARY_X : AXIS_SECONDARY_X;
|
||||
const AxesType eWantedY = bPrimaryAxes ? AXIS_PRIMARY_Y : AXIS_SECONDARY_Y;
|
||||
const AxisType eWantedX = bPrimaryAxes ? AXIS_PRIMARY_X : AXIS_SECONDARY_X;
|
||||
const AxisType eWantedY = bPrimaryAxes ? AXIS_PRIMARY_Y : AXIS_SECONDARY_Y;
|
||||
|
||||
sal_Int32 nFoundX = -1, nFoundY = -1;
|
||||
for (const auto& rAxis : maAxes)
|
||||
@@ -6246,27 +6262,119 @@ void ChartExport::createAxes(bool bPrimaryAxes, bool bCheckCombinedAxes, bool bI
|
||||
{
|
||||
nAxisIdx = lcl_generateRandomValue();
|
||||
nAxisIdy = lcl_generateRandomValue();
|
||||
AxesType eXAxis = bPrimaryAxes ? AXIS_PRIMARY_X : AXIS_SECONDARY_X;
|
||||
AxesType eYAxis = bPrimaryAxes ? AXIS_PRIMARY_Y : AXIS_SECONDARY_Y;
|
||||
AxisType eXAxis = bPrimaryAxes ? AXIS_PRIMARY_X : AXIS_SECONDARY_X;
|
||||
AxisType eYAxis = bPrimaryAxes ? AXIS_PRIMARY_Y : AXIS_SECONDARY_Y;
|
||||
maAxes.emplace_back( eXAxis, nAxisIdx, nAxisIdy );
|
||||
maAxes.emplace_back( eYAxis, nAxisIdy, nAxisIdx );
|
||||
}
|
||||
|
||||
if (!bIsChartex) {
|
||||
// Export IDs
|
||||
FSHelperPtr pFS = GetFS();
|
||||
pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdx));
|
||||
pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdy));
|
||||
if (mbHasZAxis)
|
||||
// Export IDs
|
||||
FSHelperPtr pFS = GetFS();
|
||||
|
||||
pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdx));
|
||||
pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdy));
|
||||
if (mbHasZAxis)
|
||||
{
|
||||
sal_Int32 nAxisIdz = 0;
|
||||
if( isDeep3dChart() )
|
||||
{
|
||||
sal_Int32 nAxisIdz = 0;
|
||||
if( isDeep3dChart() )
|
||||
{
|
||||
nAxisIdz = lcl_generateRandomValue();
|
||||
maAxes.emplace_back( AXIS_PRIMARY_Z, nAxisIdz, nAxisIdy );
|
||||
}
|
||||
pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdz));
|
||||
nAxisIdz = lcl_generateRandomValue();
|
||||
maAxes.emplace_back( AXIS_PRIMARY_Z, nAxisIdz, nAxisIdy );
|
||||
}
|
||||
pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdz));
|
||||
}
|
||||
}
|
||||
|
||||
void ChartExport::createAxes_chartex(
|
||||
const Reference<chart2::XDataSeries>& xSeries)
|
||||
{
|
||||
FSHelperPtr pFS = GetFS();
|
||||
|
||||
// Use the cx:axisId list preserved on the data series at import.
|
||||
// For axis-less chartex types (regionMap, sunburst, treemap) the
|
||||
// sequence is empty, so no <cx:axisId> and no <cx:axis> are emitted.
|
||||
uno::Sequence<sal_Int32> aAxisIds;
|
||||
Reference<beans::XPropertySet> xSeriesProp(xSeries, uno::UNO_QUERY);
|
||||
if (xSeriesProp.is())
|
||||
{
|
||||
try
|
||||
{
|
||||
xSeriesProp->getPropertyValue(u"ChartexAxisIds"_ustr) >>= aAxisIds;
|
||||
}
|
||||
catch (const uno::Exception&)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (!aAxisIds.hasElements())
|
||||
return;
|
||||
|
||||
for (sal_Int32 nAxisId : aAxisIds)
|
||||
{
|
||||
pFS->singleElement(FSNS(XML_cx, XML_axisId), XML_val,
|
||||
OString::number(nAxisId));
|
||||
|
||||
// Each axis appears in maAxes (and therefore in <cx:axis> output)
|
||||
// exactly once across all series.
|
||||
bool bKnown = std::any_of(maAxes.begin(), maAxes.end(),
|
||||
[nAxisId](const AxisIdPair& rPair)
|
||||
{ return rPair.nAxisId == nAxisId; });
|
||||
if (bKnown)
|
||||
continue;
|
||||
|
||||
// Find the axis type by walking through and
|
||||
// matching the AxisId property we stored at import.
|
||||
AxisType eAxisType = AXIS_CATEGORY;
|
||||
try
|
||||
{
|
||||
Reference<chart2::XCoordinateSystemContainer> xCooSysCnt(
|
||||
mxNewDiagram, uno::UNO_QUERY);
|
||||
if (xCooSysCnt.is())
|
||||
{
|
||||
bool bFound = false;
|
||||
const auto aCooSysSeq = xCooSysCnt->getCoordinateSystems();
|
||||
for (const auto& xCooSys : aCooSysSeq)
|
||||
{
|
||||
if (bFound)
|
||||
break;
|
||||
const sal_Int32 nDim = xCooSys->getDimension();
|
||||
for (sal_Int32 i = 0; i < nDim && !bFound; ++i)
|
||||
{
|
||||
const sal_Int32 nMaxAxIdx = xCooSys->getMaximumAxisIndexByDimension(i);
|
||||
for (sal_Int32 j = 0; j <= nMaxAxIdx && !bFound; ++j)
|
||||
{
|
||||
Reference<chart2::XAxis> xAxis = xCooSys->getAxisByDimension(i, j);
|
||||
Reference<beans::XPropertySet> xAxisProp( xAxis, uno::UNO_QUERY);
|
||||
if (!xAxisProp.is())
|
||||
continue;
|
||||
sal_Int32 nStoredId = -1;
|
||||
try
|
||||
{
|
||||
xAxisProp->getPropertyValue(u"AxisId"_ustr) >>= nStoredId;
|
||||
}
|
||||
catch (const uno::Exception&)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (nStoredId == nAxisId)
|
||||
{
|
||||
bool bCatNotVal = false;
|
||||
xAxisProp->getPropertyValue(u"CatNotVal"_ustr) >>= bCatNotVal;
|
||||
eAxisType = bCatNotVal ? AXIS_CATEGORY : AXIS_VALUE;
|
||||
bFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const uno::Exception&)
|
||||
{
|
||||
}
|
||||
|
||||
// nCrossAx is unused for chartex (<cx:axis> has no crossAx field),
|
||||
// so the value passed for the third parameter is irrelevant
|
||||
maAxes.emplace_back(eAxisType, nAxisId, -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ AttachedAxisIndex
|
||||
AutoFilter
|
||||
AutoShowInfo
|
||||
Autocomplete
|
||||
AxisId
|
||||
BackColor
|
||||
BackColorTransparency
|
||||
BackGraphicLocation
|
||||
@@ -48,6 +49,7 @@ BulletFontName
|
||||
BulletRelSize
|
||||
CLSID
|
||||
CalcAsShown
|
||||
CatNotVal
|
||||
CellBackColor
|
||||
CellProtection
|
||||
CellStyle
|
||||
@@ -57,6 +59,7 @@ Change
|
||||
ChartExDimType
|
||||
ChartExFormula
|
||||
ChartExNFormula
|
||||
ChartexAxisIds
|
||||
CharBackColor
|
||||
CharBackgroundComplexColor
|
||||
CharCaseMap
|
||||
|
||||
Reference in New Issue
Block a user