oox: support histogram OOXML import and export
Preserve histogram charts when importng and exporting XLSX file. Import histogram charts from XLSX, keep their source data and settings, and export them back that keeps them working as histogram charts. Change-Id: I9c3e39fd3b429b9f61c249dd952bf70d9113ed7f (cherry picked from commit 330fa62f1ef7b7c848c2da98d18a8bb453e82201) Signed-off-by: Devansh Varshney <varshney.devansh614@gmail.com> Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/1882 Tested-by: Jenkins CPCI <releng@collaboraoffice.com> Reviewed-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com> (cherry picked from commit 2ed0256704eb3ca34ff0a217b330849a3f7952da) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/203895 Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Tested-by: Jenkins
This commit is contained in:
committed by
Xisco Fauli
parent
0a99dee0ba
commit
5e836dfec5
@@ -1022,6 +1022,64 @@ CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramODSRoundtrip)
|
||||
CPPUNIT_ASSERT_EQUAL(3.5, fBinWidth);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramXLSXRoundtrip)
|
||||
{
|
||||
// See tdf#163727
|
||||
// OOXML chartex represents histograms as <cx:series layoutId="clusteredColumn">
|
||||
// with a <cx:layoutPr><cx:binning/></cx:layoutPr> child.
|
||||
// On import that should be detected as a HistogramChartType and on export the binning
|
||||
// markup should stay preserved.
|
||||
|
||||
loadFromFile(u"xlsx/SimpleHistogram.xlsx");
|
||||
|
||||
// Round trip 1: chart type identity + binning element preserved.
|
||||
saveAndReload(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",
|
||||
"layoutId", u"clusteredColumn");
|
||||
assertXPath(pXmlDoc,
|
||||
"/cx:chartSpace/cx:chart/cx:plotArea/cx:plotAreaRegion/cx:series/cx:layoutPr/"
|
||||
"cx:binning",
|
||||
"intervalClosed", u"r");
|
||||
|
||||
uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
|
||||
CPPUNIT_ASSERT(xChartDoc.is());
|
||||
|
||||
Reference<chart2::XChartType> xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
|
||||
CPPUNIT_ASSERT(xChartType.is());
|
||||
CPPUNIT_ASSERT_EQUAL(u"com.sun.star.chart2.HistogramChartType"_ustr,
|
||||
xChartType->getChartType());
|
||||
|
||||
// Round trip 2: non-default binning parameters survive save + reload.
|
||||
Reference<beans::XPropertySet> xProperties(xChartType, uno::UNO_QUERY_THROW);
|
||||
xProperties->setPropertyValue(u"FrequencyType"_ustr, uno::Any(sal_Int32(1)));
|
||||
xProperties->setPropertyValue(u"BinWidth"_ustr, uno::Any(2.5));
|
||||
|
||||
saveAndReload(TestFilter::XLSX);
|
||||
|
||||
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/cx:binSize",
|
||||
"val", u"2.5");
|
||||
|
||||
xChartDoc = getChartDocFromSheet(0);
|
||||
xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
|
||||
Reference<beans::XPropertySet> xReloadedProperties(xChartType, uno::UNO_QUERY_THROW);
|
||||
|
||||
sal_Int32 nFrequencyType = -1;
|
||||
CPPUNIT_ASSERT(xReloadedProperties->getPropertyValue(u"FrequencyType"_ustr) >>= nFrequencyType);
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), nFrequencyType);
|
||||
|
||||
double fBinWidth = 0.0;
|
||||
CPPUNIT_ASSERT(xReloadedProperties->getPropertyValue(u"BinWidth"_ustr) >>= fBinWidth);
|
||||
CPPUNIT_ASSERT_EQUAL(2.5, fBinWidth);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf60316)
|
||||
{
|
||||
loadFromFile(u"pptx/tdf60316.pptx");
|
||||
|
||||
Binary file not shown.
@@ -74,6 +74,8 @@ struct TypeGroupModel
|
||||
bool mbSmooth; /// True = smooth lines in line charts.
|
||||
bool mbVaryColors; /// True = different automatic colors for each point.
|
||||
bool mbWireframe; /// True = wireframe surface chart, false = filled surface chart.
|
||||
/// True = this type group should be imported as histogram type - TYPEID_HISTO
|
||||
bool mbForceHistogram = false;
|
||||
|
||||
explicit TypeGroupModel( sal_Int32 nTypeId, bool bMSO2007Doc );
|
||||
~TypeGroupModel();
|
||||
|
||||
@@ -112,11 +112,45 @@ ModelRef< AxisModel > lclGetOrCreateAxis( const AxesSetModel::AxisMap& rFromAxes
|
||||
return xAxis;
|
||||
}
|
||||
|
||||
bool lclHasTypeGroupWithLayout(const AxesSetModel::TypeGroupVector& rTypeGroups, sal_Int32 nToken)
|
||||
{
|
||||
for (auto const& typeGroup : rTypeGroups)
|
||||
if (typeGroup && typeGroup->mnTypeId == nToken)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void AxesSetConverter::convertFromModel( const Reference< XDiagram >& rxDiagram,
|
||||
View3DModel& rView3DModel, sal_Int32 nAxesSetIdx,
|
||||
DataSourceCxModel::DataMap& raSourceMap,
|
||||
bool bSupportsVaryColorsByPoint, bool bUseFixedInnerSize)
|
||||
{
|
||||
// chartex clusteredColumn + <cx:binning> is how a histogram is stored in OOXML.
|
||||
// But the same markup also appears on the clusteredColumn sub-chart of a
|
||||
// paretoLine, which must not be reclassified. The difference is visible only at
|
||||
// this level, where we see all sibling type groups.
|
||||
{
|
||||
const bool bHasParetoSibling
|
||||
= lclHasTypeGroupWithLayout(mrModel.maTypeGroups, CX_TOKEN(paretoLine));
|
||||
if (!bHasParetoSibling)
|
||||
{
|
||||
for (auto const& typeGroup : mrModel.maTypeGroups)
|
||||
{
|
||||
if (!typeGroup || typeGroup->mnTypeId != CX_TOKEN(clusteredColumn))
|
||||
continue;
|
||||
for (auto const& rxSeries : typeGroup->maSeries)
|
||||
{
|
||||
if (rxSeries && rxSeries->mxLayoutPr.is()
|
||||
&& rxSeries->mxLayoutPr->mxBinning.is())
|
||||
{
|
||||
typeGroup->mbForceHistogram = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create type group converter objects for all type groups
|
||||
typedef RefVector< TypeGroupConverter > TypeGroupConvVector;
|
||||
TypeGroupConvVector aTypeGroups;
|
||||
@@ -172,18 +206,17 @@ void AxesSetConverter::convertFromModel( const Reference< XDiagram >& rxDiagram,
|
||||
to the data provider attached to the chart document. */
|
||||
if( xCoordSystem.is() )
|
||||
{
|
||||
// Transfer any (chartex) data, specified at the chartSpace level,
|
||||
// into the appropriate series. Must happen before convertFromModel
|
||||
// so the series data is available when data series are created.
|
||||
for (auto const& typeGroup : aTypeGroups)
|
||||
typeGroup->moveDataToSeries(raSourceMap);
|
||||
|
||||
// convert all chart type groups, this converts all series data and formatting
|
||||
for (auto const& typeGroup : aTypeGroups)
|
||||
typeGroup->convertFromModel( rxDiagram, xCoordSystem,
|
||||
nAxesSetIdx,bSupportsVaryColorsByPoint );
|
||||
|
||||
// Transfer any (chartex) data, specified at the chartSpace level,
|
||||
// into the appropriate series. This needs to happen before the
|
||||
// calls to AxisConverter::convertFromModel() below.
|
||||
for (auto const& typeGroup : aTypeGroups) {
|
||||
typeGroup->moveDataToSeries(raSourceMap);
|
||||
}
|
||||
|
||||
bool bMSO2007Doc = getFilter().isMSO2007Document();
|
||||
// convert all axes (create missing axis models)
|
||||
ModelRef< AxisModel > xXAxis = lclGetOrCreateAxis( mrModel.maAxes, API_X_AXIS, rFirstTypeGroup.getTypeInfo().mbCategoryAxis ? C_TOKEN( catAx ) : C_TOKEN( valAx ), bMSO2007Doc );
|
||||
|
||||
@@ -794,7 +794,7 @@ ContextHandlerRef BinningContext::onCreateContext( sal_Int32 nElement, const Att
|
||||
{
|
||||
switch( getCurrentElement() )
|
||||
{
|
||||
case CX_TOKEN( layoutPr ):
|
||||
case CX_TOKEN(binning):
|
||||
switch( nElement )
|
||||
{
|
||||
case CX_TOKEN( binSize ):
|
||||
@@ -807,6 +807,7 @@ ContextHandlerRef BinningContext::onCreateContext( sal_Int32 nElement, const Att
|
||||
// TODO
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ContextBase::onCreateContext( nElement, rAttribs );
|
||||
}
|
||||
@@ -854,9 +855,8 @@ ContextHandlerRef LayoutPropsContext::onCreateContext( sal_Int32 nElement, const
|
||||
rB.meIntervalClosed = BinningModel::ClosedSide::L;
|
||||
} else if (rAttribs.getString( XML_intervalClosed ) == "r") {
|
||||
rB.meIntervalClosed = BinningModel::ClosedSide::R;
|
||||
} else {
|
||||
assert(false); // no other options
|
||||
}
|
||||
// intervalClosed is optional in OOXML chartex
|
||||
// TODO: Also handle attributes underflow, overflow
|
||||
return new BinningContext( *this, rB );
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
|
||||
#include <drawingml/chart/typegroupconverter.hxx>
|
||||
|
||||
#include <com/sun/star/chart2/XChartDocument.hpp>
|
||||
#include <com/sun/star/chart2/XChartTypeManager.hpp>
|
||||
#include <com/sun/star/chart2/XChartTypeTemplate.hpp>
|
||||
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
|
||||
#include <com/sun/star/chart/DataLabelPlacement.hpp>
|
||||
#include <com/sun/star/chart2/CartesianCoordinateSystem2d.hpp>
|
||||
#include <com/sun/star/chart2/CartesianCoordinateSystem3d.hpp>
|
||||
@@ -90,7 +94,7 @@ const TypeGroupInfo spTypeInfos[] =
|
||||
// type-id type-category service varied-point-color default label pos polar area2d 1stvis xcateg swap stack picopt
|
||||
{ TYPEID_BAR, TYPECATEGORY_BAR, SERVICE_CHART2_COLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, true, true },
|
||||
{ TYPEID_HORBAR, TYPECATEGORY_BAR, SERVICE_CHART2_COLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, true, true, true },
|
||||
{ TYPEID_HISTO, TYPECATEGORY_HISTO, SERVICE_CHART2_HISTO, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, true, true, true },
|
||||
{ TYPEID_HISTO, TYPECATEGORY_HISTO, SERVICE_CHART2_HISTO, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, true, true },
|
||||
{ TYPEID_LINE, TYPECATEGORY_LINE, SERVICE_CHART2_LINE, VARPOINTMODE_SINGLE, csscd::RIGHT, false, false, false, true, false, true, false },
|
||||
{ TYPEID_AREA, TYPECATEGORY_LINE, SERVICE_CHART2_AREA, VARPOINTMODE_NONE, csscd::CENTER, false, true, false, true, false, true, false },
|
||||
{ TYPEID_STOCK, TYPECATEGORY_LINE, SERVICE_CHART2_CANDLE, VARPOINTMODE_NONE, csscd::RIGHT, false, false, false, true, false, true, false },
|
||||
@@ -217,6 +221,15 @@ TypeGroupConverter::TypeGroupConverter( const ConverterRoot& rParent, TypeGroupM
|
||||
default:;
|
||||
}
|
||||
|
||||
// In OOXML chartex, histogram is represented as clusteredColumn with
|
||||
// <cx:layoutPr><cx:binning .../></cx:layoutPr>. The decision whether this
|
||||
// is a histogram or the clusteredColumn half of a paretoLine compound chart
|
||||
// is made by the plot-area converter, which has sibling type-group visibility.
|
||||
if (eTypeId == TYPEID_CLUSTEREDCOLUMN && mrModel.mbForceHistogram)
|
||||
{
|
||||
eTypeId = TYPEID_HISTO;
|
||||
}
|
||||
|
||||
// set the chart type info struct for the current chart type
|
||||
maTypeInfo = lclGetTypeInfoFromTypeId( eTypeId );
|
||||
}
|
||||
@@ -367,6 +380,50 @@ void TypeGroupConverter::convertFromModel( const Reference< XDiagram >& rxDiagra
|
||||
// additional properties
|
||||
PropertySet aDiaProp( rxDiagram );
|
||||
PropertySet aTypeProp( xChartType );
|
||||
|
||||
if (maTypeInfo.meTypeId == TYPEID_HISTO)
|
||||
{
|
||||
Reference<css::beans::XPropertySet> xTypePropSet(xChartType, UNO_QUERY);
|
||||
if (xTypePropSet.is())
|
||||
{
|
||||
for (const auto& rxSeriesModel : mrModel.maSeries)
|
||||
{
|
||||
if (!rxSeriesModel || !rxSeriesModel->mxLayoutPr.is()
|
||||
|| !rxSeriesModel->mxLayoutPr->mxBinning.is())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const BinningModel& rBinning = *rxSeriesModel->mxLayoutPr->mxBinning;
|
||||
|
||||
if (rBinning.maBinSizeOrCount)
|
||||
{
|
||||
if (std::holds_alternative<double>(*rBinning.maBinSizeOrCount))
|
||||
{
|
||||
xTypePropSet->setPropertyValue(
|
||||
u"BinWidth"_ustr,
|
||||
uno::Any(std::get<double>(*rBinning.maBinSizeOrCount)));
|
||||
xTypePropSet->setPropertyValue(
|
||||
u"FrequencyType"_ustr,
|
||||
uno::Any(sal_Int32(1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
xTypePropSet->setPropertyValue(
|
||||
u"BinCount"_ustr,
|
||||
uno::Any(static_cast<sal_Int32>(
|
||||
std::get<sal_uInt32>(*rBinning.maBinSizeOrCount))));
|
||||
xTypePropSet->setPropertyValue(
|
||||
u"FrequencyType"_ustr,
|
||||
uno::Any(sal_Int32(2)));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch( maTypeInfo.meTypeCategory )
|
||||
{
|
||||
case TYPECATEGORY_BAR:
|
||||
@@ -511,6 +568,23 @@ void TypeGroupConverter::convertFromModel( const Reference< XDiagram >& rxDiagra
|
||||
xChartTypeCont->addChartType(xChartType);
|
||||
}
|
||||
|
||||
if (maTypeInfo.meTypeId == TYPEID_HISTO)
|
||||
{
|
||||
Reference<chart2::XChartDocument> const& xChartDoc = getChartDocument();
|
||||
Reference<chart2::XChartTypeManager> xChartTypeManager(
|
||||
xChartDoc.is() ? xChartDoc->getChartTypeManager() : nullptr);
|
||||
Reference<lang::XMultiServiceFactory> xTemplateFactory(
|
||||
xChartTypeManager, UNO_QUERY);
|
||||
if (xTemplateFactory.is())
|
||||
{
|
||||
Reference<chart2::XChartTypeTemplate> xTemplate(
|
||||
xTemplateFactory->createInstance(u"com.sun.star.chart2.template.Histogram"_ustr),
|
||||
UNO_QUERY);
|
||||
if (xTemplate.is())
|
||||
xTemplate->changeDiagram(rxDiagram);
|
||||
}
|
||||
}
|
||||
|
||||
// set existence of bar connector lines at diagram (only in stacked 2D bar charts)
|
||||
if( mrModel.mxSerLines.is() && !mb3dChart && (maTypeInfo.meTypeCategory == TYPECATEGORY_BAR) && (isStacked() || isPercent()) )
|
||||
aDiaProp.setProperty( PROP_ConnectBars, true );
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include <docmodel/uno/UnoGradientTools.hxx>
|
||||
#include <docmodel/uno/UnoComplexColor.hxx>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
|
||||
@@ -230,6 +232,133 @@ std::vector<Sequence<Reference<chart2::XDataSeries> > > splitDataSeriesByAxis(co
|
||||
return aSplitSeries;
|
||||
}
|
||||
|
||||
bool lclGetAutoHistogramBinning(const css::uno::Reference<css::chart2::XDataSeries>& xSeries,
|
||||
double& rfBinWidth, sal_Int32& rnBinCount)
|
||||
{
|
||||
rfBinWidth = 0.0;
|
||||
rnBinCount = 0;
|
||||
|
||||
css::uno::Reference<css::chart2::data::XDataSource> xSource(xSeries, css::uno::UNO_QUERY);
|
||||
if (!xSource.is())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
css::uno::Sequence<css::uno::Reference<css::chart2::data::XLabeledDataSequence>> aSeqs
|
||||
= xSource->getDataSequences();
|
||||
|
||||
css::uno::Reference<css::chart2::data::XDataSequence> xValuesY;
|
||||
for (const auto& xLabeledSeq : aSeqs)
|
||||
{
|
||||
if (!xLabeledSeq.is())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
css::uno::Reference<css::chart2::data::XDataSequence> xValues = xLabeledSeq->getValues();
|
||||
if (!xValues.is())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
css::uno::Reference<css::beans::XPropertySet> xSeqProp(xValues, css::uno::UNO_QUERY);
|
||||
if (!xSeqProp.is())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
OUString aRole;
|
||||
xSeqProp->getPropertyValue(u"Role"_ustr) >>= aRole;
|
||||
if (aRole == "values-y")
|
||||
{
|
||||
xValuesY = xValues;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!xValuesY.is())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<double> aRawData;
|
||||
css::uno::Sequence<css::uno::Any> aValues = xValuesY->getData();
|
||||
aRawData.reserve(aValues.getLength());
|
||||
|
||||
for (const auto& rAny : aValues)
|
||||
{
|
||||
double fValue = 0.0;
|
||||
if (rAny >>= fValue)
|
||||
aRawData.push_back(fValue);
|
||||
}
|
||||
|
||||
if (aRawData.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
double fSum = 0.0;
|
||||
double fSquareSum = 0.0;
|
||||
double fMinValue = aRawData[0];
|
||||
double fMaxValue = aRawData[0];
|
||||
sal_Int32 nValidCount = 0;
|
||||
|
||||
for (double fValue : aRawData)
|
||||
{
|
||||
if (std::isfinite(fValue))
|
||||
{
|
||||
fSum += fValue;
|
||||
fSquareSum += fValue * fValue;
|
||||
fMinValue = std::min(fMinValue, fValue);
|
||||
fMaxValue = std::max(fMaxValue, fValue);
|
||||
++nValidCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (nValidCount == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nValidCount < 2 || fMinValue == fMaxValue)
|
||||
{
|
||||
rfBinWidth = 1.0;
|
||||
rnBinCount = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
const double fMean = fSum / nValidCount;
|
||||
const double fVariance = (fSquareSum - fSum * fMean) / (nValidCount - 1);
|
||||
const double fStdDev = std::sqrt(fVariance);
|
||||
|
||||
if (!std::isfinite(fStdDev) || fStdDev <= 0.0)
|
||||
{
|
||||
rfBinWidth = 1.0;
|
||||
rnBinCount = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
rfBinWidth = (3.5 * fStdDev) / std::cbrt(static_cast<double>(nValidCount));
|
||||
if (!std::isfinite(rfBinWidth) || rfBinWidth <= 0.0)
|
||||
{
|
||||
rfBinWidth = 1.0;
|
||||
rnBinCount = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
rfBinWidth = std::round(rfBinWidth * 100.0) / 100.0;
|
||||
if (rfBinWidth <= 0.0)
|
||||
{
|
||||
rfBinWidth = 1.0;
|
||||
rnBinCount = 1;
|
||||
return true;
|
||||
}
|
||||
rnBinCount = static_cast<sal_Int32>(std::ceil((fMaxValue - fMinValue) / rfBinWidth));
|
||||
rnBinCount = std::max<sal_Int32>(rnBinCount, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
static Reference< chart2::data::XLabeledDataSequence > lcl_getCategories(
|
||||
@@ -552,6 +681,8 @@ constexpr auto constChartTypeMap = frozen::make_unordered_map<std::u16string_vie
|
||||
{ u"com.sun.star.chart2.BoxWhiskerChartType", chart::TYPEID_BOXWHISKER },
|
||||
{ u"com.sun.star.chart2.ClusteredColumnDiagram", chart::TYPEID_CLUSTEREDCOLUMN },
|
||||
{ u"com.sun.star.chart2.ClusteredColumnChartType", chart::TYPEID_CLUSTEREDCOLUMN },
|
||||
{ u"com.sun.star.chart.HistogramDiagram", chart::TYPEID_HISTO },
|
||||
{ u"com.sun.star.chart2.HistogramChartType", chart::TYPEID_HISTO },
|
||||
{ u"com.sun.star.chart.FunnelDiagram", chart::TYPEID_FUNNEL },
|
||||
{ u"com.sun.star.chart2.FunnelChartType", chart::TYPEID_FUNNEL },
|
||||
{ u"com.sun.star.chart2.ParetoLineDiagram", chart::TYPEID_PARETOLINE },
|
||||
@@ -1658,7 +1789,8 @@ void ChartExport::exportData_chartex( [[maybe_unused]] const Reference< css::cha
|
||||
// as well as a ../embeddings file (which LO doesn't seem to produce).
|
||||
// But there's probably a smarter way to determine which pathway to take
|
||||
// than based on document type.
|
||||
if (GetDocumentType() == DOCUMENT_XLSX) {
|
||||
if (GetDocumentType() == DOCUMENT_XLSX && eChartType != chart::TYPEID_HISTO)
|
||||
{
|
||||
// Just hard-coding this for now
|
||||
|
||||
sal_Int32 nSuffixVal = nSeriesIndex;
|
||||
@@ -1707,6 +1839,7 @@ void ChartExport::exportData_chartex( [[maybe_unused]] const Reference< css::cha
|
||||
{
|
||||
case chart::TYPEID_BOXWHISKER:
|
||||
case chart::TYPEID_CLUSTEREDCOLUMN:
|
||||
case chart::TYPEID_HISTO:
|
||||
case chart::TYPEID_PARETOLINE:
|
||||
case chart::TYPEID_SUNBURST:
|
||||
case chart::TYPEID_TREEMAP:
|
||||
@@ -2692,6 +2825,7 @@ void ChartExport::exportPlotArea(const Reference< css::chart::XChartDocument >&
|
||||
break;
|
||||
}
|
||||
case chart::TYPEID_CLUSTEREDCOLUMN:
|
||||
case chart::TYPEID_HISTO:
|
||||
{
|
||||
exportChartex( xChartType, "clusteredColumn" );
|
||||
break;
|
||||
@@ -3996,9 +4130,12 @@ void ChartExport::exportSeries_chartex( const Reference<chart2::XChartType>& xCh
|
||||
exportShapeProps( xOldPropSet, XML_cx );
|
||||
}
|
||||
|
||||
DataLabelsRange aDLblsRange;
|
||||
// export data labels
|
||||
exportDataLabels(rSeries, nSeriesLength, eChartType, aDLblsRange, true);
|
||||
if (aChartType != "com.sun.star.chart2.HistogramChartType")
|
||||
{
|
||||
DataLabelsRange aDLblsRange;
|
||||
// export data labels
|
||||
exportDataLabels(rSeries, nSeriesLength, eChartType, aDLblsRange, true);
|
||||
}
|
||||
|
||||
// dataId links to the correct data set in the <cx:chartData>. See
|
||||
// DATA_ID_COMMENT
|
||||
@@ -4041,8 +4178,27 @@ void ChartExport::exportSeries_chartex( const Reference<chart2::XChartType>& xCh
|
||||
bool bHasGeography = false;
|
||||
xSeriesProp->getPropertyValue(u"HasGeography"_ustr) >>= bHasGeography;
|
||||
|
||||
sal_Int32 nFrequencyType = 0;
|
||||
double fBinWidth = 0.0;
|
||||
sal_Int32 nBinCount = 0;
|
||||
bool bHasBinWidth = false;
|
||||
bool bHasBinCount = false;
|
||||
if (aChartType == "com.sun.star.chart2.HistogramChartType")
|
||||
{
|
||||
Reference<beans::XPropertySet> xChartTypePropSet(xChartType, uno::UNO_QUERY);
|
||||
if (xChartTypePropSet.is())
|
||||
{
|
||||
xChartTypePropSet->getPropertyValue(u"FrequencyType"_ustr) >>= nFrequencyType;
|
||||
bHasBinWidth =
|
||||
(xChartTypePropSet->getPropertyValue(u"BinWidth"_ustr) >>= fBinWidth);
|
||||
bHasBinCount =
|
||||
(xChartTypePropSet->getPropertyValue(u"BinCount"_ustr) >>= nBinCount);
|
||||
}
|
||||
}
|
||||
|
||||
bool bHasAny = bHasParentLL || bHasRegionLL || bHasVisibility
|
||||
|| bHasQM || bHasSubtotals || bHasIC || bHasGeography;
|
||||
|| bHasQM || bHasSubtotals || bHasIC || bHasGeography
|
||||
|| aChartType == "com.sun.star.chart2.HistogramChartType";
|
||||
if (bHasAny)
|
||||
{
|
||||
pFS->startElement(FSNS(XML_cx, XML_layoutPr));
|
||||
@@ -4078,8 +4234,54 @@ void ChartExport::exportSeries_chartex( const Reference<chart2::XChartType>& xCh
|
||||
if (bHasGeography)
|
||||
oox::drawingml::chart::exportGeography(xSeriesProp, pFS);
|
||||
|
||||
if (bHasIC)
|
||||
if (aChartType == "com.sun.star.chart2.HistogramChartType")
|
||||
{
|
||||
// histogram <cx:binning> wraps bin-size/bin-count children
|
||||
const char* pIntervalClosed = nullptr;
|
||||
if (bHasIC)
|
||||
{
|
||||
sal_uInt32 nIntervalClosedChar = '\0';
|
||||
aIntervalClosed >>= nIntervalClosedChar;
|
||||
if (nIntervalClosedChar == 'l')
|
||||
pIntervalClosed = "l";
|
||||
else if (nIntervalClosedChar == 'r')
|
||||
pIntervalClosed = "r";
|
||||
}
|
||||
|
||||
if (pIntervalClosed)
|
||||
pFS->startElement(FSNS(XML_cx, XML_binning),
|
||||
XML_intervalClosed, pIntervalClosed);
|
||||
else
|
||||
pFS->startElement(FSNS(XML_cx, XML_binning));
|
||||
|
||||
if (nFrequencyType == 1 && bHasBinWidth)
|
||||
{
|
||||
pFS->singleElement(FSNS(XML_cx, XML_binSize), XML_val,
|
||||
OString::number(fBinWidth));
|
||||
}
|
||||
else if (nFrequencyType == 2 && bHasBinCount)
|
||||
{
|
||||
pFS->singleElement(FSNS(XML_cx, XML_binCount), XML_val,
|
||||
OString::number(nBinCount));
|
||||
}
|
||||
else if (nFrequencyType == 0)
|
||||
{
|
||||
double fAutoBinWidth = 0.0;
|
||||
sal_Int32 nAutoBinCount = 0;
|
||||
|
||||
if (lclGetAutoHistogramBinning(rSeries, fAutoBinWidth, nAutoBinCount)
|
||||
&& fAutoBinWidth > 0.0)
|
||||
{
|
||||
pFS->singleElement(FSNS(XML_cx, XML_binSize), XML_val,
|
||||
OString::number(fAutoBinWidth));
|
||||
}
|
||||
}
|
||||
|
||||
pFS->endElement(FSNS(XML_cx, XML_binning));
|
||||
}
|
||||
else if (bHasIC)
|
||||
{
|
||||
// non-histogram (BoxWhisker): self-closing <cx:binning intervalClosed="…"/>
|
||||
sal_uInt32 nIntervalClosedChar = '\0';
|
||||
aIntervalClosed >>= nIntervalClosedChar;
|
||||
|
||||
@@ -6448,6 +6650,7 @@ bool ChartExport::isChartexNotChartNS(NamespaceAbbrev *peNS) const
|
||||
break;
|
||||
case chart::TYPEID_BOXWHISKER:
|
||||
case chart::TYPEID_CLUSTEREDCOLUMN:
|
||||
case chart::TYPEID_HISTO:
|
||||
case chart::TYPEID_PARETOLINE:
|
||||
case chart::TYPEID_SUNBURST:
|
||||
case chart::TYPEID_TREEMAP:
|
||||
|
||||
Reference in New Issue
Block a user