1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <drawingml/chart/chartspaceconverter.hxx>
22 #include <com/sun/star/chart/MissingValueTreatment.hpp>
23 #include <com/sun/star/chart/XChartDocument.hpp>
24 #include <com/sun/star/chart2/XChartDocument.hpp>
25 #include <com/sun/star/chart2/XChartType.hpp>
26 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
27 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
28 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
29 #include <com/sun/star/chart2/XTitled.hpp>
30 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
31 #include <oox/core/xmlfilterbase.hxx>
32 #include <oox/drawingml/chart/chartconverter.hxx>
33 #include <oox/token/properties.hxx>
34 #include <oox/token/tokens.hxx>
35 #include <drawingml/chart/chartdrawingfragment.hxx>
36 #include <drawingml/chart/chartspacemodel.hxx>
37 #include <drawingml/chart/plotareaconverter.hxx>
38 #include <drawingml/chart/titleconverter.hxx>
39 #include <ooxresid.hxx>
40 #include <strings.hrc>
41 #include <drawingml/textbody.hxx>
43 using namespace ::com::sun::star
;
44 using ::com::sun::star::uno::Reference
;
45 using ::com::sun::star::uno::Exception
;
46 using ::com::sun::star::uno::UNO_QUERY
;
47 using ::com::sun::star::uno::UNO_QUERY_THROW
;
48 using ::com::sun::star::uno::Any
;
49 using ::com::sun::star::drawing::XDrawPageSupplier
;
50 using ::com::sun::star::drawing::XShapes
;
51 using ::com::sun::star::chart2::XDiagram
;
52 using ::com::sun::star::chart2::XTitled
;
54 namespace oox::drawingml::chart
{
56 using namespace ::com::sun::star::awt
;
57 using namespace ::com::sun::star::chart2
;
58 using namespace ::com::sun::star::chart2::data
;
59 using namespace ::com::sun::star::drawing
;
60 using namespace ::com::sun::star::uno
;
62 ChartSpaceConverter::ChartSpaceConverter( const ConverterRoot
& rParent
, ChartSpaceModel
& rModel
) :
63 ConverterBase
< ChartSpaceModel
>( rParent
, rModel
)
67 ChartSpaceConverter::~ChartSpaceConverter()
71 // Formulas with no numeric values and strings are zeroes in OOXML line charts also in the mode DispBlanksAs=gap,
72 // unlike in OpenDocument LEAVE_GAP mode. As a workaround, we will use the OpenDocument mode USE_ZERO, if the OOXML
73 // line chart has got formulas with no numeric values and strings, but it doesn't have empty cells, showing the
74 // same chart as in MSO. (Empty cells need gaps, so in that case, we cannot use this workaround).
75 static bool lcl_useWorkaroundForNoGapInOOXML( Reference
< chart2::XChartDocument
> const & xChartDoc
)
77 Reference
<chart2::XDiagram
> xDiagram
= xChartDoc
->getFirstDiagram();
81 Reference
< chart2::XCoordinateSystemContainer
> xCooSysContainer( xDiagram
, UNO_QUERY_THROW
);
83 Sequence
< Reference
< chart2::XCoordinateSystem
> > xCooSysSequence( xCooSysContainer
->getCoordinateSystems());
84 if ( !xCooSysSequence
.hasElements() )
87 Reference
< chart2::XChartTypeContainer
> xChartTypeContainer( xCooSysSequence
[0], UNO_QUERY_THROW
);
89 Sequence
< Reference
< chart2::XChartType
> > xChartTypeSequence( xChartTypeContainer
->getChartTypes() );
90 if ( !xChartTypeSequence
.hasElements() )
93 const Reference
<chart2::XChartType
>& xCT
= xChartTypeSequence
[0];
95 if ( xCT
->getChartType() != "com.sun.star.chart2.LineChartType" )
98 Reference
<chart2::XDataSeriesContainer
> xDSCont(xCT
, uno::UNO_QUERY
);
103 const Sequence
<uno::Reference
<chart2::XDataSeries
> > aDataSeriesSeq
= xDSCont
->getDataSeries();
105 bool bHasNoGapBlankValue
= false;
106 bool bHasEmptyCell
= false;
108 for (const auto& rDataSeries
: aDataSeriesSeq
)
110 uno::Reference
<chart2::data::XDataSource
> xDSrc(rDataSeries
, uno::UNO_QUERY
);
114 const uno::Sequence
<Reference
<chart2::data::XLabeledDataSequence
> > aDataSeqs
= xDSrc
->getDataSequences();
115 for (const auto& rDataSeq
: aDataSeqs
)
117 Reference
<chart2::data::XDataSequence
> xValues
= rDataSeq
->getValues();
120 Reference
<beans::XPropertySet
> xPropSet(xValues
, uno::UNO_QUERY
);
125 xPropSet
->getPropertyValue(u
"Role"_ustr
) >>= aRoleName
;
126 if (aRoleName
== "values-y")
128 const uno::Sequence
<uno::Any
> aData
= xValues
->getData();
129 for (const auto& rVal
: aData
)
135 else if (rVal
>>= sStr
)
136 bHasNoGapBlankValue
= true;
138 bHasEmptyCell
= true;
144 return bHasNoGapBlankValue
&& !bHasEmptyCell
;
147 void ChartSpaceConverter::convertFromModel( const Reference
< XShapes
>& rxExternalPage
, const awt::Point
& rChartPos
)
149 /* create data provider (virtual function in the ChartConverter class,
150 derived converters may create an external data provider) */
151 getChartConverter().createDataProvider( getChartDocument() );
153 // formatting of the chart background. The default fill style varies with applications.
154 PropertySet
aBackPropSet( getChartDocument()->getPageBackground() );
155 getFormatter().convertFrameFormatting( aBackPropSet
, mrModel
.mxShapeProp
, OBJECTTYPE_CHARTSPACE
);
157 bool bMSO2007Doc
= getFilter().isMSO2007Document();
158 // convert plot area (container of all chart type groups)
159 PlotAreaConverter
aPlotAreaConv( *this, mrModel
.mxPlotArea
.getOrCreate() );
160 aPlotAreaConv
.convertFromModel( mrModel
.mxView3D
.getOrCreate(bMSO2007Doc
) );
162 // plot area converter has created the diagram object
163 Reference
< XDiagram
> xDiagram
= getChartDocument()->getFirstDiagram();
165 // convert wall and floor formatting in 3D charts
166 if( xDiagram
.is() && aPlotAreaConv
.isWall3dChart() )
168 WallFloorConverter
aFloorConv( *this, mrModel
.mxFloor
.getOrCreate() );
169 aFloorConv
.convertFromModel( xDiagram
, OBJECTTYPE_FLOOR
);
171 WallFloorConverter
aWallConv( *this, mrModel
.mxBackWall
.getOrCreate() );
172 aWallConv
.convertFromModel( xDiagram
, OBJECTTYPE_WALL
);
176 /* tdf#119138 autoTitleDeleted might be omitted by generators other than Excel
177 while providing custom title. mbAutoTitleDel is set only based on the attribute value
178 and the default also varies on whether MSO 2007 or newer is the generator, see tdf#78080 */
179 if( !mrModel
.mbAutoTitleDel
|| mrModel
.mxTitle
.is() ) try
181 /* If the title model is missing, but the chart shows exactly one
182 series, the series title is shown as chart title. */
183 OUString aAutoTitle
= aPlotAreaConv
.getAutomaticTitle();
184 if( mrModel
.mxTitle
.is() || !aAutoTitle
.isEmpty() )
186 // tdf#146487 In some cases, we need to show the empty title
187 bool bShowEmptyTitle
= aAutoTitle
.isEmpty() && !mrModel
.mbAutoTitleDel
188 && aPlotAreaConv
.isSingleSeriesTitle()
189 && mrModel
.mxTitle
->mxShapeProp
.is()
190 && mrModel
.mxTitle
->mxTextProp
.is()
191 && mrModel
.mxTitle
->mxTextProp
->isEmpty();
192 // Also for tdf#146487
193 bool bEmptyRichText
= mrModel
.mxTitle
194 && mrModel
.mxTitle
->mxText
.is()
195 && mrModel
.mxTitle
->mxText
->mxTextBody
.is()
196 && mrModel
.mxTitle
->mxText
->mxTextBody
->isEmpty();
198 if (aAutoTitle
.isEmpty() && !bShowEmptyTitle
&& !bEmptyRichText
)
199 aAutoTitle
= OoxResId(STR_DIAGRAM_TITLE
);
200 Reference
< XTitled
> xTitled( getChartDocument(), UNO_QUERY_THROW
);
201 TitleConverter
aTitleConv( *this, mrModel
.mxTitle
.getOrCreate() );
202 aTitleConv
.convertFromModel( xTitled
, aAutoTitle
, OBJECTTYPE_CHARTTITLE
);
210 if( xDiagram
.is() && mrModel
.mxLegend
.is() )
212 LegendConverter
aLegendConv( *this, *mrModel
.mxLegend
);
213 aLegendConv
.convertFromModel( xDiagram
);
216 // treatment of missing values
219 using namespace ::com::sun::star::chart::MissingValueTreatment
;
220 sal_Int32 nMissingValues
= LEAVE_GAP
;
222 // tdf#134118 leave gap if the time unit is month
223 bool bIsMonthBasedTimeUnit
= false;
224 if( mrModel
.mxPlotArea
.is() && mrModel
.mxPlotArea
->maAxes
.size() > 0 &&
225 mrModel
.mxPlotArea
->maAxes
[0]->monBaseTimeUnit
.has_value() )
227 bIsMonthBasedTimeUnit
= mrModel
.mxPlotArea
->maAxes
[0]->monBaseTimeUnit
.value() == XML_months
;
230 if (!bIsMonthBasedTimeUnit
) switch( mrModel
.mnDispBlanksAs
)
232 case XML_gap
: nMissingValues
= LEAVE_GAP
; break;
233 case XML_zero
: nMissingValues
= USE_ZERO
; break;
234 case XML_span
: nMissingValues
= CONTINUE
; break;
237 // use a workaround, if it's possible for the difference of OOXML and OpenDocument
238 if ( nMissingValues
== LEAVE_GAP
&& lcl_useWorkaroundForNoGapInOOXML(getChartDocument()) )
239 nMissingValues
= USE_ZERO
;
241 PropertySet
aDiaProp( xDiagram
);
242 aDiaProp
.setProperty( PROP_MissingValueTreatment
, nMissingValues
);
245 /* Following all conversions needing the old Chart1 API that involves full
246 initialization of the chart view. */
247 namespace cssc
= css::chart
;
248 Reference
< cssc::XChartDocument
> xChart1Doc( getChartDocument(), UNO_QUERY
);
249 if( xChart1Doc
.is() )
251 /* Set the IncludeHiddenCells property via the old API as only this
252 ensures that the data provider and all created sequences get this
254 PropertySet
aDiaProp( xChart1Doc
->getDiagram() );
255 aDiaProp
.setProperty( PROP_IncludeHiddenCells
, !mrModel
.mbPlotVisOnly
);
257 // plot area position and size
258 aPlotAreaConv
.convertPositionFromModel();
260 // positions of main title and all axis titles
261 convertTitlePositions();
264 // embedded drawing shapes
265 if( !mrModel
.maDrawingPath
.isEmpty() ) try
267 /* Get the internal draw page of the chart document, if no external
268 drawing page has been passed. */
269 Reference
< XShapes
> xShapes
;
270 awt::Point
aShapesOffset( 0, 0 );
271 if( rxExternalPage
.is() )
273 xShapes
= rxExternalPage
;
274 // offset for embedded shapes to move them inside the chart area
275 aShapesOffset
= rChartPos
;
279 Reference
< XDrawPageSupplier
> xDrawPageSupp( getChartDocument(), UNO_QUERY_THROW
);
280 xShapes
.set( xDrawPageSupp
->getDrawPage(), UNO_QUERY_THROW
);
283 /* If an external drawing page is passed, all embedded shapes will be
284 inserted there (used e.g. with 'chart sheets' in spreadsheet
285 documents). In this case, all types of shapes including OLE objects
286 are supported. If the shapes are inserted into the internal chart
287 drawing page instead, it is not possible to embed OLE objects. */
288 bool bOleSupport
= rxExternalPage
.is();
290 awt::Size aChartSize
= getChartSize();
291 if( aChartSize
.Width
<= 0 || aChartSize
.Height
<= 0 )
292 aChartSize
= getDefaultPageSize();
294 // now, xShapes is not null anymore
295 getFilter().importFragment( new ChartDrawingFragment(
296 getFilter(), mrModel
.maDrawingPath
, xShapes
, aChartSize
, aShapesOffset
, bOleSupport
) );
303 if ( mrModel
.mbPivotChart
)
305 PropertySet
aProps( getChartDocument() );
306 aProps
.setProperty( PROP_DisableDataTableDialog
, true );
307 aProps
.setProperty( PROP_DisableComplexChartTypes
, true );
310 if(!mrModel
.maSheetPath
.isEmpty() )
312 Reference
< css::chart::XChartDocument
> xChartDoc( getChartDocument(), UNO_QUERY
);
313 PropertySet
aProps( xChartDoc
->getDiagram() );
314 aProps
.setProperty( PROP_ExternalData
, uno::Any(mrModel
.maSheetPath
) );
318 } // namespace oox::drawingml::chart
320 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */