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/converterbase.hxx>
22 #include <com/sun/star/chart/XAxisXSupplier.hpp>
23 #include <com/sun/star/chart/XAxisYSupplier.hpp>
24 #include <com/sun/star/chart/XAxisZSupplier.hpp>
25 #include <com/sun/star/chart/XChartDocument.hpp>
26 #include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp>
27 #include <com/sun/star/chart2/XChartDocument.hpp>
28 #include <com/sun/star/chart2/RelativePosition.hpp>
29 #include <com/sun/star/chart2/RelativeSize.hpp>
30 #include <com/sun/star/chart2/XTitle.hpp>
31 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #include <com/sun/star/uno/XComponentContext.hpp>
33 #include <osl/diagnose.h>
34 #include <sal/log.hxx>
35 #include <basegfx/numeric/ftools.hxx>
36 #include <oox/core/xmlfilterbase.hxx>
37 #include <oox/helper/helper.hxx>
38 #include <oox/token/properties.hxx>
39 #include <oox/token/tokens.hxx>
42 namespace oox::drawingml::chart
{
44 namespace cssc
= ::com::sun::star::chart
;
46 using namespace ::com::sun::star
;
47 using namespace ::com::sun::star::chart2
;
48 using namespace ::com::sun::star::drawing
;
49 using namespace ::com::sun::star::lang
;
50 using namespace ::com::sun::star::uno
;
52 using ::oox::core::XmlFilterBase
;
56 struct TitleKey
: public ::std::pair
< ObjectType
, ::std::pair
< sal_Int32
, sal_Int32
> >
58 explicit TitleKey( ObjectType eObjType
, sal_Int32 nMainIdx
= -1, sal_Int32 nSubIdx
= -1 )
59 { first
= eObjType
; second
.first
= nMainIdx
; second
.second
= nSubIdx
; }
62 /** A helper structure to store all data related to title objects. Needed for
63 the conversion of manual title positions that needs the old Chart1 API.
65 struct TitleLayoutInfo
67 typedef Reference
< XShape
> (*GetShapeFunc
)( const Reference
< cssc::XChartDocument
>& );
69 Reference
< XTitle
> mxTitle
; /// The API title object.
70 ModelRef
< LayoutModel
> mxLayout
; /// The layout model, if existing.
71 GetShapeFunc mpGetShape
; /// Helper function to receive the title shape.
73 explicit TitleLayoutInfo() : mpGetShape( nullptr ) {}
76 ConverterRoot
const & rRoot
,
77 const Reference
< cssc::XChartDocument
>& rxChart1Doc
);
80 void TitleLayoutInfo::convertTitlePos( ConverterRoot
const & rRoot
, const Reference
< cssc::XChartDocument
>& rxChart1Doc
)
82 if( !(mxTitle
.is() && mpGetShape
) )
87 // try to get the title shape
88 Reference
< XShape
> xTitleShape
= mpGetShape( rxChart1Doc
);
91 SAL_WARN("oox", "failed to get a TitleShape");
94 // get title rotation angle, needed for correction of position of top-left edge
96 PropertySet
aTitleProp( mxTitle
);
97 aTitleProp
.getProperty( fAngle
, PROP_TextRotation
);
98 // convert the position
99 LayoutModel
& rLayout
= mxLayout
.getOrCreate();
100 LayoutConverter
aLayoutConv( rRoot
, rLayout
);
101 aLayoutConv
.convertFromModel( xTitleShape
, fAngle
);
108 /* The following local functions implement getting the XShape interface of all
109 supported title objects (chart and axes). This needs some effort due to the
110 design of the old Chart1 API used to access these objects. */
112 /** A code fragment that returns a shape object from the passed shape supplier
113 using the specified interface function. Checks a boolean property first. */
114 #define OOX_FRAGMENT_GETTITLESHAPE( shape_supplier, supplier_func, property_name ) \
115 PropertySet aPropSet( shape_supplier ); \
116 if( shape_supplier.is() && aPropSet.getBoolProperty( PROP_##property_name ) ) \
117 return shape_supplier->supplier_func(); \
118 return Reference< XShape >(); \
120 /** Implements a function returning the drawing shape of an axis title, if
121 existing, using the specified API interface and its function. */
122 #define OOX_DEFINEFUNC_GETAXISTITLESHAPE( func_name, interface_type, supplier_func, property_name ) \
123 Reference< XShape > func_name( const Reference< cssc::XChartDocument >& rxChart1Doc ) \
125 Reference< cssc::interface_type > xAxisSupp( rxChart1Doc->getDiagram(), UNO_QUERY ); \
126 OOX_FRAGMENT_GETTITLESHAPE( xAxisSupp, supplier_func, property_name ) \
129 /** Returns the drawing shape of the main title, if existing. */
130 Reference
< XShape
> lclGetMainTitleShape( const Reference
< cssc::XChartDocument
>& rxChart1Doc
)
132 OOX_FRAGMENT_GETTITLESHAPE( rxChart1Doc
, getTitle
, HasMainTitle
)
135 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetXAxisTitleShape
, XAxisXSupplier
, getXAxisTitle
, HasXAxisTitle
)
136 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetYAxisTitleShape
, XAxisYSupplier
, getYAxisTitle
, HasYAxisTitle
)
137 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetZAxisTitleShape
, XAxisZSupplier
, getZAxisTitle
, HasZAxisTitle
)
138 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetSecXAxisTitleShape
, XSecondAxisTitleSupplier
, getSecondXAxisTitle
, HasSecondaryXAxisTitle
)
139 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetSecYAxisTitleShape
, XSecondAxisTitleSupplier
, getSecondYAxisTitle
, HasSecondaryYAxisTitle
)
141 #undef OOX_DEFINEFUNC_GETAXISTITLESHAPE
142 #undef OOX_IMPLEMENT_GETTITLESHAPE
148 ObjectFormatter maFormatter
;
149 std::map
< TitleKey
, TitleLayoutInfo
>
151 XmlFilterBase
& mrFilter
;
152 ChartConverter
& mrConverter
;
153 Reference
< XChartDocument
> mxDoc
;
156 explicit ConverterData(
157 XmlFilterBase
& rFilter
,
158 ChartConverter
& rChartConverter
,
159 const ChartSpaceModel
& rChartModel
,
160 const Reference
< XChartDocument
>& rxChartDoc
,
161 const awt::Size
& rChartSize
);
165 ConverterData::ConverterData(
166 XmlFilterBase
& rFilter
,
167 ChartConverter
& rChartConverter
,
168 const ChartSpaceModel
& rChartModel
,
169 const Reference
< XChartDocument
>& rxChartDoc
,
170 const awt::Size
& rChartSize
) :
171 maFormatter( rFilter
, rxChartDoc
, rChartModel
),
173 mrConverter( rChartConverter
),
177 OSL_ENSURE( mxDoc
.is(), "ConverterData::ConverterData - missing chart document" );
178 // lock the model to suppress internal updates during conversion
181 mxDoc
->lockControllers();
187 // prepare conversion of title positions
188 maTitles
[ TitleKey( OBJECTTYPE_CHARTTITLE
) ].mpGetShape
= lclGetMainTitleShape
;
189 maTitles
[ TitleKey( OBJECTTYPE_AXISTITLE
, API_PRIM_AXESSET
, API_X_AXIS
) ].mpGetShape
= lclGetXAxisTitleShape
;
190 maTitles
[ TitleKey( OBJECTTYPE_AXISTITLE
, API_PRIM_AXESSET
, API_Y_AXIS
) ].mpGetShape
= lclGetYAxisTitleShape
;
191 maTitles
[ TitleKey( OBJECTTYPE_AXISTITLE
, API_PRIM_AXESSET
, API_Z_AXIS
) ].mpGetShape
= lclGetZAxisTitleShape
;
192 maTitles
[ TitleKey( OBJECTTYPE_AXISTITLE
, API_SECN_AXESSET
, API_X_AXIS
) ].mpGetShape
= lclGetSecXAxisTitleShape
;
193 maTitles
[ TitleKey( OBJECTTYPE_AXISTITLE
, API_SECN_AXESSET
, API_Y_AXIS
) ].mpGetShape
= lclGetSecYAxisTitleShape
;
196 ConverterData::~ConverterData()
201 mxDoc
->unlockControllers();
208 ConverterRoot::ConverterRoot(
209 XmlFilterBase
& rFilter
,
210 ChartConverter
& rChartConverter
,
211 const ChartSpaceModel
& rChartModel
,
212 const Reference
< XChartDocument
>& rxChartDoc
,
213 const awt::Size
& rChartSize
) :
214 mxData( std::make_shared
<ConverterData
>( rFilter
, rChartConverter
, rChartModel
, rxChartDoc
, rChartSize
) )
218 ConverterRoot::~ConverterRoot()
222 Reference
< XInterface
> ConverterRoot::createInstance( const OUString
& rServiceName
) const
224 Reference
< XInterface
> xInt
;
227 Reference
<XMultiServiceFactory
> xMSF(getComponentContext()->getServiceManager(), uno::UNO_QUERY_THROW
);
229 xInt
= xMSF
->createInstance( rServiceName
);
234 OSL_ENSURE( xInt
.is(), "ConverterRoot::createInstance - cannot create instance" );
238 Reference
< XComponentContext
> const & ConverterRoot::getComponentContext() const
240 return mxData
->mrFilter
.getComponentContext();
243 XmlFilterBase
& ConverterRoot::getFilter() const
245 return mxData
->mrFilter
;
248 ChartConverter
& ConverterRoot::getChartConverter() const
250 return mxData
->mrConverter
;
253 Reference
< XChartDocument
> const & ConverterRoot::getChartDocument() const
255 return mxData
->mxDoc
;
258 const awt::Size
& ConverterRoot::getChartSize() const
260 return mxData
->maSize
;
263 ObjectFormatter
& ConverterRoot::getFormatter() const
265 return mxData
->maFormatter
;
268 void ConverterRoot::registerTitleLayout( const Reference
< XTitle
>& rxTitle
,
269 const ModelRef
< LayoutModel
>& rxLayout
, ObjectType eObjType
, sal_Int32 nMainIdx
, sal_Int32 nSubIdx
)
271 OSL_ENSURE( rxTitle
.is(), "ConverterRoot::registerTitleLayout - missing title object" );
272 TitleLayoutInfo
& rTitleInfo
= mxData
->maTitles
[ TitleKey( eObjType
, nMainIdx
, nSubIdx
) ];
273 OSL_ENSURE( rTitleInfo
.mpGetShape
, "ConverterRoot::registerTitleLayout - invalid title key" );
274 rTitleInfo
.mxTitle
= rxTitle
;
275 rTitleInfo
.mxLayout
= rxLayout
;
278 void ConverterRoot::convertTitlePositions()
282 Reference
< cssc::XChartDocument
> xChart1Doc( mxData
->mxDoc
, UNO_QUERY_THROW
);
283 for (auto & title
: mxData
->maTitles
)
284 title
.second
.convertTitlePos( *this, xChart1Doc
);
293 /** Returns a position value in the chart area in 1/100 mm. */
294 sal_Int32
lclCalcPosition( sal_Int32 nChartSize
, double fPos
, sal_Int32 nPosMode
)
298 case XML_edge
: // absolute start position as factor of chart size
299 return getLimitedValue
< sal_Int32
, double >( nChartSize
* fPos
+ 0.5, 0, nChartSize
);
300 case XML_factor
: // position relative to object default position
301 OSL_FAIL( "lclCalcPosition - relative positioning not supported" );
305 OSL_FAIL( "lclCalcPosition - unknown positioning mode" );
309 /** Returns a size value in the chart area in 1/100 mm. */
310 sal_Int32
lclCalcSize( sal_Int32 nPos
, sal_Int32 nChartSize
, double fSize
, sal_Int32 nSizeMode
)
312 sal_Int32 nValue
= getLimitedValue
< sal_Int32
, double >( nChartSize
* fSize
+ 0.5, 0, nChartSize
);
315 case XML_factor
: // passed value is width/height
317 case XML_edge
: // passed value is right/bottom position
318 return nValue
- nPos
+ 1;
321 OSL_FAIL( "lclCalcSize - unknown size mode" );
325 /** Returns a relative size value in the chart area. */
326 double lclCalcRelSize( double fPos
, double fSize
, sal_Int32 nSizeMode
)
330 case XML_factor
: // passed value is width/height
332 case XML_edge
: // passed value is right/bottom position
336 OSL_ENSURE( false, "lclCalcRelSize - unknown size mode" );
339 return getLimitedValue
< double, double >( fSize
, 0.0, 1.0 - fPos
);
344 LayoutConverter::LayoutConverter( const ConverterRoot
& rParent
, LayoutModel
& rModel
) :
345 ConverterBase
< LayoutModel
>( rParent
, rModel
)
349 LayoutConverter::~LayoutConverter()
353 bool LayoutConverter::calcAbsRectangle( awt::Rectangle
& orRect
) const
355 if( !mrModel
.mbAutoLayout
)
357 awt::Size aChartSize
= getChartSize();
358 if( aChartSize
.Width
<= 0 || aChartSize
.Height
<= 0 )
360 aChartSize
= getDefaultPageSize();
362 orRect
.X
= lclCalcPosition( aChartSize
.Width
, mrModel
.mfX
, mrModel
.mnXMode
);
363 orRect
.Y
= lclCalcPosition( aChartSize
.Height
, mrModel
.mfY
, mrModel
.mnYMode
);
364 if( (orRect
.X
>= 0) && (orRect
.Y
>= 0) )
366 orRect
.Width
= lclCalcSize( orRect
.X
, aChartSize
.Width
, mrModel
.mfW
, mrModel
.mnWMode
);
367 orRect
.Height
= lclCalcSize( orRect
.Y
, aChartSize
.Height
, mrModel
.mfH
, mrModel
.mnHMode
);
368 return (orRect
.Width
> 0) && (orRect
.Height
> 0);
374 bool LayoutConverter::convertFromModel( PropertySet
& rPropSet
)
376 if( !mrModel
.mbAutoLayout
&&
377 (mrModel
.mnXMode
== XML_edge
) && (mrModel
.mfX
>= 0.0) &&
378 (mrModel
.mnYMode
== XML_edge
) && (mrModel
.mfY
>= 0.0) )
380 RelativePosition
aPos(
381 getLimitedValue
< double, double >( mrModel
.mfX
, 0.0, 1.0 ),
382 getLimitedValue
< double, double >( mrModel
.mfY
, 0.0, 1.0 ),
383 Alignment_TOP_LEFT
);
384 rPropSet
.setProperty( PROP_RelativePosition
, aPos
);
387 lclCalcRelSize( aPos
.Primary
, mrModel
.mfW
, mrModel
.mnWMode
),
388 lclCalcRelSize( aPos
.Secondary
, mrModel
.mfH
, mrModel
.mnHMode
) );
389 if( (aSize
.Primary
> 0.0) && (aSize
.Secondary
> 0.0) )
391 rPropSet
.setProperty( PROP_RelativeSize
, aSize
);
398 void LayoutConverter::convertFromModel( const Reference
< XShape
>& rxShape
, double fRotationAngle
)
400 if( mrModel
.mbAutoLayout
)
403 awt::Size aChartSize
= getChartSize();
404 if( aChartSize
.Width
<= 0 || aChartSize
.Height
<= 0 )
406 aChartSize
= getDefaultPageSize();
408 awt::Point
aShapePos(
409 lclCalcPosition( aChartSize
.Width
, mrModel
.mfX
, mrModel
.mnXMode
),
410 lclCalcPosition( aChartSize
.Height
, mrModel
.mfY
, mrModel
.mnYMode
) );
411 if( (aShapePos
.X
< 0) || (aShapePos
.Y
< 0) )
414 bool bPropSet
= false;
415 // the call to XShape.getSize() may recalc the chart view
416 awt::Size aShapeSize
= rxShape
->getSize();
417 // rotated shapes need special handling...
418 if( aShapeSize
.Height
> 0 || aShapeSize
.Width
> 0 )
420 double fSin
= fabs(sin(basegfx::deg2rad(fRotationAngle
)));
421 // add part of height to X direction, if title is rotated down
422 if( fRotationAngle
> 180.0 )
423 aShapePos
.X
+= static_cast<sal_Int32
>(fSin
* aShapeSize
.Height
+ 0.5);
424 // add part of width to Y direction, if title is rotated up
425 else if( fRotationAngle
> 0.0 )
426 aShapePos
.Y
+= static_cast<sal_Int32
>(fSin
* aShapeSize
.Width
+ 0.5);
428 else if( fRotationAngle
== 90.0 || fRotationAngle
== 270.0 )
430 PropertySet
aShapeProp( rxShape
);
431 RelativePosition
aPos(
432 getLimitedValue
< double, double >(mrModel
.mfX
, 0.0, 1.0),
433 getLimitedValue
< double, double >(mrModel
.mfY
, 0.0, 1.0),
434 fRotationAngle
== 90.0 ? Alignment_TOP_RIGHT
: Alignment_BOTTOM_LEFT
);
435 // set the resulting position at the shape
436 if( aShapeProp
.setProperty(PROP_RelativePosition
, aPos
) )
439 // set the resulting position at the shape
441 rxShape
->setPosition( aShapePos
);
446 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */