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