diff options
Diffstat (limited to 'oox/source/drawingml/chart/chartspaceconverter.cxx')
-rw-r--r-- | oox/source/drawingml/chart/chartspaceconverter.cxx | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/oox/source/drawingml/chart/chartspaceconverter.cxx b/oox/source/drawingml/chart/chartspaceconverter.cxx new file mode 100644 index 000000000..774d784a6 --- /dev/null +++ b/oox/source/drawingml/chart/chartspaceconverter.cxx @@ -0,0 +1,308 @@ +/* -*- 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 <drawingml/chart/chartspaceconverter.hxx> + +#include <com/sun/star/chart/MissingValueTreatment.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/XChartType.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/chart2/XDataSeriesContainer.hpp> +#include <com/sun/star/chart2/XTitled.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <oox/core/xmlfilterbase.hxx> +#include <oox/drawingml/chart/chartconverter.hxx> +#include <oox/token/properties.hxx> +#include <oox/token/tokens.hxx> +#include <drawingml/chart/chartdrawingfragment.hxx> +#include <drawingml/chart/chartspacemodel.hxx> +#include <drawingml/chart/plotareaconverter.hxx> +#include <drawingml/chart/titleconverter.hxx> +#include <ooxresid.hxx> +#include <strings.hrc> + +using namespace ::com::sun::star; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::Any; +using ::com::sun::star::drawing::XDrawPageSupplier; +using ::com::sun::star::drawing::XShapes; +using ::com::sun::star::chart2::XDiagram; +using ::com::sun::star::chart2::XTitled; + +namespace oox::drawingml::chart { + +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::chart2; +using namespace ::com::sun::star::chart2::data; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; + +ChartSpaceConverter::ChartSpaceConverter( const ConverterRoot& rParent, ChartSpaceModel& rModel ) : + ConverterBase< ChartSpaceModel >( rParent, rModel ) +{ +} + +ChartSpaceConverter::~ChartSpaceConverter() +{ +} + +// Formulas with no numeric values and strings are zeroes in OOXML line charts also in the mode DispBlanksAs=gap, +// unlike in OpenDocument LEAVE_GAP mode. As a workaround, we will use the OpenDocument mode USE_ZERO, if the OOXML +// line chart has got formulas with no numeric values and strings, but it doesn't have empty cells, showing the +// same chart as in MSO. (Empty cells need gaps, so in that case, we cannot use this workaround). +static bool lcl_useWorkaroundForNoGapInOOXML( Reference< chart2::XChartDocument > const & xChartDoc) +{ + Reference <chart2::XDiagram > xDiagram = xChartDoc->getFirstDiagram(); + if ( !xDiagram.is() ) + return false; + + Reference< chart2::XCoordinateSystemContainer > xCooSysContainer( xDiagram, UNO_QUERY_THROW ); + + Sequence< Reference< chart2::XCoordinateSystem > > xCooSysSequence( xCooSysContainer->getCoordinateSystems()); + if ( !xCooSysSequence.hasElements() ) + return false; + + Reference< chart2::XChartTypeContainer > xChartTypeContainer( xCooSysSequence[0], UNO_QUERY_THROW ); + + Sequence< Reference< chart2::XChartType > > xChartTypeSequence( xChartTypeContainer->getChartTypes() ); + if ( !xChartTypeSequence.hasElements() ) + return false; + + const Reference<chart2::XChartType>& xCT = xChartTypeSequence[0]; + + if ( xCT->getChartType() != "com.sun.star.chart2.LineChartType" ) + return false; + + Reference<chart2::XDataSeriesContainer> xDSCont(xCT, uno::UNO_QUERY); + + if (!xDSCont.is()) + return false; + + const Sequence<uno::Reference<chart2::XDataSeries> > aDataSeriesSeq = xDSCont->getDataSeries(); + + bool bHasNoGapBlankValue = false; + bool bHasEmptyCell = false; + + for (const auto& rDataSeries : aDataSeriesSeq) + { + uno::Reference<chart2::data::XDataSource> xDSrc(rDataSeries, uno::UNO_QUERY); + if (!xDSrc.is()) + return false; + + const uno::Sequence<Reference<chart2::data::XLabeledDataSequence> > aDataSeqs = xDSrc->getDataSequences(); + for (const auto& rDataSeq : aDataSeqs) + { + Reference<chart2::data::XDataSequence> xValues = rDataSeq->getValues(); + if(!xValues.is()) + return false; + Reference<beans::XPropertySet> xPropSet(xValues, uno::UNO_QUERY); + if (!xPropSet.is()) + continue; + + OUString aRoleName; + xPropSet->getPropertyValue("Role") >>= aRoleName; + if (aRoleName == "values-y") + { + const uno::Sequence<uno::Any> aData = xValues->getData(); + for (const auto& rVal : aData) + { + double fVal; + OUString sStr; + if (rVal >>= fVal) + continue; + else if (rVal >>= sStr) + bHasNoGapBlankValue = true; + else + bHasEmptyCell = true; + } + } + } + } + + return bHasNoGapBlankValue && !bHasEmptyCell; +} + +void ChartSpaceConverter::convertFromModel( const Reference< XShapes >& rxExternalPage, const awt::Point& rChartPos ) +{ + /* create data provider (virtual function in the ChartConverter class, + derived converters may create an external data provider) */ + getChartConverter().createDataProvider( getChartDocument() ); + + // formatting of the chart background. The default fill style varies with applications. + PropertySet aBackPropSet( getChartDocument()->getPageBackground() ); + getFormatter().convertFrameFormatting( aBackPropSet, mrModel.mxShapeProp, OBJECTTYPE_CHARTSPACE ); + + bool bMSO2007Doc = getFilter().isMSO2007Document(); + // convert plot area (container of all chart type groups) + PlotAreaConverter aPlotAreaConv( *this, mrModel.mxPlotArea.getOrCreate() ); + aPlotAreaConv.convertFromModel( mrModel.mxView3D.getOrCreate(bMSO2007Doc) ); + + // plot area converter has created the diagram object + Reference< XDiagram > xDiagram = getChartDocument()->getFirstDiagram(); + + // convert wall and floor formatting in 3D charts + if( xDiagram.is() && aPlotAreaConv.isWall3dChart() ) + { + WallFloorConverter aFloorConv( *this, mrModel.mxFloor.getOrCreate() ); + aFloorConv.convertFromModel( xDiagram, OBJECTTYPE_FLOOR ); + + WallFloorConverter aWallConv( *this, mrModel.mxBackWall.getOrCreate() ); + aWallConv.convertFromModel( xDiagram, OBJECTTYPE_WALL ); + } + + // chart title + /* tdf#119138 autoTitleDeleted might be omitted by generators other than Excel + while providing custom title. mbAutoTitleDel is set only based on the attribute value + and the default also varies on whether MSO 2007 or newer is the generator, see tdf#78080 */ + if( !mrModel.mbAutoTitleDel || mrModel.mxTitle.is() ) try + { + /* If the title model is missing, but the chart shows exactly one + series, the series title is shown as chart title. */ + OUString aAutoTitle = aPlotAreaConv.getAutomaticTitle(); + if( mrModel.mxTitle.is() || !aAutoTitle.isEmpty() ) + { + if( aAutoTitle.isEmpty() ) + aAutoTitle = OoxResId(STR_DIAGRAM_TITLE); + Reference< XTitled > xTitled( getChartDocument(), UNO_QUERY_THROW ); + TitleConverter aTitleConv( *this, mrModel.mxTitle.getOrCreate() ); + aTitleConv.convertFromModel( xTitled, aAutoTitle, OBJECTTYPE_CHARTTITLE ); + } + } + catch( Exception& ) + { + } + + // legend + if( xDiagram.is() && mrModel.mxLegend.is() ) + { + LegendConverter aLegendConv( *this, *mrModel.mxLegend ); + aLegendConv.convertFromModel( xDiagram ); + } + + // treatment of missing values + if( xDiagram.is() ) + { + using namespace ::com::sun::star::chart::MissingValueTreatment; + sal_Int32 nMissingValues = LEAVE_GAP; + + // tdf#134118 leave gap if the time unit is month + bool bIsMonthBasedTimeUnit = false; + if( mrModel.mxPlotArea.is() && mrModel.mxPlotArea->maAxes.size() > 0 && + mrModel.mxPlotArea->maAxes[0]->monBaseTimeUnit.has() ) + { + bIsMonthBasedTimeUnit = mrModel.mxPlotArea->maAxes[0]->monBaseTimeUnit.get() == XML_months; + } + + if (!bIsMonthBasedTimeUnit) switch( mrModel.mnDispBlanksAs ) + { + case XML_gap: nMissingValues = LEAVE_GAP; break; + case XML_zero: nMissingValues = USE_ZERO; break; + case XML_span: nMissingValues = CONTINUE; break; + } + + // use a workaround, if it's possible for the difference of OOXML and OpenDocument + if ( nMissingValues == LEAVE_GAP && lcl_useWorkaroundForNoGapInOOXML(getChartDocument()) ) + nMissingValues = USE_ZERO; + + PropertySet aDiaProp( xDiagram ); + aDiaProp.setProperty( PROP_MissingValueTreatment, nMissingValues ); + } + + /* Following all conversions needing the old Chart1 API that involves full + initialization of the chart view. */ + namespace cssc = ::com::sun::star::chart; + Reference< cssc::XChartDocument > xChart1Doc( getChartDocument(), UNO_QUERY ); + if( xChart1Doc.is() ) + { + /* Set the IncludeHiddenCells property via the old API as only this + ensures that the data provider and all created sequences get this + flag correctly. */ + PropertySet aDiaProp( xChart1Doc->getDiagram() ); + aDiaProp.setProperty( PROP_IncludeHiddenCells, !mrModel.mbPlotVisOnly ); + + // plot area position and size + aPlotAreaConv.convertPositionFromModel(); + + // positions of main title and all axis titles + convertTitlePositions(); + } + + // embedded drawing shapes + if( !mrModel.maDrawingPath.isEmpty() ) try + { + /* Get the internal draw page of the chart document, if no external + drawing page has been passed. */ + Reference< XShapes > xShapes; + awt::Point aShapesOffset( 0, 0 ); + if( rxExternalPage.is() ) + { + xShapes = rxExternalPage; + // offset for embedded shapes to move them inside the chart area + aShapesOffset = rChartPos; + } + else + { + Reference< XDrawPageSupplier > xDrawPageSupp( getChartDocument(), UNO_QUERY_THROW ); + xShapes.set( xDrawPageSupp->getDrawPage(), UNO_QUERY_THROW ); + } + + /* If an external drawing page is passed, all embedded shapes will be + inserted there (used e.g. with 'chart sheets' in spreadsheet + documents). In this case, all types of shapes including OLE objects + are supported. If the shapes are inserted into the internal chart + drawing page instead, it is not possible to embed OLE objects. */ + bool bOleSupport = rxExternalPage.is(); + + awt::Size aChartSize = getChartSize(); + if( aChartSize.Width <= 0 || aChartSize.Height <= 0 ) + aChartSize = getDefaultPageSize(); + + // now, xShapes is not null anymore + getFilter().importFragment( new ChartDrawingFragment( + getFilter(), mrModel.maDrawingPath, xShapes, aChartSize, aShapesOffset, bOleSupport ) ); + } + catch( Exception& ) + { + } + + // pivot chart + if ( mrModel.mbPivotChart ) + { + PropertySet aProps( getChartDocument() ); + aProps.setProperty( PROP_DisableDataTableDialog , true ); + aProps.setProperty( PROP_DisableComplexChartTypes , true ); + } + + if(!mrModel.maSheetPath.isEmpty() ) + { + Reference< css::chart::XChartDocument > xChartDoc( getChartDocument(), UNO_QUERY ); + PropertySet aProps( xChartDoc->getDiagram() ); + aProps.setProperty( PROP_ExternalData , uno::Any(mrModel.maSheetPath) ); + } +} + +} // namespace oox::drawingml::chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |