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/export/vmlexport.hxx> 25 26 #include <tokens.hxx> 27 28 #include <rtl/strbuf.hxx> 29 #include <rtl/ustring.hxx> 30 31 #include <tools/stream.hxx> 32 33 #include <cstdio> 34 35 using rtl::OString; 36 using rtl::OStringBuffer; 37 using rtl::OUString; 38 using rtl::OUStringBuffer; 39 40 using namespace sax_fastparser; 41 using namespace oox::vml; 42 43 /// Implementation of an empty stream that silently succeeds, but does nothing. 44 /// 45 /// In fact, this is a hack. The right solution is to abstract EscherEx to be 46 /// able to work without SvStream; but at the moment it is better to live with 47 /// this I guess. 48 class SvNullStream : public SvStream 49 { 50 protected: 51 virtual sal_Size GetData( void* pData, sal_Size nSize ) { memset( pData, 0, nSize ); return nSize; } 52 virtual sal_Size PutData( const void*, sal_Size nSize ) { return nSize; } 53 virtual sal_Size SeekPos( sal_Size nPos ) { return nPos; } 54 virtual void SetSize( sal_Size ) {} 55 virtual void FlushData() {} 56 57 public: 58 SvNullStream() : SvStream() {} 59 virtual ~SvNullStream() {} 60 }; 61 62 VMLExport::VMLExport( ::sax_fastparser::FSHelperPtr pSerializer ) 63 : EscherEx( *( new SvNullStream ), 0 ), 64 m_pSerializer( pSerializer ), 65 m_pShapeAttrList( NULL ), 66 m_nShapeType( ESCHER_ShpInst_Nil ), 67 m_pShapeStyle( new OStringBuffer( 200 ) ), 68 m_pShapeTypeWritten( new bool[ ESCHER_ShpInst_COUNT ] ) 69 { 70 mnGroupLevel = 1; 71 memset( m_pShapeTypeWritten, 0, ESCHER_ShpInst_COUNT * sizeof( bool ) ); 72 } 73 74 VMLExport::~VMLExport() 75 { 76 delete mpOutStrm, mpOutStrm = NULL; 77 delete m_pShapeStyle, m_pShapeStyle = NULL; 78 delete[] m_pShapeTypeWritten, m_pShapeTypeWritten = NULL; 79 } 80 81 void VMLExport::OpenContainer( UINT16 nEscherContainer, int nRecInstance ) 82 { 83 EscherEx::OpenContainer( nEscherContainer, nRecInstance ); 84 85 if ( nEscherContainer == ESCHER_SpContainer ) 86 { 87 // opening a shape container 88 #if OSL_DEBUG_LEVEL > 0 89 if ( m_nShapeType != ESCHER_ShpInst_Nil ) 90 fprintf( stderr, "Warning! VMLExport::OpenContainer(): opening shape inside a shape.\n" ); 91 #endif 92 m_nShapeType = ESCHER_ShpInst_Nil; 93 m_pShapeAttrList = m_pSerializer->createAttrList(); 94 95 if ( m_pShapeStyle->getLength() ) 96 m_pShapeStyle->makeStringAndClear(); 97 98 m_pShapeStyle->ensureCapacity( 200 ); 99 100 // postpone the ouput so that we are able to write even the elements 101 // that we learn inside Commit() 102 m_pSerializer->mark(); 103 } 104 } 105 106 void VMLExport::CloseContainer() 107 { 108 if ( mRecTypes.back() == ESCHER_SpContainer ) 109 { 110 // write the shape now when we have all the info 111 sal_Int32 nShapeElement = StartShape(); 112 113 m_pSerializer->mergeTopMarks(); 114 115 EndShape( nShapeElement ); 116 117 // cleanup 118 m_nShapeType = ESCHER_ShpInst_Nil; 119 m_pShapeAttrList = NULL; 120 } 121 122 EscherEx::CloseContainer(); 123 } 124 125 UINT32 VMLExport::EnterGroup( const String& rShapeName, const Rectangle* pRect ) 126 { 127 UINT32 nShapeId = GetShapeID(); 128 129 OStringBuffer aStyle( 200 ); 130 FastAttributeList *pAttrList = m_pSerializer->createAttrList(); 131 132 pAttrList->add( XML_id, ShapeIdString( nShapeId ) ); 133 134 if ( rShapeName.Len() ) 135 pAttrList->add( XML_alt, OUStringToOString( OUString( rShapeName ), RTL_TEXTENCODING_UTF8 ) ); 136 137 // style 138 if ( pRect ) 139 AddRectangleDimensions( aStyle, *pRect ); 140 141 if ( aStyle.getLength() ) 142 pAttrList->add( XML_style, aStyle.makeStringAndClear() ); 143 144 // coordorigin/coordsize 145 if ( pRect && ( mnGroupLevel == 1 ) ) 146 { 147 pAttrList->add( XML_coordorigin, 148 OStringBuffer( 20 ).append( sal_Int32( pRect->Left() ) ) 149 .append( "," ).append( sal_Int32( pRect->Top() ) ) 150 .makeStringAndClear() ); 151 152 pAttrList->add( XML_coordsize, 153 OStringBuffer( 20 ).append( sal_Int32( pRect->Right() ) - sal_Int32( pRect->Left() ) ) 154 .append( "," ).append( sal_Int32( pRect->Bottom() ) - sal_Int32( pRect->Top() ) ) 155 .makeStringAndClear() ); 156 } 157 158 m_pSerializer->startElementNS( XML_v, XML_group, XFastAttributeListRef( pAttrList ) ); 159 160 mnGroupLevel++; 161 return nShapeId; 162 } 163 164 void VMLExport::LeaveGroup() 165 { 166 --mnGroupLevel; 167 m_pSerializer->endElementNS( XML_v, XML_group ); 168 } 169 170 void VMLExport::AddShape( UINT32 nShapeType, UINT32 nShapeFlags, UINT32 nShapeId ) 171 { 172 m_nShapeType = nShapeType; 173 m_nShapeFlags = nShapeFlags; 174 175 m_pShapeAttrList->add( XML_id, ShapeIdString( nShapeId ) ); 176 } 177 178 static void impl_AddArrowHead( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue ) 179 { 180 if ( !pAttrList ) 181 return; 182 183 const char *pArrowHead = NULL; 184 switch ( nValue ) 185 { 186 case ESCHER_LineNoEnd: pArrowHead = "none"; break; 187 case ESCHER_LineArrowEnd: pArrowHead = "block"; break; 188 case ESCHER_LineArrowStealthEnd: pArrowHead = "classic"; break; 189 case ESCHER_LineArrowDiamondEnd: pArrowHead = "diamond"; break; 190 case ESCHER_LineArrowOvalEnd: pArrowHead = "oval"; break; 191 case ESCHER_LineArrowOpenEnd: pArrowHead = "open"; break; 192 } 193 194 if ( pArrowHead ) 195 pAttrList->add( nElement, pArrowHead ); 196 } 197 198 static void impl_AddArrowLength( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue ) 199 { 200 if ( !pAttrList ) 201 return; 202 203 const char *pArrowLength = NULL; 204 switch ( nValue ) 205 { 206 case ESCHER_LineShortArrow: pArrowLength = "short"; break; 207 case ESCHER_LineMediumLenArrow: pArrowLength = "medium"; break; 208 case ESCHER_LineLongArrow: pArrowLength = "long"; break; 209 } 210 211 if ( pArrowLength ) 212 pAttrList->add( nElement, pArrowLength ); 213 } 214 215 static void impl_AddArrowWidth( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue ) 216 { 217 if ( !pAttrList ) 218 return; 219 220 const char *pArrowWidth = NULL; 221 switch ( nValue ) 222 { 223 case ESCHER_LineNarrowArrow: pArrowWidth = "narrow"; break; 224 case ESCHER_LineMediumWidthArrow: pArrowWidth = "medium"; break; 225 case ESCHER_LineWideArrow: pArrowWidth = "wide"; break; 226 } 227 228 if ( pArrowWidth ) 229 pAttrList->add( nElement, pArrowWidth ); 230 } 231 232 static void impl_AddBool( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, bool bValue ) 233 { 234 if ( !pAttrList ) 235 return; 236 237 pAttrList->add( nElement, bValue? "t": "f" ); 238 } 239 240 static void impl_AddColor( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nColor ) 241 { 242 #if OSL_DEBUG_LEVEL > 0 243 if ( nColor & 0xFF000000 ) 244 fprintf( stderr, "TODO: this is not a RGB value!\n" ); 245 #endif 246 247 if ( !pAttrList || ( nColor & 0xFF000000 ) ) 248 return; 249 250 nColor = ( ( nColor & 0xFF ) << 16 ) + ( nColor & 0xFF00 ) + ( ( nColor & 0xFF0000 ) >> 16 ); 251 252 const char *pColor = NULL; 253 char pRgbColor[10]; 254 switch ( nColor ) 255 { 256 case 0x000000: pColor = "black"; break; 257 case 0xC0C0C0: pColor = "silver"; break; 258 case 0x808080: pColor = "gray"; break; 259 case 0xFFFFFF: pColor = "white"; break; 260 case 0x800000: pColor = "maroon"; break; 261 case 0xFF0000: pColor = "red"; break; 262 case 0x800080: pColor = "purple"; break; 263 case 0xFF00FF: pColor = "fuchsia"; break; 264 case 0x008000: pColor = "green"; break; 265 case 0x00FF00: pColor = "lime"; break; 266 case 0x808000: pColor = "olive"; break; 267 case 0xFFFF00: pColor = "yellow"; break; 268 case 0x000080: pColor = "navy"; break; 269 case 0x0000FF: pColor = "blue"; break; 270 case 0x008080: pColor = "teal"; break; 271 case 0x00FFFF: pColor = "aqua"; break; 272 default: 273 { 274 snprintf( pRgbColor, sizeof( pRgbColor ), "#%06x", static_cast< unsigned int >( nColor ) ); // not too handy to use OString::valueOf() here :-( 275 pColor = pRgbColor; 276 } 277 break; 278 } 279 280 pAttrList->add( nElement, pColor ); 281 } 282 283 static void impl_AddInt( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue ) 284 { 285 if ( !pAttrList ) 286 return; 287 288 pAttrList->add( nElement, OString::valueOf( static_cast< sal_Int32 >( nValue ) ).getStr() ); 289 } 290 291 inline sal_uInt16 impl_GetUInt16( const sal_uInt8* &pVal ) 292 { 293 sal_uInt16 nRet = *pVal++; 294 nRet += ( *pVal++ ) << 8; 295 return nRet; 296 } 297 298 inline sal_Int32 impl_GetPointComponent( const sal_uInt8* &pVal, sal_uInt16 nPointSize ) 299 { 300 sal_Int32 nRet = 0; 301 if ( ( nPointSize == 0xfff0 ) || ( nPointSize == 4 ) ) 302 { 303 sal_uInt16 nUnsigned = *pVal++; 304 nUnsigned += ( *pVal++ ) << 8; 305 306 nRet = sal_Int16( nUnsigned ); 307 } 308 else if ( nPointSize == 8 ) 309 { 310 sal_uInt32 nUnsigned = *pVal++; 311 nUnsigned += ( *pVal++ ) << 8; 312 nUnsigned += ( *pVal++ ) << 16; 313 nUnsigned += ( *pVal++ ) << 24; 314 315 nRet = nUnsigned; 316 } 317 318 return nRet; 319 } 320 321 void VMLExport::Commit( EscherPropertyContainer& rProps, const Rectangle& rRect ) 322 { 323 if ( m_nShapeType == ESCHER_ShpInst_Nil ) 324 return; 325 326 // postpone the output of the embedded elements so that they are written 327 // inside the shapes 328 m_pSerializer->mark(); 329 330 // dimensions 331 if ( m_nShapeType == ESCHER_ShpInst_Line ) 332 AddLineDimensions( rRect ); 333 else 334 AddRectangleDimensions( *m_pShapeStyle, rRect ); 335 336 // properties 337 bool bAlreadyWritten[ 0xFFF ]; 338 memset( bAlreadyWritten, 0, sizeof( bAlreadyWritten ) ); 339 const EscherProperties &rOpts = rProps.GetOpts(); 340 for ( EscherProperties::const_iterator it = rOpts.begin(); it != rOpts.end(); ++it ) 341 { 342 sal_uInt16 nId = ( it->nPropId & 0x0FFF ); 343 344 if ( bAlreadyWritten[ nId ] ) 345 continue; 346 347 switch ( nId ) 348 { 349 case ESCHER_Prop_WrapText: // 133 350 { 351 const char *pWrapType = NULL; 352 switch ( it->nPropValue ) 353 { 354 case ESCHER_WrapSquare: 355 case ESCHER_WrapByPoints: pWrapType = "square"; break; // these two are equivalent according to the docu 356 case ESCHER_WrapNone: pWrapType = "none"; break; 357 case ESCHER_WrapTopBottom: pWrapType = "topAndBottom"; break; 358 case ESCHER_WrapThrough: pWrapType = "through"; break; 359 } 360 if ( pWrapType ) 361 m_pSerializer->singleElementNS( XML_w10, XML_wrap, 362 FSNS( XML_w10, XML_type ), pWrapType, 363 FSEND ); 364 } 365 bAlreadyWritten[ ESCHER_Prop_WrapText ] = true; 366 break; 367 368 // coordorigin 369 case ESCHER_Prop_geoLeft: // 320 370 case ESCHER_Prop_geoTop: // 321 371 { 372 sal_uInt32 nLeft = 0, nTop = 0; 373 374 if ( nId == ESCHER_Prop_geoLeft ) 375 { 376 nLeft = it->nPropValue; 377 rProps.GetOpt( ESCHER_Prop_geoTop, nTop ); 378 } 379 else 380 { 381 nTop = it->nPropValue; 382 rProps.GetOpt( ESCHER_Prop_geoLeft, nLeft ); 383 } 384 385 m_pShapeAttrList->add( XML_coordorigin, 386 OStringBuffer( 20 ).append( sal_Int32( nLeft ) ) 387 .append( "," ).append( sal_Int32( nTop ) ) 388 .makeStringAndClear() ); 389 } 390 bAlreadyWritten[ ESCHER_Prop_geoLeft ] = true; 391 bAlreadyWritten[ ESCHER_Prop_geoTop ] = true; 392 break; 393 394 // coordsize 395 case ESCHER_Prop_geoRight: // 322 396 case ESCHER_Prop_geoBottom: // 323 397 { 398 sal_uInt32 nLeft = 0, nRight = 0, nTop = 0, nBottom = 0; 399 rProps.GetOpt( ESCHER_Prop_geoLeft, nLeft ); 400 rProps.GetOpt( ESCHER_Prop_geoTop, nTop ); 401 402 if ( nId == ESCHER_Prop_geoRight ) 403 { 404 nRight = it->nPropValue; 405 rProps.GetOpt( ESCHER_Prop_geoBottom, nBottom ); 406 } 407 else 408 { 409 nBottom = it->nPropValue; 410 rProps.GetOpt( ESCHER_Prop_geoRight, nRight ); 411 } 412 413 m_pShapeAttrList->add( XML_coordsize, 414 OStringBuffer( 20 ).append( sal_Int32( nRight ) - sal_Int32( nLeft ) ) 415 .append( "," ).append( sal_Int32( nBottom ) - sal_Int32( nTop ) ) 416 .makeStringAndClear() ); 417 } 418 bAlreadyWritten[ ESCHER_Prop_geoRight ] = true; 419 bAlreadyWritten[ ESCHER_Prop_geoBottom ] = true; 420 break; 421 422 case ESCHER_Prop_pVertices: // 325 423 case ESCHER_Prop_pSegmentInfo: // 326 424 { 425 EscherPropSortStruct aVertices; 426 EscherPropSortStruct aSegments; 427 428 if ( rProps.GetOpt( ESCHER_Prop_pVertices, aVertices ) && 429 rProps.GetOpt( ESCHER_Prop_pSegmentInfo, aSegments ) ) 430 { 431 const sal_uInt8 *pVerticesIt = aVertices.pBuf + 6; 432 const sal_uInt8 *pSegmentIt = aSegments.pBuf; 433 OStringBuffer aPath( 512 ); 434 435 sal_uInt16 nPointSize = aVertices.pBuf[4] + ( aVertices.pBuf[5] << 8 ); 436 437 // number of segments 438 sal_uInt16 nSegments = impl_GetUInt16( pSegmentIt ); 439 pSegmentIt += 4; 440 441 for ( ; nSegments; --nSegments ) 442 { 443 sal_uInt16 nSeg = impl_GetUInt16( pSegmentIt ); 444 switch ( nSeg ) 445 { 446 case 0x4000: // moveto 447 { 448 sal_Int32 nX = impl_GetPointComponent( pVerticesIt, nPointSize ); 449 sal_Int32 nY = impl_GetPointComponent( pVerticesIt, nPointSize ); 450 aPath.append( "m" ).append( nX ).append( "," ).append( nY ); 451 } 452 break; 453 case 0xb300: 454 case 0xac00: 455 break; 456 case 0x0001: // lineto 457 { 458 sal_Int32 nX = impl_GetPointComponent( pVerticesIt, nPointSize ); 459 sal_Int32 nY = impl_GetPointComponent( pVerticesIt, nPointSize ); 460 aPath.append( "l" ).append( nX ).append( "," ).append( nY ); 461 } 462 break; 463 case 0x2001: // curveto 464 { 465 sal_Int32 nX1 = impl_GetPointComponent( pVerticesIt, nPointSize ); 466 sal_Int32 nY1 = impl_GetPointComponent( pVerticesIt, nPointSize ); 467 sal_Int32 nX2 = impl_GetPointComponent( pVerticesIt, nPointSize ); 468 sal_Int32 nY2 = impl_GetPointComponent( pVerticesIt, nPointSize ); 469 sal_Int32 nX3 = impl_GetPointComponent( pVerticesIt, nPointSize ); 470 sal_Int32 nY3 = impl_GetPointComponent( pVerticesIt, nPointSize ); 471 aPath.append( "c" ).append( nX1 ).append( "," ).append( nY1 ).append( "," ) 472 .append( nX2 ).append( "," ).append( nY2 ).append( "," ) 473 .append( nX3 ).append( "," ).append( nY3 ); 474 } 475 break; 476 case 0xaa00: // nofill 477 aPath.append( "nf" ); 478 break; 479 case 0xab00: // nostroke 480 aPath.append( "ns" ); 481 break; 482 case 0x6001: // close 483 aPath.append( "x" ); 484 break; 485 case 0x8000: // end 486 aPath.append( "e" ); 487 break; 488 default: 489 #if OSL_DEBUG_LEVEL > 0 490 fprintf( stderr, "TODO: unhandled segment '%x' in the path\n", nSeg ); 491 #endif 492 break; 493 } 494 } 495 496 if ( aPath.getLength() ) 497 m_pShapeAttrList->add( XML_path, aPath.getStr() ); 498 } 499 #if OSL_DEBUG_LEVEL > 0 500 else 501 fprintf( stderr, "TODO: unhandled shape path, missing either pVertices or pSegmentInfo.\n" ); 502 #endif 503 } 504 bAlreadyWritten[ ESCHER_Prop_pVertices ] = true; 505 bAlreadyWritten[ ESCHER_Prop_pSegmentInfo ] = true; 506 break; 507 508 case ESCHER_Prop_fillType: // 384 509 case ESCHER_Prop_fillColor: // 385 510 case ESCHER_Prop_fillBackColor: // 387 511 case ESCHER_Prop_fNoFillHitTest: // 447 512 { 513 sal_uInt32 nValue; 514 sax_fastparser::FastAttributeList *pAttrList = m_pSerializer->createAttrList(); 515 516 if ( rProps.GetOpt( ESCHER_Prop_fillType, nValue ) ) 517 { 518 const char *pFillType = NULL; 519 switch ( nValue ) 520 { 521 case ESCHER_FillSolid: pFillType = "solid"; break; 522 // TODO case ESCHER_FillPattern: pFillType = ""; break; 523 // TODO case ESCHER_FillTexture: pFillType = ""; break; 524 // TODO case ESCHER_FillPicture: pFillType = ""; break; 525 // TODO case ESCHER_FillShade: pFillType = ""; break; 526 // TODO case ESCHER_FillShadeCenter: pFillType = ""; break; 527 // TODO case ESCHER_FillShadeShape: pFillType = ""; break; 528 // TODO case ESCHER_FillShadeScale: pFillType = ""; break; 529 // TODO case ESCHER_FillShadeTitle: pFillType = ""; break; 530 // TODO case ESCHER_FillBackground: pFillType = ""; break; 531 default: 532 #if OSL_DEBUG_LEVEL > 0 533 fprintf( stderr, "TODO: unhandled fill type\n" ); 534 #endif 535 break; 536 } 537 if ( pFillType ) 538 pAttrList->add( XML_type, pFillType ); 539 } 540 541 if ( rProps.GetOpt( ESCHER_Prop_fillColor, nValue ) ) 542 impl_AddColor( pAttrList, XML_color, nValue ); 543 544 if ( rProps.GetOpt( ESCHER_Prop_fillBackColor, nValue ) ) 545 impl_AddColor( pAttrList, XML_color2, nValue ); 546 547 if ( rProps.GetOpt( ESCHER_Prop_fNoFillHitTest, nValue ) ) 548 impl_AddBool( pAttrList, XML_detectmouseclick, nValue ); 549 550 m_pSerializer->singleElementNS( XML_v, XML_fill, XFastAttributeListRef( pAttrList ) ); 551 } 552 bAlreadyWritten[ ESCHER_Prop_fillType ] = true; 553 bAlreadyWritten[ ESCHER_Prop_fillColor ] = true; 554 bAlreadyWritten[ ESCHER_Prop_fillBackColor ] = true; 555 bAlreadyWritten[ ESCHER_Prop_fNoFillHitTest ] = true; 556 break; 557 558 case ESCHER_Prop_lineColor: // 448 559 case ESCHER_Prop_lineWidth: // 459 560 case ESCHER_Prop_lineDashing: // 462 561 case ESCHER_Prop_lineStartArrowhead: // 464 562 case ESCHER_Prop_lineEndArrowhead: // 465 563 case ESCHER_Prop_lineStartArrowWidth: // 466 564 case ESCHER_Prop_lineStartArrowLength: // 467 565 case ESCHER_Prop_lineEndArrowWidth: // 468 566 case ESCHER_Prop_lineEndArrowLength: // 469 567 case ESCHER_Prop_lineJoinStyle: // 470 568 case ESCHER_Prop_lineEndCapStyle: // 471 569 { 570 sal_uInt32 nValue; 571 sax_fastparser::FastAttributeList *pAttrList = m_pSerializer->createAttrList(); 572 573 if ( rProps.GetOpt( ESCHER_Prop_lineColor, nValue ) ) 574 impl_AddColor( pAttrList, XML_color, nValue ); 575 576 if ( rProps.GetOpt( ESCHER_Prop_lineWidth, nValue ) ) 577 impl_AddInt( pAttrList, XML_weight, nValue ); 578 579 if ( rProps.GetOpt( ESCHER_Prop_lineDashing, nValue ) ) 580 { 581 const char *pDashStyle = NULL; 582 switch ( nValue ) 583 { 584 case ESCHER_LineSolid: pDashStyle = "solid"; break; 585 case ESCHER_LineDashSys: pDashStyle = "shortdash"; break; 586 case ESCHER_LineDotSys: pDashStyle = "shortdot"; break; 587 case ESCHER_LineDashDotSys: pDashStyle = "shortdashdot"; break; 588 case ESCHER_LineDashDotDotSys: pDashStyle = "shortdashdotdot"; break; 589 case ESCHER_LineDotGEL: pDashStyle = "dot"; break; 590 case ESCHER_LineDashGEL: pDashStyle = "dash"; break; 591 case ESCHER_LineLongDashGEL: pDashStyle = "longdash"; break; 592 case ESCHER_LineDashDotGEL: pDashStyle = "dashdot"; break; 593 case ESCHER_LineLongDashDotGEL: pDashStyle = "longdashdot"; break; 594 case ESCHER_LineLongDashDotDotGEL: pDashStyle = "longdashdotdot"; break; 595 } 596 if ( pDashStyle ) 597 pAttrList->add( XML_dashstyle, pDashStyle ); 598 } 599 600 if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowhead, nValue ) ) 601 impl_AddArrowHead( pAttrList, XML_startarrow, nValue ); 602 603 if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowhead, nValue ) ) 604 impl_AddArrowHead( pAttrList, XML_endarrow, nValue ); 605 606 if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowWidth, nValue ) ) 607 impl_AddArrowWidth( pAttrList, XML_startarrowwidth, nValue ); 608 609 if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowLength, nValue ) ) 610 impl_AddArrowLength( pAttrList, XML_startarrowlength, nValue ); 611 612 if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowWidth, nValue ) ) 613 impl_AddArrowWidth( pAttrList, XML_endarrowwidth, nValue ); 614 615 if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowLength, nValue ) ) 616 impl_AddArrowLength( pAttrList, XML_endarrowlength, nValue ); 617 618 if ( rProps.GetOpt( ESCHER_Prop_lineJoinStyle, nValue ) ) 619 { 620 const char *pJoinStyle = NULL; 621 switch ( nValue ) 622 { 623 case ESCHER_LineJoinBevel: pJoinStyle = "bevel"; break; 624 case ESCHER_LineJoinMiter: pJoinStyle = "miter"; break; 625 case ESCHER_LineJoinRound: pJoinStyle = "round"; break; 626 } 627 if ( pJoinStyle ) 628 pAttrList->add( XML_joinstyle, pJoinStyle ); 629 } 630 631 if ( rProps.GetOpt( ESCHER_Prop_lineEndCapStyle, nValue ) ) 632 { 633 const char *pEndCap = NULL; 634 switch ( nValue ) 635 { 636 case ESCHER_LineEndCapRound: pEndCap = "round"; break; 637 case ESCHER_LineEndCapSquare: pEndCap = "square"; break; 638 case ESCHER_LineEndCapFlat: pEndCap = "flat"; break; 639 } 640 if ( pEndCap ) 641 pAttrList->add( XML_endcap, pEndCap ); 642 } 643 644 m_pSerializer->singleElementNS( XML_v, XML_stroke, XFastAttributeListRef( pAttrList ) ); 645 } 646 bAlreadyWritten[ ESCHER_Prop_lineColor ] = true; 647 bAlreadyWritten[ ESCHER_Prop_lineWidth ] = true; 648 bAlreadyWritten[ ESCHER_Prop_lineDashing ] = true; 649 bAlreadyWritten[ ESCHER_Prop_lineStartArrowhead ] = true; 650 bAlreadyWritten[ ESCHER_Prop_lineEndArrowhead ] = true; 651 bAlreadyWritten[ ESCHER_Prop_lineStartArrowWidth ] = true; 652 bAlreadyWritten[ ESCHER_Prop_lineStartArrowLength ] = true; 653 bAlreadyWritten[ ESCHER_Prop_lineEndArrowWidth ] = true; 654 bAlreadyWritten[ ESCHER_Prop_lineEndArrowLength ] = true; 655 bAlreadyWritten[ ESCHER_Prop_lineJoinStyle ] = true; 656 bAlreadyWritten[ ESCHER_Prop_lineEndCapStyle ] = true; 657 break; 658 659 case ESCHER_Prop_fHidden: 660 m_pShapeStyle->append( ";visibility:hidden" ); 661 break; 662 default: 663 #if OSL_DEBUG_LEVEL > 0 664 fprintf( stderr, "TODO VMLExport::Commit(), unimplemented id: %d, value: %d, data: [%d, %p]\n", 665 it->nPropId, it->nPropValue, it->nPropSize, it->pBuf ); 666 if ( it->nPropSize ) 667 { 668 const sal_uInt8 *pIt = it->pBuf; 669 fprintf( stderr, " ( " ); 670 for ( int nCount = it->nPropSize; nCount; --nCount ) 671 { 672 fprintf( stderr, "%02x ", *pIt ); 673 ++pIt; 674 } 675 fprintf( stderr, ")\n" ); 676 } 677 #endif 678 break; 679 } 680 } 681 682 m_pSerializer->mergeTopMarks( sax_fastparser::MERGE_MARKS_POSTPONE ); 683 } 684 685 OString VMLExport::ShapeIdString( sal_uInt32 nId ) 686 { 687 return OStringBuffer( 20 ).append( "shape_" ).append( sal_Int64( nId ) ).makeStringAndClear(); 688 } 689 690 void VMLExport::AddLineDimensions( const Rectangle& rRectangle ) 691 { 692 // style 693 if ( m_pShapeStyle->getLength() ) 694 m_pShapeStyle->append( ";" ); 695 696 m_pShapeStyle->append( "position:absolute" ); 697 698 switch ( m_nShapeFlags & 0xC0 ) 699 { 700 case 0x40: m_pShapeStyle->append( ";flip:y" ); break; 701 case 0x80: m_pShapeStyle->append( ";flip:x" ); break; 702 case 0xC0: m_pShapeStyle->append( ";flip:xy" ); break; 703 } 704 705 // the actual dimensions 706 OString aLeft, aTop, aRight, aBottom; 707 708 if ( mnGroupLevel == 1 ) 709 { 710 const OString aPt( "pt" ); 711 aLeft = OString::valueOf( double( rRectangle.Left() ) / 20 ) + aPt; 712 aTop = OString::valueOf( double( rRectangle.Top() ) / 20 ) + aPt; 713 aRight = OString::valueOf( double( rRectangle.Right() ) / 20 ) + aPt; 714 aBottom = OString::valueOf( double( rRectangle.Bottom() ) / 20 ) + aPt; 715 } 716 else 717 { 718 aLeft = OString::valueOf( rRectangle.Left() ); 719 aTop = OString::valueOf( rRectangle.Top() ); 720 aRight = OString::valueOf( rRectangle.Right() ); 721 aBottom = OString::valueOf( rRectangle.Bottom() ); 722 } 723 724 m_pShapeAttrList->add( XML_from, 725 OStringBuffer( 20 ).append( aLeft ) 726 .append( "," ).append( aTop ) 727 .makeStringAndClear() ); 728 729 m_pShapeAttrList->add( XML_to, 730 OStringBuffer( 20 ).append( aRight ) 731 .append( "," ).append( aBottom ) 732 .makeStringAndClear() ); 733 } 734 735 void VMLExport::AddRectangleDimensions( rtl::OStringBuffer& rBuffer, const Rectangle& rRectangle ) 736 { 737 if ( rBuffer.getLength() ) 738 rBuffer.append( ";" ); 739 740 rBuffer.append( "position:absolute;" ); 741 742 if ( mnGroupLevel == 1 ) 743 { 744 rBuffer.append( "margin-left:" ).append( double( rRectangle.Left() ) / 20 ) 745 .append( "pt;margin-top:" ).append( double( rRectangle.Top() ) / 20 ) 746 .append( "pt;width:" ).append( double( rRectangle.Right() - rRectangle.Left() ) / 20 ) 747 .append( "pt;height:" ).append( double( rRectangle.Bottom() - rRectangle.Top() ) / 20 ) 748 .append( "pt" ); 749 } 750 else 751 { 752 rBuffer.append( "left:" ).append( rRectangle.Left() ) 753 .append( ";top:" ).append( rRectangle.Top() ) 754 .append( ";width:" ).append( rRectangle.Right() - rRectangle.Left() ) 755 .append( ";height:" ).append( rRectangle.Bottom() - rRectangle.Top() ); 756 } 757 } 758 759 void VMLExport::AddShapeAttribute( sal_Int32 nAttribute, const rtl::OString& rValue ) 760 { 761 m_pShapeAttrList->add( nAttribute, rValue ); 762 } 763 764 extern const char* pShapeTypes[]; 765 766 sal_Int32 VMLExport::StartShape() 767 { 768 if ( m_nShapeType == ESCHER_ShpInst_Nil ) 769 return -1; 770 771 // some of the shapes have their own name ;-) 772 sal_Int32 nShapeElement = -1; 773 bool bReferToShapeType = false; 774 switch ( m_nShapeType ) 775 { 776 case ESCHER_ShpInst_NotPrimitive: nShapeElement = XML_shape; break; 777 case ESCHER_ShpInst_Rectangle: nShapeElement = XML_rect; break; 778 case ESCHER_ShpInst_RoundRectangle: nShapeElement = XML_roundrect; break; 779 case ESCHER_ShpInst_Ellipse: nShapeElement = XML_oval; break; 780 case ESCHER_ShpInst_Arc: nShapeElement = XML_arc; break; 781 case ESCHER_ShpInst_Line: nShapeElement = XML_line; break; 782 default: 783 if ( m_nShapeType < ESCHER_ShpInst_COUNT ) 784 { 785 nShapeElement = XML_shape; 786 787 // a predefined shape? 788 const char* pShapeType = pShapeTypes[ m_nShapeType ]; 789 if ( pShapeType ) 790 { 791 bReferToShapeType = true; 792 if ( !m_pShapeTypeWritten[ m_nShapeType ] ) 793 { 794 m_pSerializer->write( pShapeType ); 795 m_pShapeTypeWritten[ m_nShapeType ] = true; 796 } 797 } 798 else 799 { 800 // rectangle is probably the best fallback... 801 nShapeElement = XML_rect; 802 } 803 } 804 break; 805 } 806 807 // add style 808 m_pShapeAttrList->add( XML_style, m_pShapeStyle->makeStringAndClear() ); 809 810 if ( nShapeElement >= 0 ) 811 { 812 if ( bReferToShapeType ) 813 { 814 m_pShapeAttrList->add( XML_type, OStringBuffer( 20 ) 815 .append( "shapetype_" ).append( sal_Int32( m_nShapeType ) ) 816 .makeStringAndClear() ); 817 } 818 819 // start of the shape 820 m_pSerializer->startElementNS( XML_v, nShapeElement, XFastAttributeListRef( m_pShapeAttrList ) ); 821 } 822 823 return nShapeElement; 824 } 825 826 void VMLExport::EndShape( sal_Int32 nShapeElement ) 827 { 828 if ( nShapeElement >= 0 ) 829 { 830 // end of the shape 831 m_pSerializer->endElementNS( XML_v, nShapeElement ); 832 } 833 } 834