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