1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "oox/drawingml/chart/converterbase.hxx"
25 
26 #include <com/sun/star/chart/XAxisXSupplier.hpp>
27 #include <com/sun/star/chart/XAxisYSupplier.hpp>
28 #include <com/sun/star/chart/XAxisZSupplier.hpp>
29 #include <com/sun/star/chart/XChartDocument.hpp>
30 #include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp>
31 #include <com/sun/star/chart2/RelativePosition.hpp>
32 #include <com/sun/star/chart2/RelativeSize.hpp>
33 #include <com/sun/star/drawing/FillStyle.hpp>
34 #include <com/sun/star/drawing/LineStyle.hpp>
35 #include <com/sun/star/frame/XModel.hpp>
36 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
37 #include <tools/solar.h>    // for F_PI180
38 #include "oox/core/xmlfilterbase.hxx"
39 #include "oox/drawingml/theme.hxx"
40 
41 namespace oox {
42 namespace drawingml {
43 namespace chart {
44 
45 // ============================================================================
46 
47 namespace cssc = ::com::sun::star::chart;
48 
49 using namespace ::com::sun::star::awt;
50 using namespace ::com::sun::star::chart2;
51 using namespace ::com::sun::star::drawing;
52 using namespace ::com::sun::star::frame;
53 using namespace ::com::sun::star::lang;
54 using namespace ::com::sun::star::uno;
55 
56 using ::oox::core::XmlFilterBase;
57 using ::rtl::OUString;
58 
59 // ============================================================================
60 
61 namespace {
62 
63 struct TitleKey : public ::std::pair< ObjectType, ::std::pair< sal_Int32, sal_Int32 > >
64 {
TitleKeyoox::drawingml::chart::__anon8752f2fc0111::TitleKey65     inline explicit     TitleKey( ObjectType eObjType, sal_Int32 nMainIdx = -1, sal_Int32 nSubIdx = -1 )
66                             { first = eObjType; second.first = nMainIdx; second.second = nSubIdx; }
67 };
68 
69 // ----------------------------------------------------------------------------
70 
71 /** A helper structure to store all data related to title objects. Needed for
72     the conversion of manual title positions that needs the old Chart1 API.
73  */
74 struct TitleLayoutInfo
75 {
76     typedef Reference< XShape > (*GetShapeFunc)( const Reference< cssc::XChartDocument >& );
77 
78     Reference< XTitle > mxTitle;        /// The API title object.
79     ModelRef< LayoutModel > mxLayout;   /// The layout model, if existing.
80     GetShapeFunc        mpGetShape;     /// Helper function to receive the title shape.
81 
TitleLayoutInfooox::drawingml::chart::__anon8752f2fc0111::TitleLayoutInfo82     inline explicit     TitleLayoutInfo() : mpGetShape( 0 ) {}
83 
84     void                convertTitlePos(
85                             ConverterRoot& rRoot,
86                             const Reference< cssc::XChartDocument >& rxChart1Doc );
87 };
88 
convertTitlePos(ConverterRoot & rRoot,const Reference<cssc::XChartDocument> & rxChart1Doc)89 void TitleLayoutInfo::convertTitlePos( ConverterRoot& rRoot, const Reference< cssc::XChartDocument >& rxChart1Doc )
90 {
91     if( mxTitle.is() && mpGetShape ) try
92     {
93         // try to get the title shape
94         Reference< XShape > xTitleShape( mpGetShape( rxChart1Doc ), UNO_SET_THROW );
95         // get title rotation angle, needed for correction of position of top-left edge
96         double fAngle = 0.0;
97         PropertySet aTitleProp( mxTitle );
98         aTitleProp.getProperty( fAngle, PROP_TextRotation );
99         // convert the position
100         LayoutModel& rLayout = mxLayout.getOrCreate();
101         LayoutConverter aLayoutConv( rRoot, rLayout );
102         aLayoutConv.convertFromModel( xTitleShape, fAngle );
103     }
104     catch( Exception& )
105     {
106     }
107 }
108 
109 // ----------------------------------------------------------------------------
110 
111 /*  The following local functions implement getting the XShape interface of all
112     supported title objects (chart and axes). This needs some effort due to the
113     design of the old Chart1 API used to access these objects. */
114 
115 /** A code fragment that returns a shape object from the passed shape supplier
116     using the specified interface function. Checks a boolean property first. */
117 #define OOX_FRAGMENT_GETTITLESHAPE( shape_supplier, supplier_func, property_name ) \
118     PropertySet aPropSet( shape_supplier ); \
119     if( shape_supplier.is() && aPropSet.getBoolProperty( PROP_##property_name ) ) \
120         return shape_supplier->supplier_func(); \
121     return Reference< XShape >(); \
122 
123 /** Implements a function returning the drawing shape of an axis title, if
124     existing, using the specified API interface and its function. */
125 #define OOX_DEFINEFUNC_GETAXISTITLESHAPE( func_name, interface_type, supplier_func, property_name ) \
126 Reference< XShape > func_name( const Reference< cssc::XChartDocument >& rxChart1Doc ) \
127 { \
128     Reference< cssc::interface_type > xAxisSupp( rxChart1Doc->getDiagram(), UNO_QUERY ); \
129     OOX_FRAGMENT_GETTITLESHAPE( xAxisSupp, supplier_func, property_name ) \
130 }
131 
132 /** Returns the drawing shape of the main title, if existing. */
lclGetMainTitleShape(const Reference<cssc::XChartDocument> & rxChart1Doc)133 Reference< XShape > lclGetMainTitleShape( const Reference< cssc::XChartDocument >& rxChart1Doc )
134 {
135     OOX_FRAGMENT_GETTITLESHAPE( rxChart1Doc, getTitle, HasMainTitle )
136 }
137 
138 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetXAxisTitleShape, XAxisXSupplier, getXAxisTitle, HasXAxisTitle )
139 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetYAxisTitleShape, XAxisYSupplier, getYAxisTitle, HasYAxisTitle )
140 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetZAxisTitleShape, XAxisZSupplier, getZAxisTitle, HasZAxisTitle )
141 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetSecXAxisTitleShape, XSecondAxisTitleSupplier, getSecondXAxisTitle, HasSecondaryXAxisTitle )
142 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetSecYAxisTitleShape, XSecondAxisTitleSupplier, getSecondYAxisTitle, HasSecondaryYAxisTitle )
143 
144 #undef OOX_DEFINEFUNC_GETAXISTITLESHAPE
145 #undef OOX_IMPLEMENT_GETTITLESHAPE
146 
147 } // namespace
148 
149 // ============================================================================
150 
151 struct ConverterData
152 {
153     typedef ::std::map< TitleKey, TitleLayoutInfo > TitleMap;
154 
155     ObjectFormatter     maFormatter;
156     TitleMap            maTitles;
157     XmlFilterBase&      mrFilter;
158     ChartConverter&     mrConverter;
159     Reference< XChartDocument > mxDoc;
160     Size                maSize;
161 
162     explicit            ConverterData(
163                             XmlFilterBase& rFilter,
164                             ChartConverter& rChartConverter,
165                             const ChartSpaceModel& rChartModel,
166                             const Reference< XChartDocument >& rxChartDoc,
167                             const Size& rChartSize );
168                         ~ConverterData();
169 };
170 
171 // ----------------------------------------------------------------------------
172 
ConverterData(XmlFilterBase & rFilter,ChartConverter & rChartConverter,const ChartSpaceModel & rChartModel,const Reference<XChartDocument> & rxChartDoc,const Size & rChartSize)173 ConverterData::ConverterData(
174         XmlFilterBase& rFilter,
175         ChartConverter& rChartConverter,
176         const ChartSpaceModel& rChartModel,
177         const Reference< XChartDocument >& rxChartDoc,
178         const Size& rChartSize ) :
179     maFormatter( rFilter, rxChartDoc, rChartModel ),
180     mrFilter( rFilter ),
181     mrConverter( rChartConverter ),
182     mxDoc( rxChartDoc ),
183     maSize( rChartSize )
184 {
185     OSL_ENSURE( mxDoc.is(), "ConverterData::ConverterData - missing chart document" );
186     // lock the model to suppress internal updates during conversion
187     try
188     {
189         Reference< XModel > xModel( mxDoc, UNO_QUERY_THROW );
190         xModel->lockControllers();
191     }
192     catch( Exception& )
193     {
194     }
195 
196     // prepare conversion of title positions
197     maTitles[ TitleKey( OBJECTTYPE_CHARTTITLE ) ].mpGetShape = lclGetMainTitleShape;
198     maTitles[ TitleKey( OBJECTTYPE_AXISTITLE, API_PRIM_AXESSET, API_X_AXIS ) ].mpGetShape = lclGetXAxisTitleShape;
199     maTitles[ TitleKey( OBJECTTYPE_AXISTITLE, API_PRIM_AXESSET, API_Y_AXIS ) ].mpGetShape = lclGetYAxisTitleShape;
200     maTitles[ TitleKey( OBJECTTYPE_AXISTITLE, API_PRIM_AXESSET, API_Z_AXIS ) ].mpGetShape = lclGetZAxisTitleShape;
201     maTitles[ TitleKey( OBJECTTYPE_AXISTITLE, API_SECN_AXESSET, API_X_AXIS ) ].mpGetShape = lclGetSecXAxisTitleShape;
202     maTitles[ TitleKey( OBJECTTYPE_AXISTITLE, API_SECN_AXESSET, API_Y_AXIS ) ].mpGetShape = lclGetSecYAxisTitleShape;
203 }
204 
~ConverterData()205 ConverterData::~ConverterData()
206 {
207     // unlock the model
208     try
209     {
210         Reference< XModel > xModel( mxDoc, UNO_QUERY_THROW );
211         xModel->unlockControllers();
212     }
213     catch( Exception& )
214     {
215     }
216 }
217 
218 // ============================================================================
219 
ConverterRoot(XmlFilterBase & rFilter,ChartConverter & rChartConverter,const ChartSpaceModel & rChartModel,const Reference<XChartDocument> & rxChartDoc,const Size & rChartSize)220 ConverterRoot::ConverterRoot(
221         XmlFilterBase& rFilter,
222         ChartConverter& rChartConverter,
223         const ChartSpaceModel& rChartModel,
224         const Reference< XChartDocument >& rxChartDoc,
225         const Size& rChartSize ) :
226     mxData( new ConverterData( rFilter, rChartConverter, rChartModel, rxChartDoc, rChartSize ) )
227 {
228 }
229 
~ConverterRoot()230 ConverterRoot::~ConverterRoot()
231 {
232 }
233 
createInstance(const OUString & rServiceName) const234 Reference< XInterface > ConverterRoot::createInstance( const OUString& rServiceName ) const
235 {
236     Reference< XInterface > xInt;
237     try
238     {
239         xInt = mxData->mrFilter.getServiceFactory()->createInstance( rServiceName );
240     }
241     catch( Exception& )
242     {
243     }
244     OSL_ENSURE( xInt.is(), "ConverterRoot::createInstance - cannot create instance" );
245     return xInt;
246 }
247 
getFilter() const248 XmlFilterBase& ConverterRoot::getFilter() const
249 {
250     return mxData->mrFilter;
251 }
252 
getChartConverter() const253 ChartConverter& ConverterRoot::getChartConverter() const
254 {
255     return mxData->mrConverter;
256 }
257 
getChartDocument() const258 Reference< XChartDocument > ConverterRoot::getChartDocument() const
259 {
260     return mxData->mxDoc;
261 }
262 
getChartSize() const263 const Size& ConverterRoot::getChartSize() const
264 {
265     return mxData->maSize;
266 }
267 
getFormatter() const268 ObjectFormatter& ConverterRoot::getFormatter() const
269 {
270     return mxData->maFormatter;
271 }
272 
registerTitleLayout(const Reference<XTitle> & rxTitle,const ModelRef<LayoutModel> & rxLayout,ObjectType eObjType,sal_Int32 nMainIdx,sal_Int32 nSubIdx)273 void ConverterRoot::registerTitleLayout( const Reference< XTitle >& rxTitle,
274         const ModelRef< LayoutModel >& rxLayout, ObjectType eObjType, sal_Int32 nMainIdx, sal_Int32 nSubIdx )
275 {
276     OSL_ENSURE( rxTitle.is(), "ConverterRoot::registerTitleLayout - missing title object" );
277     TitleLayoutInfo& rTitleInfo = mxData->maTitles[ TitleKey( eObjType, nMainIdx, nSubIdx ) ];
278     OSL_ENSURE( rTitleInfo.mpGetShape, "ConverterRoot::registerTitleLayout - invalid title key" );
279     rTitleInfo.mxTitle = rxTitle;
280     rTitleInfo.mxLayout = rxLayout;
281 }
282 
convertTitlePositions()283 void ConverterRoot::convertTitlePositions()
284 {
285     try
286     {
287         Reference< cssc::XChartDocument > xChart1Doc( mxData->mxDoc, UNO_QUERY_THROW );
288         for( ConverterData::TitleMap::iterator aIt = mxData->maTitles.begin(), aEnd = mxData->maTitles.end(); aIt != aEnd; ++aIt )
289             aIt->second.convertTitlePos( *this, xChart1Doc );
290     }
291     catch( Exception& )
292     {
293     }
294 }
295 
296 // ============================================================================
297 
298 namespace {
299 
300 /** Returns a position value in the chart area in 1/100 mm. */
lclCalcPosition(sal_Int32 nChartSize,double fPos,sal_Int32 nPosMode)301 sal_Int32 lclCalcPosition( sal_Int32 nChartSize, double fPos, sal_Int32 nPosMode )
302 {
303     switch( nPosMode )
304     {
305         case XML_edge:      // absolute start position as factor of chart size
306             return getLimitedValue< sal_Int32, double >( nChartSize * fPos + 0.5, 0, nChartSize );
307         case XML_factor:    // position relative to object default position
308             OSL_ENSURE( false, "lclCalcPosition - relative positioning not supported" );
309             return -1;
310     };
311 
312     OSL_ENSURE( false, "lclCalcPosition - unknown positioning mode" );
313     return -1;
314 }
315 
316 /** Returns a size value in the chart area in 1/100 mm. */
lclCalcSize(sal_Int32 nPos,sal_Int32 nChartSize,double fSize,sal_Int32 nSizeMode)317 sal_Int32 lclCalcSize( sal_Int32 nPos, sal_Int32 nChartSize, double fSize, sal_Int32 nSizeMode )
318 {
319     sal_Int32 nValue = getLimitedValue< sal_Int32, double >( nChartSize * fSize + 0.5, 0, nChartSize );
320     switch( nSizeMode )
321     {
322         case XML_factor:    // passed value is width/height
323             return nValue;
324         case XML_edge:      // passed value is right/bottom position
325             return nValue - nPos + 1;
326     };
327 
328     OSL_ENSURE( false, "lclCalcSize - unknown size mode" );
329     return -1;
330 }
331 
332 /** Returns a relative size value in the chart area. */
lclCalcRelSize(double fPos,double fSize,sal_Int32 nSizeMode)333 double lclCalcRelSize( double fPos, double fSize, sal_Int32 nSizeMode )
334 {
335     switch( nSizeMode )
336     {
337         case XML_factor:    // passed value is width/height
338         break;
339         case XML_edge:      // passed value is right/bottom position
340             fSize -= fPos;
341         break;
342         default:
343             OSL_ENSURE( false, "lclCalcRelSize - unknown size mode" );
344             fSize = 0.0;
345     };
346     return getLimitedValue< double, double >( fSize, 0.0, 1.0 - fPos );
347 }
348 
349 } // namespace
350 
351 // ----------------------------------------------------------------------------
352 
LayoutConverter(const ConverterRoot & rParent,LayoutModel & rModel)353 LayoutConverter::LayoutConverter( const ConverterRoot& rParent, LayoutModel& rModel ) :
354     ConverterBase< LayoutModel >( rParent, rModel )
355 {
356 }
357 
~LayoutConverter()358 LayoutConverter::~LayoutConverter()
359 {
360 }
361 
calcAbsRectangle(Rectangle & orRect) const362 bool LayoutConverter::calcAbsRectangle( Rectangle& orRect ) const
363 {
364     if( !mrModel.mbAutoLayout )
365     {
366         const Size& rChartSize = getChartSize();
367         orRect.X = lclCalcPosition( rChartSize.Width,  mrModel.mfX, mrModel.mnXMode );
368         orRect.Y = lclCalcPosition( rChartSize.Height, mrModel.mfY, mrModel.mnYMode );
369         if( (orRect.X >= 0) && (orRect.Y >= 0) )
370         {
371             orRect.Width  = lclCalcSize( orRect.X, rChartSize.Width,  mrModel.mfW, mrModel.mnWMode );
372             orRect.Height = lclCalcSize( orRect.Y, rChartSize.Height, mrModel.mfH, mrModel.mnHMode );
373             return (orRect.Width > 0) && (orRect.Height > 0);
374         }
375     }
376     return false;
377 }
378 
convertFromModel(PropertySet & rPropSet)379 bool LayoutConverter::convertFromModel( PropertySet& rPropSet )
380 {
381     if( !mrModel.mbAutoLayout &&
382         (mrModel.mnXMode == XML_edge) && (mrModel.mfX >= 0.0) &&
383         (mrModel.mnYMode == XML_edge) && (mrModel.mfY >= 0.0) )
384     {
385         RelativePosition aPos(
386             getLimitedValue< double, double >( mrModel.mfX, 0.0, 1.0 ),
387             getLimitedValue< double, double >( mrModel.mfY, 0.0, 1.0 ),
388             Alignment_TOP_LEFT );
389         rPropSet.setProperty( PROP_RelativePosition, aPos );
390 
391         RelativeSize aSize(
392             lclCalcRelSize( aPos.Primary, mrModel.mfW, mrModel.mnWMode ),
393             lclCalcRelSize( aPos.Secondary, mrModel.mfH, mrModel.mnHMode ) );
394         if( (aSize.Primary > 0.0) && (aSize.Secondary > 0.0) )
395         {
396             rPropSet.setProperty( PROP_RelativeSize, aSize );
397             return true;
398         }
399     }
400     return false;
401 }
402 
convertFromModel(const Reference<XShape> & rxShape,double fRotationAngle)403 bool LayoutConverter::convertFromModel( const Reference< XShape >& rxShape, double fRotationAngle )
404 {
405     if( !mrModel.mbAutoLayout )
406     {
407         const Size& rChartSize = getChartSize();
408         Point aShapePos(
409             lclCalcPosition( rChartSize.Width,  mrModel.mfX, mrModel.mnXMode ),
410             lclCalcPosition( rChartSize.Height, mrModel.mfY, mrModel.mnYMode ) );
411         if( (aShapePos.X >= 0) && (aShapePos.Y >= 0) )
412         {
413             // the call to XShape.getSize() may recalc the chart view
414             Size aShapeSize = rxShape->getSize();
415             // rotated shapes need special handling...
416             double fSin = fabs( sin( fRotationAngle * F_PI180 ) );
417             // add part of height to X direction, if title is rotated down
418             if( fRotationAngle > 180.0 )
419                 aShapePos.X += static_cast< sal_Int32 >( fSin * aShapeSize.Height + 0.5 );
420             // add part of width to Y direction, if title is rotated up
421             else if( fRotationAngle > 0.0 )
422                 aShapePos.Y += static_cast< sal_Int32 >( fSin * aShapeSize.Width + 0.5 );
423             // set the resulting position at the shape
424             rxShape->setPosition( aShapePos );
425             return true;
426         }
427     }
428     return false;
429 }
430 
431 // ============================================================================
432 
433 } // namespace chart
434 } // namespace drawingml
435 } // namespace oox
436