Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / oox / source / export / vmlexport.cxx
blob5196edbe74ecb872f496f310908c91bf446a1833
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_folders.h>
21 #include "rtl/bootstrap.hxx"
22 #include <oox/export/drawingml.hxx>
23 #include <oox/export/vmlexport.hxx>
25 #include <oox/token/tokens.hxx>
27 #include <rtl/strbuf.hxx>
28 #include <rtl/ustring.hxx>
30 #include <tools/stream.hxx>
31 #include <comphelper/sequenceashashmap.hxx>
32 #include <svx/svdotext.hxx>
33 #include <vcl/cvtgrf.hxx>
34 #include <filter/msfilter/msdffimp.hxx>
35 #include <filter/msfilter/util.hxx>
36 #include <filter/msfilter/escherex.hxx>
38 #include <com/sun/star/beans/XPropertySet.hpp>
39 #include <com/sun/star/beans/XPropertySetInfo.hpp>
40 #include <com/sun/star/drawing/XShape.hpp>
41 #include <com/sun/star/text/HoriOrientation.hpp>
42 #include <com/sun/star/text/VertOrientation.hpp>
43 #include <com/sun/star/text/RelOrientation.hpp>
45 #include <cstdio>
47 using namespace sax_fastparser;
48 using namespace oox::vml;
49 using namespace com::sun::star;
51 static const sal_Int32 Tag_Container = 44444;
52 static const sal_Int32 Tag_Commit = 44445;
54 VMLExport::VMLExport( ::sax_fastparser::FSHelperPtr const & pSerializer, VMLTextExport* pTextExport )
55 : EscherEx( std::make_shared<EscherExGlobal>(), nullptr, /*bOOXML=*/true )
56 , m_pSerializer( pSerializer )
57 , m_pTextExport( pTextExport )
58 , m_eHOri( 0 )
59 , m_eVOri( 0 )
60 , m_eHRel( 0 )
61 , m_eVRel( 0 )
62 , m_pNdTopLeft( nullptr )
63 , m_pSdrObject( nullptr )
64 , m_pShapeAttrList( nullptr )
65 , m_nShapeType( ESCHER_ShpInst_Nil )
66 , m_nShapeFlags(0)
67 , m_ShapeStyle( 200 )
68 , m_aShapeTypeWritten( ESCHER_ShpInst_COUNT )
70 mnGroupLevel = 1;
73 void VMLExport::SetFS( const ::sax_fastparser::FSHelperPtr& pSerializer )
75 m_pSerializer = pSerializer;
78 VMLExport::~VMLExport()
80 delete mpOutStrm;
81 mpOutStrm = nullptr;
84 void VMLExport::OpenContainer( sal_uInt16 nEscherContainer, int nRecInstance )
86 EscherEx::OpenContainer( nEscherContainer, nRecInstance );
88 if ( nEscherContainer == ESCHER_SpContainer )
90 // opening a shape container
91 #if OSL_DEBUG_LEVEL > 0
92 if ( m_nShapeType != ESCHER_ShpInst_Nil )
93 fprintf( stderr, "Warning! VMLExport::OpenContainer(): opening shape inside a shape.\n" );
94 #endif
95 m_nShapeType = ESCHER_ShpInst_Nil;
96 m_pShapeAttrList = FastSerializerHelper::createAttrList();
98 m_ShapeStyle.setLength(0);
99 m_ShapeStyle.ensureCapacity(200);
101 // postpone the output so that we are able to write even the elements
102 // that we learn inside Commit()
103 m_pSerializer->mark(Tag_Container);
107 void VMLExport::CloseContainer()
109 if ( mRecTypes.back() == ESCHER_SpContainer )
111 // write the shape now when we have all the info
112 sal_Int32 nShapeElement = StartShape();
114 m_pSerializer->mergeTopMarks(Tag_Container);
116 EndShape( nShapeElement );
118 // cleanup
119 m_nShapeType = ESCHER_ShpInst_Nil;
120 m_pShapeAttrList = nullptr;
123 EscherEx::CloseContainer();
126 sal_uInt32 VMLExport::EnterGroup( const OUString& rShapeName, const tools::Rectangle* pRect )
128 sal_uInt32 nShapeId = GenerateShapeId();
130 OStringBuffer aStyle( 200 );
131 FastAttributeList *pAttrList = FastSerializerHelper::createAttrList();
133 pAttrList->add( XML_id, ShapeIdString( nShapeId ) );
135 if ( rShapeName.getLength() )
136 pAttrList->add( XML_alt, OUStringToOString( rShapeName, RTL_TEXTENCODING_UTF8 ) );
138 bool rbAbsolutePos = true;
139 //editAs
140 OUString rEditAs = EscherEx::GetEditAs();
141 if (!rEditAs.isEmpty())
143 pAttrList->add(XML_editas, OUStringToOString( rEditAs, RTL_TEXTENCODING_UTF8 ));
144 rbAbsolutePos = false;
147 // style
148 if ( pRect )
149 AddRectangleDimensions( aStyle, *pRect, rbAbsolutePos );
151 if ( !aStyle.isEmpty() )
152 pAttrList->add( XML_style, aStyle.makeStringAndClear() );
154 // coordorigin/coordsize
155 if ( pRect && ( mnGroupLevel == 1 ) )
157 pAttrList->add( XML_coordorigin,
158 OStringBuffer( 20 ).append( sal_Int32( pRect->Left() ) )
159 .append( "," ).append( sal_Int32( pRect->Top() ) )
160 .makeStringAndClear() );
162 pAttrList->add( XML_coordsize,
163 OStringBuffer( 20 ).append( sal_Int32( pRect->Right() ) - sal_Int32( pRect->Left() ) )
164 .append( "," ).append( sal_Int32( pRect->Bottom() ) - sal_Int32( pRect->Top() ) )
165 .makeStringAndClear() );
168 m_pSerializer->startElementNS( XML_v, XML_group, XFastAttributeListRef( pAttrList ) );
170 mnGroupLevel++;
171 return nShapeId;
174 void VMLExport::LeaveGroup()
176 --mnGroupLevel;
177 m_pSerializer->endElementNS( XML_v, XML_group );
180 void VMLExport::AddShape( sal_uInt32 nShapeType, sal_uInt32 nShapeFlags, sal_uInt32 nShapeId )
182 m_nShapeType = nShapeType;
183 m_nShapeFlags = nShapeFlags;
184 // If shape is a watermark object - should keep the original shape's name
185 // because Microsoft detects if it is a watermark by the actual name
186 if (!IsWaterMarkShape(m_pSdrObject->GetName()))
188 // Not a watermark object
189 m_pShapeAttrList->add( XML_id, ShapeIdString( nShapeId ) );
191 else
193 // A watermark object - store the optional shape ID
194 m_pShapeAttrList->add( XML_id, OUStringToOString(m_pSdrObject->GetName(), RTL_TEXTENCODING_UTF8) );
195 // also ('o:spid')
196 m_pShapeAttrList->addNS( XML_o, XML_spid, ShapeIdString( nShapeId ) );
200 bool VMLExport::IsWaterMarkShape(const OUString& rStr)
202 if (rStr.isEmpty() ) return false;
204 return rStr.match("PowerPlusWaterMarkObject") || rStr.match("WordPictureWatermark");
207 static void impl_AddArrowHead( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue )
209 if ( !pAttrList )
210 return;
212 const char *pArrowHead = nullptr;
213 switch ( nValue )
215 case ESCHER_LineNoEnd: pArrowHead = "none"; break;
216 case ESCHER_LineArrowEnd: pArrowHead = "block"; break;
217 case ESCHER_LineArrowStealthEnd: pArrowHead = "classic"; break;
218 case ESCHER_LineArrowDiamondEnd: pArrowHead = "diamond"; break;
219 case ESCHER_LineArrowOvalEnd: pArrowHead = "oval"; break;
220 case ESCHER_LineArrowOpenEnd: pArrowHead = "open"; break;
223 if ( pArrowHead )
224 pAttrList->add( nElement, pArrowHead );
227 static void impl_AddArrowLength( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue )
229 if ( !pAttrList )
230 return;
232 const char *pArrowLength = nullptr;
233 switch ( nValue )
235 case ESCHER_LineShortArrow: pArrowLength = "short"; break;
236 case ESCHER_LineMediumLenArrow: pArrowLength = "medium"; break;
237 case ESCHER_LineLongArrow: pArrowLength = "long"; break;
240 if ( pArrowLength )
241 pAttrList->add( nElement, pArrowLength );
244 static void impl_AddArrowWidth( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue )
246 if ( !pAttrList )
247 return;
249 const char *pArrowWidth = nullptr;
250 switch ( nValue )
252 case ESCHER_LineNarrowArrow: pArrowWidth = "narrow"; break;
253 case ESCHER_LineMediumWidthArrow: pArrowWidth = "medium"; break;
254 case ESCHER_LineWideArrow: pArrowWidth = "wide"; break;
257 if ( pArrowWidth )
258 pAttrList->add( nElement, pArrowWidth );
261 static void impl_AddBool( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, bool bValue )
263 if ( !pAttrList )
264 return;
266 pAttrList->add( nElement, bValue? "t": "f" );
269 static void impl_AddColor( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nColor )
271 #if OSL_DEBUG_LEVEL > 0
272 if ( nColor & 0xFF000000 )
273 fprintf( stderr, "TODO: this is not a RGB value!\n" );
274 #endif
276 if ( !pAttrList || ( nColor & 0xFF000000 ) )
277 return;
279 nColor = ( ( nColor & 0xFF ) << 16 ) + ( nColor & 0xFF00 ) + ( ( nColor & 0xFF0000 ) >> 16 );
281 const char *pColor = nullptr;
282 char pRgbColor[10];
283 switch ( nColor )
285 case 0x000000: pColor = "black"; break;
286 case 0xC0C0C0: pColor = "silver"; break;
287 case 0x808080: pColor = "gray"; break;
288 case 0xFFFFFF: pColor = "white"; break;
289 case 0x800000: pColor = "maroon"; break;
290 case 0xFF0000: pColor = "red"; break;
291 case 0x800080: pColor = "purple"; break;
292 case 0xFF00FF: pColor = "fuchsia"; break;
293 case 0x008000: pColor = "green"; break;
294 case 0x00FF00: pColor = "lime"; break;
295 case 0x808000: pColor = "olive"; break;
296 case 0xFFFF00: pColor = "yellow"; break;
297 case 0x000080: pColor = "navy"; break;
298 case 0x0000FF: pColor = "blue"; break;
299 case 0x008080: pColor = "teal"; break;
300 case 0x00FFFF: pColor = "aqua"; break;
301 default:
303 snprintf( pRgbColor, sizeof( pRgbColor ), "#%06x", static_cast< unsigned int >( nColor ) ); // not too handy to use OString::valueOf() here :-(
304 pColor = pRgbColor;
306 break;
309 pAttrList->add( nElement, pColor );
312 static void impl_AddInt( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue )
314 if ( !pAttrList )
315 return;
317 pAttrList->add( nElement, OString::number( nValue ).getStr() );
320 inline sal_uInt16 impl_GetUInt16( const sal_uInt8* &pVal )
322 sal_uInt16 nRet = *pVal++;
323 nRet += ( *pVal++ ) << 8;
324 return nRet;
327 inline sal_Int32 impl_GetPointComponent( const sal_uInt8* &pVal, sal_uInt16 nPointSize )
329 sal_Int32 nRet = 0;
330 if ( ( nPointSize == 0xfff0 ) || ( nPointSize == 4 ) )
332 sal_uInt16 nUnsigned = *pVal++;
333 nUnsigned += ( *pVal++ ) << 8;
335 nRet = sal_Int16( nUnsigned );
337 else if ( nPointSize == 8 )
339 sal_uInt32 nUnsigned = *pVal++;
340 nUnsigned += ( *pVal++ ) << 8;
341 nUnsigned += ( *pVal++ ) << 16;
342 nUnsigned += ( *pVal++ ) << 24;
344 nRet = nUnsigned;
347 return nRet;
350 void VMLExport::AddSdrObjectVMLObject( const SdrObject& rObj)
352 m_pSdrObject = &rObj;
354 void VMLExport::Commit( EscherPropertyContainer& rProps, const tools::Rectangle& rRect )
356 if ( m_nShapeType == ESCHER_ShpInst_Nil )
357 return;
359 // postpone the output of the embedded elements so that they are written
360 // inside the shapes
361 m_pSerializer->mark(Tag_Commit);
363 // dimensions
364 if ( m_nShapeType == ESCHER_ShpInst_Line )
365 AddLineDimensions( rRect );
366 else
367 AddRectangleDimensions( m_ShapeStyle, rRect );
369 // properties
370 bool bAlreadyWritten[ 0xFFF ];
371 memset( bAlreadyWritten, 0, sizeof( bAlreadyWritten ) );
372 const EscherProperties &rOpts = rProps.GetOpts();
373 for ( EscherProperties::const_iterator it = rOpts.begin(); it != rOpts.end(); ++it )
375 sal_uInt16 nId = ( it->nPropId & 0x0FFF );
377 if ( bAlreadyWritten[ nId ] )
378 continue;
380 switch ( nId )
382 case ESCHER_Prop_WrapText: // 133
384 const char *pWrapType = nullptr;
385 switch ( it->nPropValue )
387 case ESCHER_WrapSquare:
388 case ESCHER_WrapByPoints: pWrapType = "square"; break; // these two are equivalent according to the docu
389 case ESCHER_WrapNone: pWrapType = "none"; break;
390 case ESCHER_WrapTopBottom: pWrapType = "topAndBottom"; break;
391 case ESCHER_WrapThrough: pWrapType = "through"; break;
393 if ( pWrapType )
394 m_pSerializer->singleElementNS( XML_w10, XML_wrap,
395 XML_type, pWrapType,
396 FSEND );
398 bAlreadyWritten[ ESCHER_Prop_WrapText ] = true;
399 break;
401 // coordorigin
402 case ESCHER_Prop_geoLeft: // 320
403 case ESCHER_Prop_geoTop: // 321
405 sal_uInt32 nLeft = 0, nTop = 0;
407 if ( nId == ESCHER_Prop_geoLeft )
409 nLeft = it->nPropValue;
410 rProps.GetOpt( ESCHER_Prop_geoTop, nTop );
412 else
414 nTop = it->nPropValue;
415 rProps.GetOpt( ESCHER_Prop_geoLeft, nLeft );
417 if(nTop!=0 && nLeft!=0)
418 m_pShapeAttrList->add( XML_coordorigin,
419 OStringBuffer( 20 ).append( sal_Int32( nLeft ) )
420 .append( "," ).append( sal_Int32( nTop ) )
421 .makeStringAndClear() );
423 bAlreadyWritten[ ESCHER_Prop_geoLeft ] = true;
424 bAlreadyWritten[ ESCHER_Prop_geoTop ] = true;
425 break;
427 // coordsize
428 case ESCHER_Prop_geoRight: // 322
429 case ESCHER_Prop_geoBottom: // 323
431 sal_uInt32 nLeft = 0, nRight = 0, nTop = 0, nBottom = 0;
432 rProps.GetOpt( ESCHER_Prop_geoLeft, nLeft );
433 rProps.GetOpt( ESCHER_Prop_geoTop, nTop );
435 if ( nId == ESCHER_Prop_geoRight )
437 nRight = it->nPropValue;
438 rProps.GetOpt( ESCHER_Prop_geoBottom, nBottom );
440 else
442 nBottom = it->nPropValue;
443 rProps.GetOpt( ESCHER_Prop_geoRight, nRight );
446 if(nTop!=0 && nLeft!=0 && nBottom!=0 && nRight!=0 )
447 m_pShapeAttrList->add( XML_coordsize,
448 OStringBuffer( 20 ).append( sal_Int32( nRight ) - sal_Int32( nLeft ) )
449 .append( "," ).append( sal_Int32( nBottom ) - sal_Int32( nTop ) )
450 .makeStringAndClear() );
452 bAlreadyWritten[ ESCHER_Prop_geoRight ] = true;
453 bAlreadyWritten[ ESCHER_Prop_geoBottom ] = true;
454 break;
456 case ESCHER_Prop_pVertices: // 325
457 case ESCHER_Prop_pSegmentInfo: // 326
459 EscherPropSortStruct aVertices;
460 EscherPropSortStruct aSegments;
462 if ( rProps.GetOpt( ESCHER_Prop_pVertices, aVertices ) &&
463 rProps.GetOpt( ESCHER_Prop_pSegmentInfo, aSegments ) )
465 const sal_uInt8 *pVerticesIt = aVertices.pBuf + 6;
466 const sal_uInt8 *pSegmentIt = aSegments.pBuf;
467 OStringBuffer aPath( 512 );
469 sal_uInt16 nPointSize = aVertices.pBuf[4] + ( aVertices.pBuf[5] << 8 );
471 // number of segments
472 sal_uInt16 nSegments = impl_GetUInt16( pSegmentIt );
473 pSegmentIt += 4;
475 for ( ; nSegments; --nSegments )
477 sal_uInt16 nSeg = impl_GetUInt16( pSegmentIt );
479 // The segment type is stored in the upper 3 bits
480 // and segment count is stored in the lower 13
481 // bits.
482 unsigned char nSegmentType = (nSeg & 0xE000) >> 13;
483 unsigned short nSegmentCount = nSeg & 0x03FF;
485 switch (nSegmentType)
487 case msopathMoveTo:
489 sal_Int32 nX = impl_GetPointComponent( pVerticesIt, nPointSize );
490 sal_Int32 nY = impl_GetPointComponent( pVerticesIt, nPointSize );
491 if (nX >= 0 && nY >= 0 )
492 aPath.append( "m" ).append( nX ).append( "," ).append( nY );
493 break;
495 case msopathClientEscape:
496 break;
497 case msopathEscape:
499 // If the segment type is msopathEscape, the lower 13 bits are
500 // divided in a 5 bit escape code and 8 bit
501 // vertex count (not segment count!)
502 unsigned char nEscapeCode = (nSegmentCount & 0x1F00) >> 8;
503 unsigned char nVertexCount = nSegmentCount & 0x00FF;
504 pVerticesIt += nVertexCount;
506 switch (nEscapeCode)
508 case 0xa: // nofill
509 aPath.append( "nf" );
510 break;
511 case 0xb: // nostroke
512 aPath.append( "ns" );
513 break;
516 break;
518 case msopathLineTo:
519 for (unsigned short i = 0; i < nSegmentCount; ++i)
521 sal_Int32 nX = impl_GetPointComponent( pVerticesIt, nPointSize );
522 sal_Int32 nY = impl_GetPointComponent( pVerticesIt, nPointSize );
523 aPath.append( "l" ).append( nX ).append( "," ).append( nY );
525 break;
526 case msopathCurveTo:
527 for (unsigned short i = 0; i < nSegmentCount; ++i)
529 sal_Int32 nX1 = impl_GetPointComponent( pVerticesIt, nPointSize );
530 sal_Int32 nY1 = impl_GetPointComponent( pVerticesIt, nPointSize );
531 sal_Int32 nX2 = impl_GetPointComponent( pVerticesIt, nPointSize );
532 sal_Int32 nY2 = impl_GetPointComponent( pVerticesIt, nPointSize );
533 sal_Int32 nX3 = impl_GetPointComponent( pVerticesIt, nPointSize );
534 sal_Int32 nY3 = impl_GetPointComponent( pVerticesIt, nPointSize );
535 aPath.append( "c" ).append( nX1 ).append( "," ).append( nY1 ).append( "," )
536 .append( nX2 ).append( "," ).append( nY2 ).append( "," )
537 .append( nX3 ).append( "," ).append( nY3 );
539 break;
540 case msopathClose:
541 aPath.append( "x" );
542 break;
543 case msopathEnd:
544 aPath.append( "e" );
545 break;
546 default:
547 SAL_WARN("oox", "Totally b0rked");
548 break;
549 case msopathInvalid:
550 SAL_WARN("oox", "Invalid - should never be found");
551 break;
554 OString pathString = aPath.makeStringAndClear();
555 if ( !aPath.isEmpty() && pathString != "xe" )
556 m_pShapeAttrList->add( XML_path, pathString );
558 #if OSL_DEBUG_LEVEL > 0
559 else
560 fprintf( stderr, "TODO: unhandled shape path, missing either pVertices or pSegmentInfo.\n" );
561 #endif
563 bAlreadyWritten[ ESCHER_Prop_pVertices ] = true;
564 bAlreadyWritten[ ESCHER_Prop_pSegmentInfo ] = true;
565 break;
567 case ESCHER_Prop_fillType: // 384
568 case ESCHER_Prop_fillColor: // 385
569 case ESCHER_Prop_fillBackColor: // 387
570 case ESCHER_Prop_fillBlip: // 390
571 case ESCHER_Prop_fNoFillHitTest: // 447
572 case ESCHER_Prop_fillOpacity: // 386
574 sal_uInt32 nValue;
575 sax_fastparser::FastAttributeList *pAttrList = FastSerializerHelper::createAttrList();
577 bool imageData = false;
578 EscherPropSortStruct aStruct;
579 if ( rProps.GetOpt( ESCHER_Prop_fillBlip, aStruct ) && m_pTextExport)
581 SvMemoryStream aStream;
582 int nHeaderSize = 25; // The first bytes are WW8-specific, we're only interested in the PNG
583 aStream.WriteBytes(aStruct.pBuf + nHeaderSize, aStruct.nPropSize - nHeaderSize);
584 aStream.Seek(0);
585 Graphic aGraphic;
586 GraphicConverter::Import(aStream, aGraphic);
588 BitmapChecksum nChecksum = aGraphic.GetChecksum();
589 OUString aImageId = m_pTextExport->FindRelId(nChecksum);
590 if (aImageId.isEmpty())
592 aImageId = m_pTextExport->GetDrawingML().WriteImage( aGraphic );
593 m_pTextExport->CacheRelId(nChecksum, aImageId);
595 pAttrList->add(FSNS(XML_r, XML_id), OUStringToOString(aImageId, RTL_TEXTENCODING_UTF8));
596 imageData = true;
599 if ( rProps.GetOpt( ESCHER_Prop_fNoFillHitTest, nValue ) )
600 impl_AddBool( pAttrList, FSNS(XML_o, XML_detectmouseclick), nValue != 0 );
602 if (imageData)
603 m_pSerializer->singleElementNS( XML_v, XML_imagedata, XFastAttributeListRef( pAttrList ) );
604 else
606 if ( rProps.GetOpt( ESCHER_Prop_fillType, nValue ) )
608 const char *pFillType = nullptr;
609 switch ( nValue )
611 case ESCHER_FillSolid: pFillType = "solid"; break;
612 // TODO case ESCHER_FillPattern: pFillType = ""; break;
613 case ESCHER_FillTexture: pFillType = "tile"; break;
614 // TODO case ESCHER_FillPicture: pFillType = ""; break;
615 // TODO case ESCHER_FillShade: pFillType = ""; break;
616 // TODO case ESCHER_FillShadeCenter: pFillType = ""; break;
617 // TODO case ESCHER_FillShadeShape: pFillType = ""; break;
618 // TODO case ESCHER_FillShadeScale: pFillType = ""; break;
619 // TODO case ESCHER_FillShadeTitle: pFillType = ""; break;
620 // TODO case ESCHER_FillBackground: pFillType = ""; break;
621 default:
622 #if OSL_DEBUG_LEVEL > 0
623 fprintf( stderr, "TODO: unhandled fill type\n" );
624 #endif
625 break;
627 if ( pFillType )
628 pAttrList->add( XML_type, pFillType );
630 else if (!rProps.GetOpt(ESCHER_Prop_fillColor, nValue))
631 pAttrList->add( XML_on, "false" );
633 if ( rProps.GetOpt( ESCHER_Prop_fillColor, nValue ) )
634 impl_AddColor( m_pShapeAttrList, XML_fillcolor, nValue );
636 if ( rProps.GetOpt( ESCHER_Prop_fillBackColor, nValue ) )
637 impl_AddColor( pAttrList, XML_color2, nValue );
640 if (rProps.GetOpt(ESCHER_Prop_fillOpacity, nValue))
641 // Partly undo the transformation at the end of EscherPropertyContainer::CreateFillProperties(): VML opacity is 0..1.
642 pAttrList->add(XML_opacity, OString::number(double((nValue * 100) >> 16) / 100));
643 m_pSerializer->singleElementNS( XML_v, XML_fill, XFastAttributeListRef( pAttrList ) );
647 bAlreadyWritten[ ESCHER_Prop_fillType ] = true;
648 bAlreadyWritten[ ESCHER_Prop_fillColor ] = true;
649 bAlreadyWritten[ ESCHER_Prop_fillBackColor ] = true;
650 bAlreadyWritten[ ESCHER_Prop_fillBlip ] = true;
651 bAlreadyWritten[ ESCHER_Prop_fNoFillHitTest ] = true;
652 bAlreadyWritten[ ESCHER_Prop_fillOpacity ] = true;
653 break;
655 case ESCHER_Prop_lineColor: // 448
656 case ESCHER_Prop_lineWidth: // 459
657 case ESCHER_Prop_lineDashing: // 462
658 case ESCHER_Prop_lineStartArrowhead: // 464
659 case ESCHER_Prop_lineEndArrowhead: // 465
660 case ESCHER_Prop_lineStartArrowWidth: // 466
661 case ESCHER_Prop_lineStartArrowLength: // 467
662 case ESCHER_Prop_lineEndArrowWidth: // 468
663 case ESCHER_Prop_lineEndArrowLength: // 469
664 case ESCHER_Prop_lineJoinStyle: // 470
665 case ESCHER_Prop_lineEndCapStyle: // 471
667 sal_uInt32 nValue;
668 sax_fastparser::FastAttributeList *pAttrList = FastSerializerHelper::createAttrList();
670 if ( rProps.GetOpt( ESCHER_Prop_lineColor, nValue ) )
671 impl_AddColor( pAttrList, XML_color, nValue );
673 if ( rProps.GetOpt( ESCHER_Prop_lineWidth, nValue ) )
674 impl_AddInt( pAttrList, XML_weight, nValue );
676 if ( rProps.GetOpt( ESCHER_Prop_lineDashing, nValue ) )
678 const char *pDashStyle = nullptr;
679 switch ( nValue )
681 case ESCHER_LineSolid: pDashStyle = "solid"; break;
682 case ESCHER_LineDashSys: pDashStyle = "shortdash"; break;
683 case ESCHER_LineDotSys: pDashStyle = "shortdot"; break;
684 case ESCHER_LineDashDotSys: pDashStyle = "shortdashdot"; break;
685 case ESCHER_LineDashDotDotSys: pDashStyle = "shortdashdotdot"; break;
686 case ESCHER_LineDotGEL: pDashStyle = "dot"; break;
687 case ESCHER_LineDashGEL: pDashStyle = "dash"; break;
688 case ESCHER_LineLongDashGEL: pDashStyle = "longdash"; break;
689 case ESCHER_LineDashDotGEL: pDashStyle = "dashdot"; break;
690 case ESCHER_LineLongDashDotGEL: pDashStyle = "longdashdot"; break;
691 case ESCHER_LineLongDashDotDotGEL: pDashStyle = "longdashdotdot"; break;
693 if ( pDashStyle )
694 pAttrList->add( XML_dashstyle, pDashStyle );
697 if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowhead, nValue ) )
698 impl_AddArrowHead( pAttrList, XML_startarrow, nValue );
700 if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowhead, nValue ) )
701 impl_AddArrowHead( pAttrList, XML_endarrow, nValue );
703 if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowWidth, nValue ) )
704 impl_AddArrowWidth( pAttrList, XML_startarrowwidth, nValue );
706 if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowLength, nValue ) )
707 impl_AddArrowLength( pAttrList, XML_startarrowlength, nValue );
709 if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowWidth, nValue ) )
710 impl_AddArrowWidth( pAttrList, XML_endarrowwidth, nValue );
712 if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowLength, nValue ) )
713 impl_AddArrowLength( pAttrList, XML_endarrowlength, nValue );
715 if ( rProps.GetOpt( ESCHER_Prop_lineJoinStyle, nValue ) )
717 const char *pJoinStyle = nullptr;
718 switch ( nValue )
720 case ESCHER_LineJoinBevel: pJoinStyle = "bevel"; break;
721 case ESCHER_LineJoinMiter: pJoinStyle = "miter"; break;
722 case ESCHER_LineJoinRound: pJoinStyle = "round"; break;
724 if ( pJoinStyle )
725 pAttrList->add( XML_joinstyle, pJoinStyle );
728 if ( rProps.GetOpt( ESCHER_Prop_lineEndCapStyle, nValue ) )
730 const char *pEndCap = nullptr;
731 switch ( nValue )
733 case ESCHER_LineEndCapRound: pEndCap = "round"; break;
734 case ESCHER_LineEndCapSquare: pEndCap = "square"; break;
735 case ESCHER_LineEndCapFlat: pEndCap = "flat"; break;
737 if ( pEndCap )
738 pAttrList->add( XML_endcap, pEndCap );
741 m_pSerializer->singleElementNS( XML_v, XML_stroke, XFastAttributeListRef( pAttrList ) );
743 bAlreadyWritten[ ESCHER_Prop_lineColor ] = true;
744 bAlreadyWritten[ ESCHER_Prop_lineWidth ] = true;
745 bAlreadyWritten[ ESCHER_Prop_lineDashing ] = true;
746 bAlreadyWritten[ ESCHER_Prop_lineStartArrowhead ] = true;
747 bAlreadyWritten[ ESCHER_Prop_lineEndArrowhead ] = true;
748 bAlreadyWritten[ ESCHER_Prop_lineStartArrowWidth ] = true;
749 bAlreadyWritten[ ESCHER_Prop_lineStartArrowLength ] = true;
750 bAlreadyWritten[ ESCHER_Prop_lineEndArrowWidth ] = true;
751 bAlreadyWritten[ ESCHER_Prop_lineEndArrowLength ] = true;
752 bAlreadyWritten[ ESCHER_Prop_lineJoinStyle ] = true;
753 bAlreadyWritten[ ESCHER_Prop_lineEndCapStyle ] = true;
754 break;
756 case ESCHER_Prop_fHidden:
757 if ( !it->nPropValue )
758 m_ShapeStyle.append( ";visibility:hidden" );
759 break;
760 case ESCHER_Prop_shadowColor:
761 case ESCHER_Prop_fshadowObscured:
763 sal_uInt32 nValue = 0;
764 bool bShadow = false;
765 bool bObscured = false;
766 if ( rProps.GetOpt( ESCHER_Prop_fshadowObscured, nValue ) )
768 bShadow = (( nValue & 0x20002 ) == 0x20002 );
769 bObscured = (( nValue & 0x10001 ) == 0x10001 );
771 if ( bShadow )
773 sax_fastparser::FastAttributeList *pAttrList = FastSerializerHelper::createAttrList();
774 impl_AddBool( pAttrList, XML_on, bShadow );
775 impl_AddBool( pAttrList, XML_obscured, bObscured );
777 if ( rProps.GetOpt( ESCHER_Prop_shadowColor, nValue ) )
778 impl_AddColor( pAttrList, XML_color, nValue );
780 m_pSerializer->singleElementNS( XML_v, XML_shadow, XFastAttributeListRef( pAttrList ) );
781 bAlreadyWritten[ ESCHER_Prop_fshadowObscured ] = true;
782 bAlreadyWritten[ ESCHER_Prop_shadowColor ] = true;
785 break;
786 case ESCHER_Prop_gtextUNICODE:
787 case ESCHER_Prop_gtextFont:
789 EscherPropSortStruct aUnicode;
790 if (rProps.GetOpt(ESCHER_Prop_gtextUNICODE, aUnicode))
792 SvMemoryStream aStream;
793 aStream.WriteBytes(it->pBuf, it->nPropSize);
794 aStream.Seek(0);
795 OUString aTextPathString = SvxMSDffManager::MSDFFReadZString(aStream, it->nPropSize, true);
796 aStream.Seek(0);
798 m_pSerializer->singleElementNS( XML_v, XML_path,
799 XML_textpathok, "t",
800 FSEND );
802 sax_fastparser::FastAttributeList* pAttrList = FastSerializerHelper::createAttrList();
803 pAttrList->add(XML_on, "t");
804 pAttrList->add(XML_fitshape, "t");
805 pAttrList->add(XML_string, OUStringToOString(aTextPathString, RTL_TEXTENCODING_UTF8));
806 EscherPropSortStruct aFont;
807 OUString aStyle;
808 if (rProps.GetOpt(ESCHER_Prop_gtextFont, aFont))
810 aStream.WriteBytes(aFont.pBuf, aFont.nPropSize);
811 aStream.Seek(0);
812 OUString aTextPathFont = SvxMSDffManager::MSDFFReadZString(aStream, aFont.nPropSize, true);
813 aStyle += "font-family:\"" + aTextPathFont + "\"";
815 sal_uInt32 nSize;
816 if (rProps.GetOpt(ESCHER_Prop_gtextSize, nSize))
818 float nSizeF = (sal_Int32)nSize / 65536.0;
819 OUString aSize = OUString::number(nSizeF);
820 aStyle += ";font-size:" + aSize + "pt";
822 if (!aStyle.isEmpty())
823 pAttrList->add(XML_style, OUStringToOString(aStyle, RTL_TEXTENCODING_UTF8));
824 m_pSerializer->singleElementNS(XML_v, XML_textpath, XFastAttributeListRef(pAttrList));
827 bAlreadyWritten[ESCHER_Prop_gtextUNICODE] = true;
828 bAlreadyWritten[ESCHER_Prop_gtextFont] = true;
830 break;
831 case ESCHER_Prop_Rotation:
833 // The higher half of the variable contains the angle.
834 m_ShapeStyle.append(";rotation:").append(double(it->nPropValue >> 16));
835 bAlreadyWritten[ESCHER_Prop_Rotation] = true;
837 break;
838 case ESCHER_Prop_fNoLineDrawDash:
840 // See DffPropertyReader::ApplyLineAttributes().
841 impl_AddBool( m_pShapeAttrList, XML_stroked, (it->nPropValue & 8) != 0 );
842 bAlreadyWritten[ESCHER_Prop_fNoLineDrawDash] = true;
844 break;
845 case ESCHER_Prop_wzName:
847 SvMemoryStream aStream;
848 aStream.WriteBytes(it->pBuf, it->nPropSize);
849 aStream.Seek(0);
850 OUString idStr = SvxMSDffManager::MSDFFReadZString(aStream, it->nPropSize, true);
851 aStream.Seek(0);
852 if (!IsWaterMarkShape(m_pSdrObject->GetName()))
853 m_pShapeAttrList->add(XML_ID, OUStringToOString(idStr, RTL_TEXTENCODING_UTF8).getStr());
855 bAlreadyWritten[ESCHER_Prop_wzName] = true;
857 break;
858 default:
859 #if OSL_DEBUG_LEVEL > 0
860 fprintf( stderr, "TODO VMLExport::Commit(), unimplemented id: %d, value: %" SAL_PRIuUINT32 ", data: [%" SAL_PRIuUINT32 ", %p]\n",
861 nId, it->nPropValue, it->nPropSize, it->pBuf );
862 if ( it->nPropSize )
864 const sal_uInt8 *pIt = it->pBuf;
865 fprintf( stderr, " ( " );
866 for ( int nCount = it->nPropSize; nCount; --nCount )
868 fprintf( stderr, "%02x ", *pIt );
869 ++pIt;
871 fprintf( stderr, ")\n" );
873 #endif
874 break;
878 m_pSerializer->mergeTopMarks(Tag_Commit, sax_fastparser::MergeMarks::POSTPONE );
881 OString VMLExport::ShapeIdString( sal_uInt32 nId )
883 return OStringBuffer( 20 ).append( "shape_" ).append( sal_Int64( nId ) ).makeStringAndClear();
886 void VMLExport::AddFlipXY( )
888 const sal_uInt32 nFlipHandV = SHAPEFLAG_FLIPH + SHAPEFLAG_FLIPV;
889 switch ( m_nShapeFlags & nFlipHandV )
891 case SHAPEFLAG_FLIPH: m_ShapeStyle.append( ";flip:x" ); break;
892 case SHAPEFLAG_FLIPV: m_ShapeStyle.append( ";flip:y" ); break;
893 case nFlipHandV: m_ShapeStyle.append( ";flip:xy" ); break;
897 void VMLExport::AddLineDimensions( const tools::Rectangle& rRectangle )
899 // style
900 if (!m_ShapeStyle.isEmpty())
901 m_ShapeStyle.append( ";" );
903 m_ShapeStyle.append( "position:absolute" );
905 AddFlipXY();
907 // the actual dimensions
908 OString aLeft, aTop, aRight, aBottom;
910 if ( mnGroupLevel == 1 )
912 const OString aPt( "pt" );
913 aLeft = OString::number( double( rRectangle.Left() ) / 20 ) + aPt;
914 aTop = OString::number( double( rRectangle.Top() ) / 20 ) + aPt;
915 aRight = OString::number( double( rRectangle.Right() ) / 20 ) + aPt;
916 aBottom = OString::number( double( rRectangle.Bottom() ) / 20 ) + aPt;
918 else
920 aLeft = OString::number( rRectangle.Left() );
921 aTop = OString::number( rRectangle.Top() );
922 aRight = OString::number( rRectangle.Right() );
923 aBottom = OString::number( rRectangle.Bottom() );
926 m_pShapeAttrList->add( XML_from,
927 OStringBuffer( 20 ).append( aLeft )
928 .append( "," ).append( aTop )
929 .makeStringAndClear() );
931 m_pShapeAttrList->add( XML_to,
932 OStringBuffer( 20 ).append( aRight )
933 .append( "," ).append( aBottom )
934 .makeStringAndClear() );
937 void VMLExport::AddRectangleDimensions( OStringBuffer& rBuffer, const tools::Rectangle& rRectangle, bool rbAbsolutePos)
939 if ( !rBuffer.isEmpty() )
940 rBuffer.append( ";" );
942 if (rbAbsolutePos)
944 rBuffer.append( "position:absolute;" );
947 if ( mnGroupLevel == 1 )
949 rBuffer.append( "margin-left:" ).append( double( rRectangle.Left() ) / 20 )
950 .append( "pt;margin-top:" ).append( double( rRectangle.Top() ) / 20 )
951 .append( "pt;width:" ).append( double( rRectangle.Right() - rRectangle.Left() ) / 20 )
952 .append( "pt;height:" ).append( double( rRectangle.Bottom() - rRectangle.Top() ) / 20 )
953 .append( "pt" );
955 else
957 rBuffer.append( "left:" ).append( rRectangle.Left() )
958 .append( ";top:" ).append( rRectangle.Top() )
959 .append( ";width:" ).append( rRectangle.Right() - rRectangle.Left() )
960 .append( ";height:" ).append( rRectangle.Bottom() - rRectangle.Top() );
963 AddFlipXY();
966 void VMLExport::AddShapeAttribute( sal_Int32 nAttribute, const OString& rValue )
968 m_pShapeAttrList->add( nAttribute, rValue );
971 std::vector<OString> lcl_getShapeTypes()
973 std::vector<OString> aRet;
975 OUString aPath("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/filter/vml-shape-types");
976 rtl::Bootstrap::expandMacros(aPath);
977 SvFileStream aStream(aPath, StreamMode::READ);
978 if (aStream.GetError() != ERRCODE_NONE)
979 SAL_WARN("oox", "failed to open vml-shape-types");
980 OString aLine;
981 bool bNotDone = aStream.ReadLine(aLine);
982 while (bNotDone)
984 // Filter out comments.
985 if (!aLine.startsWith("/"))
986 aRet.push_back(aLine);
987 bNotDone = aStream.ReadLine(aLine);
989 return aRet;
992 bool lcl_isTextBox(const SdrObject* pSdrObject)
994 uno::Reference<beans::XPropertySet> xPropertySet(const_cast<SdrObject*>(pSdrObject)->getUnoShape(), uno::UNO_QUERY);
995 if (xPropertySet.is())
997 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
998 return xPropertySetInfo->hasPropertyByName("TextBox") && xPropertySet->getPropertyValue("TextBox").get<bool>();
1000 return false;
1003 OUString lcl_getAnchorIdFromGrabBag(const SdrObject* pSdrObject)
1005 OUString aResult;
1007 uno::Reference<beans::XPropertySet> xShape(const_cast<SdrObject*>(pSdrObject)->getUnoShape(), uno::UNO_QUERY);
1008 if (xShape->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"))
1010 comphelper::SequenceAsHashMap aInteropGrabBag(xShape->getPropertyValue("InteropGrabBag"));
1011 if (aInteropGrabBag.find("AnchorId") != aInteropGrabBag.end())
1012 aInteropGrabBag["AnchorId"] >>= aResult;
1015 return aResult;
1018 sal_Int32 VMLExport::StartShape()
1020 if ( m_nShapeType == ESCHER_ShpInst_Nil )
1021 return -1;
1023 // some of the shapes have their own name ;-)
1024 sal_Int32 nShapeElement = -1;
1025 bool bReferToShapeType = false;
1026 switch ( m_nShapeType )
1028 case ESCHER_ShpInst_NotPrimitive: nShapeElement = XML_shape; break;
1029 case ESCHER_ShpInst_Rectangle: nShapeElement = XML_rect; break;
1030 case ESCHER_ShpInst_RoundRectangle: nShapeElement = XML_roundrect; break;
1031 case ESCHER_ShpInst_Ellipse: nShapeElement = XML_oval; break;
1032 case ESCHER_ShpInst_Arc: nShapeElement = XML_arc; break;
1033 case ESCHER_ShpInst_Line: nShapeElement = XML_line; break;
1034 default:
1035 if ( m_nShapeType < ESCHER_ShpInst_COUNT )
1037 nShapeElement = XML_shape;
1039 // a predefined shape?
1040 static std::vector<OString> aShapeTypes = lcl_getShapeTypes();
1041 OString aShapeType = aShapeTypes[ m_nShapeType ];
1042 if ( aShapeType != "NULL" )
1044 bReferToShapeType = true;
1045 if ( !m_aShapeTypeWritten[ m_nShapeType ] )
1047 m_pSerializer->write( aShapeType.getStr() );
1048 m_aShapeTypeWritten[ m_nShapeType ] = true;
1051 else
1053 // rectangle is probably the best fallback...
1054 nShapeElement = XML_rect;
1057 break;
1060 // anchoring
1061 switch (m_eHOri)
1063 case text::HoriOrientation::LEFT:
1064 m_ShapeStyle.append(";mso-position-horizontal:left");
1065 break;
1066 case text::HoriOrientation::CENTER:
1067 m_ShapeStyle.append(";mso-position-horizontal:center");
1068 break;
1069 case text::HoriOrientation::RIGHT:
1070 m_ShapeStyle.append(";mso-position-horizontal:right");
1071 break;
1072 case text::HoriOrientation::INSIDE:
1073 m_ShapeStyle.append(";mso-position-horizontal:inside");
1074 break;
1075 case text::HoriOrientation::OUTSIDE:
1076 m_ShapeStyle.append(";mso-position-horizontal:outside");
1077 break;
1078 default:
1079 case text::HoriOrientation::NONE:
1080 break;
1082 switch (m_eHRel)
1084 case text::RelOrientation::PAGE_PRINT_AREA:
1085 m_ShapeStyle.append(";mso-position-horizontal-relative:margin");
1086 break;
1087 case text::RelOrientation::PAGE_FRAME:
1088 case text::RelOrientation::PAGE_LEFT:
1089 case text::RelOrientation::PAGE_RIGHT:
1090 m_ShapeStyle.append(";mso-position-horizontal-relative:page");
1091 break;
1092 case text::RelOrientation::CHAR:
1093 m_ShapeStyle.append(";mso-position-horizontal-relative:char");
1094 break;
1095 default:
1096 break;
1099 switch (m_eVOri)
1101 case text::VertOrientation::TOP:
1102 case text::VertOrientation::LINE_TOP:
1103 case text::VertOrientation::CHAR_TOP:
1104 m_ShapeStyle.append(";mso-position-vertical:top");
1105 break;
1106 case text::VertOrientation::CENTER:
1107 case text::VertOrientation::LINE_CENTER:
1108 m_ShapeStyle.append(";mso-position-vertical:center");
1109 break;
1110 case text::VertOrientation::BOTTOM:
1111 case text::VertOrientation::LINE_BOTTOM:
1112 case text::VertOrientation::CHAR_BOTTOM:
1113 m_ShapeStyle.append(";mso-position-vertical:bottom");
1114 break;
1115 default:
1116 case text::VertOrientation::NONE:
1117 break;
1119 switch (m_eVRel)
1121 case text::RelOrientation::PAGE_PRINT_AREA:
1122 m_ShapeStyle.append(";mso-position-vertical-relative:margin");
1123 break;
1124 case text::RelOrientation::PAGE_FRAME:
1125 m_ShapeStyle.append(";mso-position-vertical-relative:page");
1126 break;
1127 default:
1128 break;
1131 // add style
1132 m_pShapeAttrList->add( XML_style, m_ShapeStyle.makeStringAndClear() );
1134 OUString sAnchorId = lcl_getAnchorIdFromGrabBag(m_pSdrObject);
1135 if (!sAnchorId.isEmpty())
1136 m_pShapeAttrList->addNS(XML_wp14, XML_anchorId, OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8));
1138 if ( nShapeElement >= 0 && !m_pShapeAttrList->hasAttribute( XML_type ) && bReferToShapeType )
1140 m_pShapeAttrList->add( XML_type, OStringBuffer( 20 )
1141 .append( "shapetype_" ).append( sal_Int32( m_nShapeType ) )
1142 .makeStringAndClear() );
1145 // start of the shape
1146 m_pSerializer->startElementNS( XML_v, nShapeElement, XFastAttributeListRef( m_pShapeAttrList ) );
1148 // now check if we have some editeng text (not associated textbox) and we have a text exporter registered
1149 const SdrTextObj* pTxtObj = dynamic_cast<const SdrTextObj*>( m_pSdrObject );
1150 if (pTxtObj && m_pTextExport && msfilter::util::HasTextBoxContent(m_nShapeType) && !IsWaterMarkShape(m_pSdrObject->GetName()) && !lcl_isTextBox(m_pSdrObject))
1152 const OutlinerParaObject* pParaObj = nullptr;
1153 bool bOwnParaObj = false;
1156 #i13885#
1157 When the object is actively being edited, that text is not set into
1158 the objects normal text object, but lives in a separate object.
1160 if (pTxtObj->IsTextEditActive())
1162 pParaObj = pTxtObj->GetEditOutlinerParaObject();
1163 bOwnParaObj = true;
1165 else
1167 pParaObj = pTxtObj->GetOutlinerParaObject();
1170 if( pParaObj )
1172 // this is reached only in case some text is attached to the shape
1173 m_pSerializer->startElementNS(XML_v, XML_textbox, FSEND);
1174 m_pTextExport->WriteOutliner(*pParaObj);
1175 m_pSerializer->endElementNS(XML_v, XML_textbox);
1176 if( bOwnParaObj )
1177 delete pParaObj;
1181 return nShapeElement;
1184 void VMLExport::EndShape( sal_Int32 nShapeElement )
1186 if ( nShapeElement >= 0 )
1188 if (m_pTextExport && lcl_isTextBox(m_pSdrObject))
1190 uno::Reference<beans::XPropertySet> xPropertySet(const_cast<SdrObject*>(m_pSdrObject)->getUnoShape(), uno::UNO_QUERY);
1191 comphelper::SequenceAsHashMap aCustomShapeProperties(xPropertySet->getPropertyValue("CustomShapeGeometry"));
1192 sax_fastparser::FastAttributeList* pTextboxAttrList = FastSerializerHelper::createAttrList();
1193 if (aCustomShapeProperties.find("TextPreRotateAngle") != aCustomShapeProperties.end())
1195 sal_Int32 nTextRotateAngle = aCustomShapeProperties["TextPreRotateAngle"].get<sal_Int32>();
1196 if (nTextRotateAngle == -270)
1197 pTextboxAttrList->add(XML_style, "mso-layout-flow-alt:bottom-to-top");
1199 sax_fastparser::XFastAttributeListRef xTextboxAttrList(pTextboxAttrList);
1200 pTextboxAttrList = nullptr;
1201 m_pSerializer->startElementNS(XML_v, XML_textbox, xTextboxAttrList);
1203 m_pTextExport->WriteVMLTextBox(uno::Reference<drawing::XShape>(xPropertySet, uno::UNO_QUERY_THROW));
1205 m_pSerializer->endElementNS(XML_v, XML_textbox);
1208 // end of the shape
1209 m_pSerializer->endElementNS( XML_v, nShapeElement );
1213 void VMLExport::AddSdrObject( const SdrObject& rObj, sal_Int16 eHOri, sal_Int16 eVOri, sal_Int16 eHRel, sal_Int16 eVRel, const Point* pNdTopLeft, const bool bOOxmlExport )
1215 m_pSdrObject = &rObj;
1216 m_eHOri = eHOri;
1217 m_eVOri = eVOri;
1218 m_eHRel = eHRel;
1219 m_eVRel = eVRel;
1220 m_pNdTopLeft = pNdTopLeft;
1221 EscherEx::AddSdrObject(rObj, bOOxmlExport);
1224 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */