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 { 65 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 82 inline explicit TitleLayoutInfo() : mpGetShape( 0 ) {} 83 84 void convertTitlePos( 85 ConverterRoot& rRoot, 86 const Reference< cssc::XChartDocument >& rxChart1Doc ); 87 }; 88 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. */ 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 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 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 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 230 ConverterRoot::~ConverterRoot() 231 { 232 } 233 234 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 248 XmlFilterBase& ConverterRoot::getFilter() const 249 { 250 return mxData->mrFilter; 251 } 252 253 ChartConverter& ConverterRoot::getChartConverter() const 254 { 255 return mxData->mrConverter; 256 } 257 258 Reference< XChartDocument > ConverterRoot::getChartDocument() const 259 { 260 return mxData->mxDoc; 261 } 262 263 const Size& ConverterRoot::getChartSize() const 264 { 265 return mxData->maSize; 266 } 267 268 ObjectFormatter& ConverterRoot::getFormatter() const 269 { 270 return mxData->maFormatter; 271 } 272 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 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. */ 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. */ 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. */ 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 353 LayoutConverter::LayoutConverter( const ConverterRoot& rParent, LayoutModel& rModel ) : 354 ConverterBase< LayoutModel >( rParent, rModel ) 355 { 356 } 357 358 LayoutConverter::~LayoutConverter() 359 { 360 } 361 362 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 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 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