1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 #include "oox/vml/vmlshapecontext.hxx" 29 30 #include "oox/vml/vmldrawing.hxx" 31 #include "oox/vml/vmlshape.hxx" 32 #include "oox/vml/vmlshapecontainer.hxx" 33 #include "oox/vml/vmltextboxcontext.hxx" 34 35 namespace oox { 36 namespace vml { 37 38 // ============================================================================ 39 40 using namespace ::com::sun::star::awt; 41 42 using ::oox::core::ContextHandler2; 43 using ::oox::core::ContextHandler2Helper; 44 using ::oox::core::ContextHandlerRef; 45 using ::rtl::OUString; 46 47 // ============================================================================ 48 49 namespace { 50 51 /** Returns the boolean value from the specified VML attribute (if present). 52 */ 53 OptValue< bool > lclDecodeBool( const AttributeList& rAttribs, sal_Int32 nToken ) 54 { 55 OptValue< OUString > oValue = rAttribs.getString( nToken ); 56 if( oValue.has() ) return OptValue< bool >( ConversionHelper::decodeBool( oValue.get() ) ); 57 return OptValue< bool >(); 58 } 59 60 /** Returns the percentage value from the specified VML attribute (if present). 61 The value will be normalized (1.0 is returned for 100%). 62 */ 63 OptValue< double > lclDecodePercent( const AttributeList& rAttribs, sal_Int32 nToken, double fDefValue ) 64 { 65 OptValue< OUString > oValue = rAttribs.getString( nToken ); 66 if( oValue.has() ) return OptValue< double >( ConversionHelper::decodePercent( oValue.get(), fDefValue ) ); 67 return OptValue< double >(); 68 } 69 70 /** Returns the integer value pair from the specified VML attribute (if present). 71 */ 72 OptValue< Int32Pair > lclDecodeInt32Pair( const AttributeList& rAttribs, sal_Int32 nToken ) 73 { 74 OptValue< OUString > oValue = rAttribs.getString( nToken ); 75 OptValue< Int32Pair > oRetValue; 76 if( oValue.has() ) 77 { 78 OUString aValue1, aValue2; 79 ConversionHelper::separatePair( aValue1, aValue2, oValue.get(), ',' ); 80 oRetValue = Int32Pair( aValue1.toInt32(), aValue2.toInt32() ); 81 } 82 return oRetValue; 83 } 84 85 /** Returns the percentage pair from the specified VML attribute (if present). 86 */ 87 OptValue< DoublePair > lclDecodePercentPair( const AttributeList& rAttribs, sal_Int32 nToken ) 88 { 89 OptValue< OUString > oValue = rAttribs.getString( nToken ); 90 OptValue< DoublePair > oRetValue; 91 if( oValue.has() ) 92 { 93 OUString aValue1, aValue2; 94 ConversionHelper::separatePair( aValue1, aValue2, oValue.get(), ',' ); 95 oRetValue = DoublePair( 96 ConversionHelper::decodePercent( aValue1, 0.0 ), 97 ConversionHelper::decodePercent( aValue2, 0.0 ) ); 98 } 99 return oRetValue; 100 } 101 102 /** Returns the boolean value from the passed string of an attribute in the x: 103 namespace (VML for spreadsheets). Supported values: f, t, False, True. 104 @param bDefaultForEmpty Default value for the empty string. 105 */ 106 bool lclDecodeVmlxBool( const OUString& rValue, bool bDefaultForEmpty ) 107 { 108 if( rValue.getLength() == 0 ) return bDefaultForEmpty; 109 sal_Int32 nToken = AttributeConversion::decodeToken( rValue ); 110 // anything else than 't' or 'True' is considered to be false, as specified 111 return (nToken == XML_t) || (nToken == XML_True); 112 } 113 114 } // namespace 115 116 // ============================================================================ 117 118 ShapeLayoutContext::ShapeLayoutContext( ContextHandler2Helper& rParent, Drawing& rDrawing ) : 119 ContextHandler2( rParent ), 120 mrDrawing( rDrawing ) 121 { 122 } 123 124 125 ContextHandlerRef ShapeLayoutContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) 126 { 127 switch( nElement ) 128 { 129 case O_TOKEN( idmap ): 130 { 131 OUString aBlockIds = rAttribs.getString( XML_data, OUString() ); 132 sal_Int32 nIndex = 0; 133 while( nIndex >= 0 ) 134 { 135 OUString aToken = aBlockIds.getToken( 0, ' ', nIndex ).trim(); 136 if( aToken.getLength() > 0 ) 137 mrDrawing.registerBlockId( aToken.toInt32() ); 138 } 139 } 140 break; 141 } 142 return 0; 143 } 144 145 // ============================================================================ 146 147 ClientDataContext::ClientDataContext( ContextHandler2Helper& rParent, 148 ClientData& rClientData, const AttributeList& rAttribs ) : 149 ContextHandler2( rParent ), 150 mrClientData( rClientData ) 151 { 152 mrClientData.mnObjType = rAttribs.getToken( XML_ObjectType, XML_TOKEN_INVALID ); 153 } 154 155 ContextHandlerRef ClientDataContext::onCreateContext( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ ) 156 { 157 if( isRootElement() ) 158 { 159 maElementText = OUString(); 160 return this; 161 } 162 return 0; 163 } 164 165 void ClientDataContext::onCharacters( const OUString& rChars ) 166 { 167 /* Empty but existing elements have special meaning, e.g. 'true'. Collect 168 existing text and convert it in onEndElement(). */ 169 maElementText = rChars; 170 } 171 172 void ClientDataContext::onEndElement() 173 { 174 switch( getCurrentElement() ) 175 { 176 case VMLX_TOKEN( Anchor ): mrClientData.maAnchor = maElementText; break; 177 case VMLX_TOKEN( FmlaMacro ): mrClientData.maFmlaMacro = maElementText; break; 178 case VMLX_TOKEN( FmlaPict ): mrClientData.maFmlaPict = maElementText; break; 179 case VMLX_TOKEN( FmlaLink ): mrClientData.maFmlaLink = maElementText; break; 180 case VMLX_TOKEN( FmlaRange ): mrClientData.maFmlaRange = maElementText; break; 181 case VMLX_TOKEN( FmlaGroup ): mrClientData.maFmlaGroup = maElementText; break; 182 case VMLX_TOKEN( TextHAlign ): mrClientData.mnTextHAlign = AttributeConversion::decodeToken( maElementText ); break; 183 case VMLX_TOKEN( TextVAlign ): mrClientData.mnTextVAlign = AttributeConversion::decodeToken( maElementText ); break; 184 case VMLX_TOKEN( Column ): mrClientData.mnCol = maElementText.toInt32(); break; 185 case VMLX_TOKEN( Row ): mrClientData.mnRow = maElementText.toInt32(); break; 186 case VMLX_TOKEN( Checked ): mrClientData.mnChecked = maElementText.toInt32(); break; 187 case VMLX_TOKEN( DropStyle ): mrClientData.mnDropStyle = AttributeConversion::decodeToken( maElementText ); break; 188 case VMLX_TOKEN( DropLines ): mrClientData.mnDropLines = maElementText.toInt32(); break; 189 case VMLX_TOKEN( Val ): mrClientData.mnVal = maElementText.toInt32(); break; 190 case VMLX_TOKEN( Min ): mrClientData.mnMin = maElementText.toInt32(); break; 191 case VMLX_TOKEN( Max ): mrClientData.mnMax = maElementText.toInt32(); break; 192 case VMLX_TOKEN( Inc ): mrClientData.mnInc = maElementText.toInt32(); break; 193 case VMLX_TOKEN( Page ): mrClientData.mnPage = maElementText.toInt32(); break; 194 case VMLX_TOKEN( SelType ): mrClientData.mnSelType = AttributeConversion::decodeToken( maElementText ); break; 195 case VMLX_TOKEN( VTEdit ): mrClientData.mnVTEdit = maElementText.toInt32(); break; 196 case VMLX_TOKEN( PrintObject ): mrClientData.mbPrintObject = lclDecodeVmlxBool( maElementText, true ); break; 197 case VMLX_TOKEN( Visible ): mrClientData.mbVisible = lclDecodeVmlxBool( maElementText, true ); break; 198 case VMLX_TOKEN( DDE ): mrClientData.mbDde = lclDecodeVmlxBool( maElementText, true ); break; 199 case VMLX_TOKEN( NoThreeD ): mrClientData.mbNo3D = lclDecodeVmlxBool( maElementText, true ); break; 200 case VMLX_TOKEN( NoThreeD2 ): mrClientData.mbNo3D2 = lclDecodeVmlxBool( maElementText, true ); break; 201 case VMLX_TOKEN( MultiLine ): mrClientData.mbMultiLine = lclDecodeVmlxBool( maElementText, true ); break; 202 case VMLX_TOKEN( VScroll ): mrClientData.mbVScroll = lclDecodeVmlxBool( maElementText, true ); break; 203 case VMLX_TOKEN( SecretEdit ): mrClientData.mbSecretEdit = lclDecodeVmlxBool( maElementText, true ); break; 204 } 205 } 206 207 // ============================================================================ 208 209 ShapeContextBase::ShapeContextBase( ContextHandler2Helper& rParent ) : 210 ContextHandler2( rParent ) 211 { 212 } 213 214 /*static*/ ContextHandlerRef ShapeContextBase::createShapeContext( ContextHandler2Helper& rParent, 215 ShapeContainer& rShapes, sal_Int32 nElement, const AttributeList& rAttribs ) 216 { 217 switch( nElement ) 218 { 219 case O_TOKEN( shapelayout ): 220 return new ShapeLayoutContext( rParent, rShapes.getDrawing() ); 221 222 case VML_TOKEN( shapetype ): 223 return new ShapeTypeContext( rParent, rShapes.createShapeType(), rAttribs ); 224 case VML_TOKEN( group ): 225 return new GroupShapeContext( rParent, rShapes.createShape< GroupShape >(), rAttribs ); 226 case VML_TOKEN( shape ): 227 return new ShapeContext( rParent, rShapes.createShape< ComplexShape >(), rAttribs ); 228 case VML_TOKEN( rect ): 229 case VML_TOKEN( roundrect ): 230 return new ShapeContext( rParent, rShapes.createShape< RectangleShape >(), rAttribs ); 231 case VML_TOKEN( oval ): 232 return new ShapeContext( rParent, rShapes.createShape< EllipseShape >(), rAttribs ); 233 case VML_TOKEN( polyline ): 234 return new ShapeContext( rParent, rShapes.createShape< PolyLineShape >(), rAttribs ); 235 236 // TODO: 237 case VML_TOKEN( arc ): 238 case VML_TOKEN( curve ): 239 case VML_TOKEN( line ): 240 case VML_TOKEN( diagram ): 241 case VML_TOKEN( image ): 242 return new ShapeContext( rParent, rShapes.createShape< ComplexShape >(), rAttribs ); 243 } 244 return 0; 245 } 246 247 // ============================================================================ 248 249 ShapeTypeContext::ShapeTypeContext( ContextHandler2Helper& rParent, ShapeType& rShapeType, const AttributeList& rAttribs ) : 250 ShapeContextBase( rParent ), 251 mrTypeModel( rShapeType.getTypeModel() ) 252 { 253 // shape identifier and shape name 254 bool bHasOspid = rAttribs.hasAttribute( O_TOKEN( spid ) ); 255 mrTypeModel.maShapeId = rAttribs.getXString( bHasOspid ? O_TOKEN( spid ) : XML_id, OUString() ); 256 OSL_ENSURE( mrTypeModel.maShapeId.getLength() > 0, "ShapeTypeContext::ShapeTypeContext - missing shape identifier" ); 257 // if the o:spid attribute exists, the id attribute contains the user-defined shape name 258 if( bHasOspid ) 259 mrTypeModel.maShapeName = rAttribs.getXString( XML_id, OUString() ); 260 // builtin shape type identifier 261 mrTypeModel.moShapeType = rAttribs.getInteger( O_TOKEN( spt ) ); 262 263 // coordinate system position/size, CSS style 264 mrTypeModel.moCoordPos = lclDecodeInt32Pair( rAttribs, XML_coordorigin ); 265 mrTypeModel.moCoordSize = lclDecodeInt32Pair( rAttribs, XML_coordsize ); 266 setStyle( rAttribs.getString( XML_style, OUString() ) ); 267 268 // stroke settings (may be overridden by v:stroke element later) 269 mrTypeModel.maStrokeModel.moStroked = lclDecodeBool( rAttribs, XML_stroked ); 270 mrTypeModel.maStrokeModel.moColor = rAttribs.getString( XML_strokecolor ); 271 mrTypeModel.maStrokeModel.moWeight = rAttribs.getString( XML_strokeweight ); 272 273 // fill settings (may be overridden by v:fill element later) 274 mrTypeModel.maFillModel.moFilled = lclDecodeBool( rAttribs, XML_filled ); 275 mrTypeModel.maFillModel.moColor = rAttribs.getString( XML_fillcolor ); 276 } 277 278 ContextHandlerRef ShapeTypeContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) 279 { 280 if( isRootElement() ) switch( nElement ) 281 { 282 case VML_TOKEN( stroke ): 283 mrTypeModel.maStrokeModel.moStroked.assignIfUsed( lclDecodeBool( rAttribs, XML_on ) ); 284 mrTypeModel.maStrokeModel.maStartArrow.moArrowType = rAttribs.getToken( XML_startarrow ); 285 mrTypeModel.maStrokeModel.maStartArrow.moArrowWidth = rAttribs.getToken( XML_startarrowwidth ); 286 mrTypeModel.maStrokeModel.maStartArrow.moArrowLength = rAttribs.getToken( XML_startarrowlength ); 287 mrTypeModel.maStrokeModel.maEndArrow.moArrowType = rAttribs.getToken( XML_endarrow ); 288 mrTypeModel.maStrokeModel.maEndArrow.moArrowWidth = rAttribs.getToken( XML_endarrowwidth ); 289 mrTypeModel.maStrokeModel.maEndArrow.moArrowLength = rAttribs.getToken( XML_endarrowlength ); 290 mrTypeModel.maStrokeModel.moColor.assignIfUsed( rAttribs.getString( XML_color ) ); 291 mrTypeModel.maStrokeModel.moOpacity = lclDecodePercent( rAttribs, XML_opacity, 1.0 ); 292 mrTypeModel.maStrokeModel.moWeight.assignIfUsed( rAttribs.getString( XML_weight ) ); 293 mrTypeModel.maStrokeModel.moDashStyle = rAttribs.getString( XML_dashstyle ); 294 mrTypeModel.maStrokeModel.moLineStyle = rAttribs.getToken( XML_linestyle ); 295 mrTypeModel.maStrokeModel.moEndCap = rAttribs.getToken( XML_endcap ); 296 mrTypeModel.maStrokeModel.moJoinStyle = rAttribs.getToken( XML_joinstyle ); 297 break; 298 case VML_TOKEN( fill ): 299 mrTypeModel.maFillModel.moFilled.assignIfUsed( lclDecodeBool( rAttribs, XML_on ) ); 300 mrTypeModel.maFillModel.moColor.assignIfUsed( rAttribs.getString( XML_color ) ); 301 mrTypeModel.maFillModel.moOpacity = lclDecodePercent( rAttribs, XML_opacity, 1.0 ); 302 mrTypeModel.maFillModel.moColor2 = rAttribs.getString( XML_color2 ); 303 mrTypeModel.maFillModel.moOpacity2 = lclDecodePercent( rAttribs, XML_opacity2, 1.0 ); 304 mrTypeModel.maFillModel.moType = rAttribs.getToken( XML_type ); 305 mrTypeModel.maFillModel.moAngle = rAttribs.getInteger( XML_angle ); 306 mrTypeModel.maFillModel.moFocus = lclDecodePercent( rAttribs, XML_focus, 0.0 ); 307 mrTypeModel.maFillModel.moFocusPos = lclDecodePercentPair( rAttribs, XML_focusposition ); 308 mrTypeModel.maFillModel.moFocusSize = lclDecodePercentPair( rAttribs, XML_focussize ); 309 mrTypeModel.maFillModel.moBitmapPath = decodeFragmentPath( rAttribs, O_TOKEN( relid ) ); 310 mrTypeModel.maFillModel.moRotate = lclDecodeBool( rAttribs, XML_rotate ); 311 break; 312 case VML_TOKEN( imagedata ): 313 mrTypeModel.moGraphicPath = decodeFragmentPath( rAttribs, O_TOKEN( relid ) ); 314 mrTypeModel.moGraphicTitle = rAttribs.getString( O_TOKEN( title ) ); 315 break; 316 } 317 return 0; 318 } 319 320 OptValue< OUString > ShapeTypeContext::decodeFragmentPath( const AttributeList& rAttribs, sal_Int32 nToken ) const 321 { 322 OptValue< OUString > oFragmentPath; 323 OptValue< OUString > oRelId = rAttribs.getString( nToken ); 324 if( oRelId.has() ) 325 oFragmentPath = getFragmentPathFromRelId( oRelId.get() ); 326 return oFragmentPath; 327 } 328 329 void ShapeTypeContext::setStyle( const OUString& rStyle ) 330 { 331 sal_Int32 nIndex = 0; 332 while( nIndex >= 0 ) 333 { 334 OUString aName, aValue; 335 if( ConversionHelper::separatePair( aName, aValue, rStyle.getToken( 0, ';', nIndex ), ':' ) ) 336 { 337 if( aName.equalsAscii( "position" ) ) mrTypeModel.maPosition = aValue; 338 else if( aName.equalsAscii( "left" ) ) mrTypeModel.maLeft = aValue; 339 else if( aName.equalsAscii( "top" ) ) mrTypeModel.maTop = aValue; 340 else if( aName.equalsAscii( "width" ) ) mrTypeModel.maWidth = aValue; 341 else if( aName.equalsAscii( "height" ) ) mrTypeModel.maHeight = aValue; 342 else if( aName.equalsAscii( "margin-left" ) ) mrTypeModel.maMarginLeft = aValue; 343 else if( aName.equalsAscii( "margin-top" ) ) mrTypeModel.maMarginTop = aValue; 344 } 345 } 346 } 347 348 // ============================================================================ 349 350 ShapeContext::ShapeContext( ContextHandler2Helper& rParent, ShapeBase& rShape, const AttributeList& rAttribs ) : 351 ShapeTypeContext( rParent, rShape, rAttribs ), 352 mrShapeModel( rShape.getShapeModel() ) 353 { 354 // collect shape specific attributes 355 mrShapeModel.maType = rAttribs.getXString( XML_type, OUString() ); 356 // polyline path 357 setPoints( rAttribs.getString( XML_points, OUString() ) ); 358 } 359 360 ContextHandlerRef ShapeContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) 361 { 362 // Excel specific shape client data 363 if( isRootElement() ) switch( nElement ) 364 { 365 case VML_TOKEN( textbox ): 366 return new TextBoxContext( *this, mrShapeModel.createTextBox(), rAttribs ); 367 case VMLX_TOKEN( ClientData ): 368 return new ClientDataContext( *this, mrShapeModel.createClientData(), rAttribs ); 369 } 370 // handle remaining stuff in base class 371 return ShapeTypeContext::onCreateContext( nElement, rAttribs ); 372 } 373 374 void ShapeContext::setPoints( const OUString& rPoints ) 375 { 376 mrShapeModel.maPoints.clear(); 377 sal_Int32 nIndex = 0; 378 while( nIndex >= 0 ) 379 { 380 sal_Int32 nX = rPoints.getToken( 0, ',', nIndex ).toInt32(); 381 sal_Int32 nY = rPoints.getToken( 0, ',', nIndex ).toInt32(); 382 mrShapeModel.maPoints.push_back( Point( nX, nY ) ); 383 } 384 } 385 386 // ============================================================================ 387 388 GroupShapeContext::GroupShapeContext( ContextHandler2Helper& rParent, GroupShape& rShape, const AttributeList& rAttribs ) : 389 ShapeContext( rParent, rShape, rAttribs ), 390 mrShapes( rShape.getChildren() ) 391 { 392 } 393 394 ContextHandlerRef GroupShapeContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) 395 { 396 // try to create a context of an embedded shape 397 ContextHandlerRef xContext = createShapeContext( *this, mrShapes, nElement, rAttribs ); 398 // handle remaining stuff of this shape in base class 399 return xContext.get() ? xContext : ShapeContext::onCreateContext( nElement, rAttribs ); 400 } 401 402 // ============================================================================ 403 404 } // namespace vml 405 } // namespace oox 406 407