Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / oox / source / drawingml / chart / plotareaconverter.cxx
blob24ab5f0bfd3e8f777532e4f59a7aca1aeb26e9ea
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/plotareaconverter.hxx>
22 #include <com/sun/star/chart/XChartDocument.hpp>
23 #include <com/sun/star/chart/XDiagramPositioning.hpp>
24 #include <com/sun/star/chart2/XChartDocument.hpp>
25 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
26 #include <com/sun/star/chart2/XDiagram.hpp>
27 #include <com/sun/star/drawing/Direction3D.hpp>
28 #include <com/sun/star/drawing/ProjectionMode.hpp>
29 #include <com/sun/star/drawing/ShadeMode.hpp>
30 #include <osl/diagnose.h>
31 #include <drawingml/chart/axisconverter.hxx>
32 #include <drawingml/chart/plotareamodel.hxx>
33 #include <drawingml/chart/typegroupconverter.hxx>
34 #include <oox/core/xmlfilterbase.hxx>
35 #include <oox/token/namespaces.hxx>
36 #include <oox/token/properties.hxx>
37 #include <oox/token/tokens.hxx>
38 #include <tools/helpers.hxx>
40 namespace oox {
41 namespace drawingml {
42 namespace chart {
44 using namespace ::com::sun::star;
45 using namespace ::com::sun::star::chart2;
46 using namespace ::com::sun::star::uno;
48 namespace {
50 /** Axes set model. This is a helper for the plot area converter collecting all
51 type groups and axes of the primary or secondary axes set. */
52 struct AxesSetModel
54 typedef ModelVector< TypeGroupModel > TypeGroupVector;
55 typedef ModelMap< sal_Int32, AxisModel > AxisMap;
57 TypeGroupVector maTypeGroups; /// All type groups containing data series.
58 AxisMap maAxes; /// All axes mapped by API axis type.
60 explicit AxesSetModel() {}
63 /** Axes set converter. This is a helper class for the plot area converter. */
64 class AxesSetConverter : public ConverterBase< AxesSetModel >
66 public:
67 explicit AxesSetConverter( const ConverterRoot& rParent, AxesSetModel& rModel );
69 /** Converts the axes set model to a chart2 diagram. Returns an automatic
70 chart title from a single series title, if possible. */
71 void convertFromModel(
72 const Reference< XDiagram >& rxDiagram,
73 View3DModel& rView3DModel,
74 sal_Int32 nAxesSetIdx,
75 bool bSupportsVaryColorsByPoint );
77 /** Returns the automatic chart title if the axes set contains only one series. */
78 const OUString& getAutomaticTitle() const { return maAutoTitle; }
79 /** Returns true, if the chart is three-dimensional. */
80 bool is3dChart() const { return mb3dChart; }
81 /** Returns true, if chart type supports wall and floor format in 3D mode. */
82 bool isWall3dChart() const { return mbWall3dChart; }
83 /** Returns true, if chart is a pie chart or doughnut chart. */
84 bool isPieChart() const { return mbPieChart; }
86 private:
87 OUString maAutoTitle;
88 bool mb3dChart;
89 bool mbWall3dChart;
90 bool mbPieChart;
93 AxesSetConverter::AxesSetConverter( const ConverterRoot& rParent, AxesSetModel& rModel ) :
94 ConverterBase< AxesSetModel >( rParent, rModel ),
95 mb3dChart( false ),
96 mbWall3dChart( false ),
97 mbPieChart( false )
101 ModelRef< AxisModel > lclGetOrCreateAxis( const AxesSetModel::AxisMap& rFromAxes, sal_Int32 nAxisIdx, sal_Int32 nDefTypeId, bool bMSO2007Doc )
103 ModelRef< AxisModel > xAxis = rFromAxes.get( nAxisIdx );
104 if( !xAxis )
105 xAxis.create( nDefTypeId, bMSO2007Doc ).mbDeleted = true; // missing axis is invisible
106 return xAxis;
109 void AxesSetConverter::convertFromModel( const Reference< XDiagram >& rxDiagram,
110 View3DModel& rView3DModel, sal_Int32 nAxesSetIdx, bool bSupportsVaryColorsByPoint )
112 // create type group converter objects for all type groups
113 typedef RefVector< TypeGroupConverter > TypeGroupConvVector;
114 TypeGroupConvVector aTypeGroups;
115 for (auto const& typeGroup : mrModel.maTypeGroups)
116 aTypeGroups.push_back( std::make_shared<TypeGroupConverter>( *this, *typeGroup ) );
118 OSL_ENSURE( !aTypeGroups.empty(), "AxesSetConverter::convertFromModel - no type groups in axes set" );
119 if( !aTypeGroups.empty() ) try
121 // first type group needed for coordinate system and axis conversion
122 TypeGroupConverter& rFirstTypeGroup = *aTypeGroups.front();
124 // get automatic chart title, if there is only one type group
125 if( aTypeGroups.size() == 1 )
126 maAutoTitle = rFirstTypeGroup.getSingleSeriesTitle();
128 /* Create a coordinate system. For now, all type groups from all axes sets
129 have to be inserted into one coordinate system. Later, chart2 should
130 support using one coordinate system for each axes set. */
131 Reference< XCoordinateSystem > xCoordSystem;
132 Reference< XCoordinateSystemContainer > xCoordSystemCont( rxDiagram, UNO_QUERY_THROW );
133 Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
134 if( aCoordSystems.hasElements() )
136 OSL_ENSURE( aCoordSystems.getLength() == 1, "AxesSetConverter::convertFromModel - too many coordinate systems" );
137 xCoordSystem = aCoordSystems[ 0 ];
138 OSL_ENSURE( xCoordSystem.is(), "AxesSetConverter::convertFromModel - invalid coordinate system" );
140 else
142 xCoordSystem = rFirstTypeGroup.createCoordinateSystem();
143 if( xCoordSystem.is() )
144 xCoordSystemCont->addCoordinateSystem( xCoordSystem );
147 // 3D view settings
148 mb3dChart = rFirstTypeGroup.is3dChart();
149 mbWall3dChart = rFirstTypeGroup.isWall3dChart();
150 mbPieChart = rFirstTypeGroup.getTypeInfo().meTypeCategory == TYPECATEGORY_PIE;
151 if( mb3dChart )
153 View3DConverter aView3DConv( *this, rView3DModel );
154 aView3DConv.convertFromModel( rxDiagram, rFirstTypeGroup );
157 /* Convert all chart type groups. Each type group will add its series
158 to the data provider attached to the chart document. */
159 if( xCoordSystem.is() )
161 bool bMSO2007Doc = getFilter().isMSO2007Document();
162 // convert all axes (create missing axis models)
163 ModelRef< AxisModel > xXAxis = lclGetOrCreateAxis( mrModel.maAxes, API_X_AXIS, rFirstTypeGroup.getTypeInfo().mbCategoryAxis ? C_TOKEN( catAx ) : C_TOKEN( valAx ), bMSO2007Doc );
164 ModelRef< AxisModel > xYAxis = lclGetOrCreateAxis( mrModel.maAxes, API_Y_AXIS, C_TOKEN( valAx ), bMSO2007Doc );
166 AxisConverter aXAxisConv( *this, *xXAxis );
167 aXAxisConv.convertFromModel( xCoordSystem, aTypeGroups, xYAxis.get(), nAxesSetIdx, API_X_AXIS );
168 AxisConverter aYAxisConv( *this, *xYAxis );
169 aYAxisConv.convertFromModel( xCoordSystem, aTypeGroups, xXAxis.get(), nAxesSetIdx, API_Y_AXIS );
171 if( rFirstTypeGroup.isDeep3dChart() )
173 ModelRef< AxisModel > xZAxis = lclGetOrCreateAxis( mrModel.maAxes, API_Z_AXIS, C_TOKEN( serAx ), bMSO2007Doc );
174 AxisConverter aZAxisConv( *this, *xZAxis );
175 aZAxisConv.convertFromModel( xCoordSystem, aTypeGroups, nullptr, nAxesSetIdx, API_Z_AXIS );
178 // convert all chart type groups, this converts all series data and formatting
179 for (auto const& typeGroup : aTypeGroups)
180 typeGroup->convertFromModel( rxDiagram, xCoordSystem, nAxesSetIdx, bSupportsVaryColorsByPoint );
183 catch( Exception& )
188 } // namespace
190 View3DConverter::View3DConverter( const ConverterRoot& rParent, View3DModel& rModel ) :
191 ConverterBase< View3DModel >( rParent, rModel )
195 View3DConverter::~View3DConverter()
199 void View3DConverter::convertFromModel( const Reference< XDiagram >& rxDiagram, TypeGroupConverter const & rTypeGroup )
201 namespace cssd = ::com::sun::star::drawing;
202 PropertySet aPropSet( rxDiagram );
204 sal_Int32 nRotationY = 0;
205 sal_Int32 nRotationX = 0;
206 bool bRightAngled = false;
207 sal_Int32 nAmbientColor = 0;
208 sal_Int32 nLightColor = 0;
210 if( rTypeGroup.getTypeInfo().meTypeCategory == TYPECATEGORY_PIE )
212 // Y rotation used as 'first pie slice angle' in 3D pie charts
213 rTypeGroup.convertPieRotation( aPropSet, mrModel.monRotationY.get( 0 ) );
214 // X rotation a.k.a. elevation (map OOXML [0..90] to Chart2 [-90,0])
215 nRotationX = getLimitedValue< sal_Int32, sal_Int32 >( mrModel.monRotationX.get( 15 ), 0, 90 ) - 90;
216 // no right-angled axes in pie charts
217 bRightAngled = false;
218 // ambient color (Gray 30%)
219 nAmbientColor = 0xB3B3B3;
220 // light color (Gray 70%)
221 nLightColor = 0x4C4C4C;
223 else // 3D bar/area/line charts
225 // Y rotation (OOXML [0..359], Chart2 [-179,180])
226 nRotationY = mrModel.monRotationY.get( 20 );
227 // X rotation a.k.a. elevation (OOXML [-90..90], Chart2 [-179,180])
228 nRotationX = getLimitedValue< sal_Int32, sal_Int32 >( mrModel.monRotationX.get( 15 ), -90, 90 );
229 // right-angled axes
230 bRightAngled = mrModel.mbRightAngled;
231 // ambient color (Gray 20%)
232 nAmbientColor = 0xCCCCCC;
233 // light color (Gray 60%)
234 nLightColor = 0x666666;
237 // Y rotation (map OOXML [0..359] to Chart2 [-179,180])
238 nRotationY = NormAngle180(nRotationY);
239 /* Perspective (map OOXML [0..200] to Chart2 [0,100]). Seems that MSO 2007 is
240 buggy here, the XML plugin of MSO 2003 writes the correct perspective in
241 the range from 0 to 100. We will emulate the wrong behaviour of MSO 2007. */
242 sal_Int32 nPerspective = getLimitedValue< sal_Int32, sal_Int32 >( mrModel.mnPerspective / 2, 0, 100 );
243 // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%)
244 bool bParallel = bRightAngled || (nPerspective == 0);
245 cssd::ProjectionMode eProjMode = bParallel ? cssd::ProjectionMode_PARALLEL : cssd::ProjectionMode_PERSPECTIVE;
247 // set rotation properties
248 aPropSet.setProperty( PROP_RightAngledAxes, bRightAngled );
249 aPropSet.setProperty( PROP_RotationVertical, nRotationY );
250 aPropSet.setProperty( PROP_RotationHorizontal, nRotationX );
251 aPropSet.setProperty( PROP_Perspective, nPerspective );
252 aPropSet.setProperty( PROP_D3DScenePerspective, eProjMode );
254 // set light settings
255 aPropSet.setProperty( PROP_D3DSceneShadeMode, cssd::ShadeMode_FLAT );
256 aPropSet.setProperty( PROP_D3DSceneAmbientColor, nAmbientColor );
257 aPropSet.setProperty( PROP_D3DSceneLightOn1, false );
258 aPropSet.setProperty( PROP_D3DSceneLightOn2, true );
259 aPropSet.setProperty( PROP_D3DSceneLightColor2, nLightColor );
260 aPropSet.setProperty( PROP_D3DSceneLightDirection2, cssd::Direction3D( 0.2, 0.4, 1.0 ) );
263 WallFloorConverter::WallFloorConverter( const ConverterRoot& rParent, WallFloorModel& rModel ) :
264 ConverterBase< WallFloorModel >( rParent, rModel )
268 WallFloorConverter::~WallFloorConverter()
272 void WallFloorConverter::convertFromModel( const Reference< XDiagram >& rxDiagram, ObjectType eObjType )
274 bool bMSO2007Doc = getFilter().isMSO2007Document();
275 if( rxDiagram.is() )
277 PropertySet aPropSet;
278 switch( eObjType )
280 case OBJECTTYPE_FLOOR: aPropSet.set( rxDiagram->getFloor() ); break;
281 case OBJECTTYPE_WALL: aPropSet.set( rxDiagram->getWall() ); break;
282 default: OSL_FAIL( "WallFloorConverter::convertFromModel - invalid object type" );
284 if( aPropSet.is() )
285 getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, mrModel.mxPicOptions.getOrCreate(bMSO2007Doc), eObjType );
289 DataTableConverter::DataTableConverter( const ConverterRoot& rParent, DataTableModel& rModel ) :
290 ConverterBase< DataTableModel >( rParent, rModel )
294 DataTableConverter::~DataTableConverter()
298 void DataTableConverter::convertFromModel( const Reference< XDiagram >& rxDiagram )
300 PropertySet aPropSet( rxDiagram );
301 if (mrModel.mbShowHBorder)
302 aPropSet.setProperty( PROP_DataTableHBorder, mrModel.mbShowHBorder );
303 if (mrModel.mbShowVBorder)
304 aPropSet.setProperty( PROP_DataTableVBorder, mrModel.mbShowVBorder);
305 if (mrModel.mbShowOutline)
306 aPropSet.setProperty( PROP_DataTableOutline, mrModel.mbShowOutline );
309 PlotAreaConverter::PlotAreaConverter( const ConverterRoot& rParent, PlotAreaModel& rModel ) :
310 ConverterBase< PlotAreaModel >( rParent, rModel ),
311 mb3dChart( false ),
312 mbWall3dChart( false ),
313 mbPieChart( false )
317 PlotAreaConverter::~PlotAreaConverter()
321 void PlotAreaConverter::convertFromModel( View3DModel& rView3DModel )
323 /* Create the diagram object and attach it to the chart document. One
324 diagram is used to carry all coordinate systems and data series. */
325 Reference< XDiagram > xDiagram;
328 xDiagram.set( createInstance( "com.sun.star.chart2.Diagram" ), UNO_QUERY_THROW );
329 getChartDocument()->setFirstDiagram( xDiagram );
331 catch( Exception& )
335 // store all axis models in a map, keyed by axis identifier
336 typedef ModelMap< sal_Int32, AxisModel > AxisMap;
337 AxisMap aAxisMap;
338 std::vector<sal_Int32>rValAxisIds;
339 std::vector<sal_Int32>rRealValAxisIds;
341 for (auto const& atypeGroup : mrModel.maTypeGroups)
343 if (atypeGroup->maAxisIds.size() > 1)
345 // let's collect which axId belongs to the Y Axis according to maTypeGroups
346 rRealValAxisIds.push_back(atypeGroup->maAxisIds[1]);
350 for (auto const& axis : mrModel.maAxes)
352 OSL_ENSURE( axis->mnAxisId >= 0, "PlotAreaConverter::convertFromModel - invalid axis identifier" );
353 OSL_ENSURE( !aAxisMap.has( axis->mnAxisId ), "PlotAreaConverter::convertFromModel - axis identifiers not unique" );
354 if( axis->mnAxisId != -1 )
355 aAxisMap[ axis->mnAxisId ] = axis;
357 if ( axis->mnAxisId != -1 && axis->mnTypeId == C_TOKEN(valAx) )
359 for (size_t i = 0; i < rRealValAxisIds.size(); i++)
361 if (axis->mnAxisId == rRealValAxisIds[i])
363 // let's collect which axId belongs to the Y Axis according to maAxes
364 rValAxisIds.push_back(axis->mnAxisId);
370 // group the type group models into different axes sets
371 typedef ModelVector< AxesSetModel > AxesSetVector;
372 AxesSetVector aAxesSets;
373 sal_Int32 nMaxSeriesIdx = -1;
374 for (auto const& typeGroup : mrModel.maTypeGroups)
376 if( !typeGroup->maSeries.empty() )
378 // try to find a compatible axes set for the type group
379 AxesSetModel* pAxesSet = nullptr;
380 for (auto const& axesSet : aAxesSets)
382 if( axesSet->maTypeGroups.front()->maAxisIds == typeGroup->maAxisIds )
384 pAxesSet = axesSet.get();
385 if (pAxesSet)
386 break;
390 // not possible to insert into an existing axes set -> start a new axes set
391 if( !pAxesSet )
393 pAxesSet = &aAxesSets.create();
394 // find axis models used by the type group
395 const std::vector<sal_Int32>& rAxisIds = typeGroup->maAxisIds;
396 if( !rAxisIds.empty() )
397 pAxesSet->maAxes[ API_X_AXIS ] = aAxisMap.get( rAxisIds[ 0 ] );
398 if( rAxisIds.size() >= 2 )
399 pAxesSet->maAxes[ API_Y_AXIS ] = aAxisMap.get( rAxisIds[ 1 ] );
400 if( rAxisIds.size() >= 3 )
401 pAxesSet->maAxes[ API_Z_AXIS ] = aAxisMap.get( rAxisIds[ 2 ] );
404 // insert the type group model
405 pAxesSet->maTypeGroups.push_back( typeGroup );
407 // collect the maximum series index for automatic series formatting
408 for (auto const& elemSeries : typeGroup->maSeries)
409 nMaxSeriesIdx = ::std::max( nMaxSeriesIdx, elemSeries->mnIndex );
412 getFormatter().setMaxSeriesIndex( nMaxSeriesIdx );
414 // varying point colors only for single series in single chart type
415 bool bSupportsVaryColorsByPoint = mrModel.maTypeGroups.size() == 1;
417 // convert all axes sets, and check which axis is attached to the first maTypeGroups
418 sal_Int32 nStartAxesSetIdx = (rValAxisIds.size() > 1 && aAxesSets[0]->maAxes[1]->mnAxisId != rValAxisIds[0] ) ? 1 : 0;
419 sal_Int32 nAxesSetIdx = nStartAxesSetIdx;
421 for (auto const& axesSet : aAxesSets)
423 if( !axesSet->maAxes.empty() && mrModel.maTypeGroups.size() > sal::static_int_cast<sal_uInt32>(nAxesSetIdx) )
424 mrModel.maTypeGroups[nAxesSetIdx]->mbCatAxisVisible = !axesSet->maAxes[0]->mbDeleted;
425 AxesSetConverter aAxesSetConv(*this, *axesSet);
426 aAxesSetConv.convertFromModel( xDiagram, rView3DModel, nAxesSetIdx, bSupportsVaryColorsByPoint );
427 if(nAxesSetIdx == nStartAxesSetIdx)
429 maAutoTitle = aAxesSetConv.getAutomaticTitle();
430 mb3dChart = aAxesSetConv.is3dChart();
431 mbWall3dChart = aAxesSetConv.isWall3dChart();
432 mbPieChart = aAxesSetConv.isPieChart();
434 else
436 maAutoTitle.clear();
438 nAxesSetIdx = 1 - nAxesSetIdx;
441 DataTableConverter dataTableConverter (*this, mrModel.mxDataTable.getOrCreate());
442 dataTableConverter.convertFromModel(xDiagram);
443 // plot area formatting
444 if( xDiagram.is() && !mb3dChart )
446 PropertySet aPropSet( xDiagram->getWall() );
447 getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, OBJECTTYPE_PLOTAREA2D );
451 void PlotAreaConverter::convertPositionFromModel()
453 LayoutModel& rLayout = mrModel.mxLayout.getOrCreate();
454 LayoutConverter aLayoutConv( *this, rLayout );
455 awt::Rectangle aDiagramRect;
456 if( aLayoutConv.calcAbsRectangle( aDiagramRect ) ) try
458 namespace cssc = ::com::sun::star::chart;
459 Reference< cssc::XChartDocument > xChart1Doc( getChartDocument(), UNO_QUERY_THROW );
460 Reference< cssc::XDiagramPositioning > xPositioning( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
461 // for pie charts, always set inner plot area size to exclude the data labels as Excel does
462 sal_Int32 nTarget = (mbPieChart && (rLayout.mnTarget == XML_outer)) ? XML_inner : rLayout.mnTarget;
463 switch( nTarget )
465 case XML_inner:
466 xPositioning->setDiagramPositionExcludingAxes( aDiagramRect );
467 break;
468 case XML_outer:
469 xPositioning->setDiagramPositionIncludingAxes( aDiagramRect );
470 break;
471 default:
472 OSL_FAIL( "PlotAreaConverter::convertPositionFromModel - unknown positioning target" );
475 catch( Exception& )
480 } // namespace chart
481 } // namespace drawingml
482 } // namespace oox
484 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */