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:
GetData(void * pData,sal_Size nSize)51 virtual sal_Size GetData( void* pData, sal_Size nSize ) { memset( pData, 0, nSize ); return nSize; }
PutData(const void *,sal_Size nSize)52 virtual sal_Size PutData( const void*, sal_Size nSize ) { return nSize; }
SeekPos(sal_Size nPos)53 virtual sal_Size SeekPos( sal_Size nPos ) { return nPos; }
SetSize(sal_Size)54 virtual void SetSize( sal_Size ) {}
FlushData()55 virtual void FlushData() {}
56
57 public:
SvNullStream()58 SvNullStream() : SvStream() {}
~SvNullStream()59 virtual ~SvNullStream() {}
60 };
61
VMLExport(::sax_fastparser::FSHelperPtr pSerializer)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
~VMLExport()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
OpenContainer(UINT16 nEscherContainer,int nRecInstance)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
CloseContainer()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
EnterGroup(const String & rShapeName,const Rectangle * pRect)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
LeaveGroup()164 void VMLExport::LeaveGroup()
165 {
166 --mnGroupLevel;
167 m_pSerializer->endElementNS( XML_v, XML_group );
168 }
169
AddShape(UINT32 nShapeType,UINT32 nShapeFlags,UINT32 nShapeId)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
impl_AddArrowHead(sax_fastparser::FastAttributeList * pAttrList,sal_Int32 nElement,sal_uInt32 nValue)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
impl_AddArrowLength(sax_fastparser::FastAttributeList * pAttrList,sal_Int32 nElement,sal_uInt32 nValue)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
impl_AddArrowWidth(sax_fastparser::FastAttributeList * pAttrList,sal_Int32 nElement,sal_uInt32 nValue)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
impl_AddBool(sax_fastparser::FastAttributeList * pAttrList,sal_Int32 nElement,bool bValue)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
impl_AddColor(sax_fastparser::FastAttributeList * pAttrList,sal_Int32 nElement,sal_uInt32 nColor)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
impl_AddInt(sax_fastparser::FastAttributeList * pAttrList,sal_Int32 nElement,sal_uInt32 nValue)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
impl_GetUInt16(const sal_uInt8 * & pVal)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
impl_GetPointComponent(const sal_uInt8 * & pVal,sal_uInt16 nPointSize)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
Commit(EscherPropertyContainer & rProps,const Rectangle & rRect)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
ShapeIdString(sal_uInt32 nId)685 OString VMLExport::ShapeIdString( sal_uInt32 nId )
686 {
687 return OStringBuffer( 20 ).append( "shape_" ).append( sal_Int64( nId ) ).makeStringAndClear();
688 }
689
AddLineDimensions(const Rectangle & rRectangle)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
AddRectangleDimensions(rtl::OStringBuffer & rBuffer,const Rectangle & rRectangle)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
AddShapeAttribute(sal_Int32 nAttribute,const rtl::OString & rValue)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
StartShape()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
EndShape(sal_Int32 nShapeElement)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