1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <config_folders.h>
21 #include <rtl/bootstrap.hxx>
22 #include <svl/itemset.hxx>
23 #include <oox/export/drawingml.hxx>
24 #include <oox/export/vmlexport.hxx>
25 #include <sax/fastattribs.hxx>
26 
27 #include <oox/token/tokens.hxx>
28 
29 #include <rtl/strbuf.hxx>
30 #include <rtl/ustring.hxx>
31 #include <sal/log.hxx>
32 
33 #include <tools/stream.hxx>
34 #include <comphelper/sequenceashashmap.hxx>
35 #include <svx/svdotext.hxx>
36 #include <svx/svdograf.hxx>
37 #include <svx/sdmetitm.hxx>
38 #include <vcl/cvtgrf.hxx>
39 #include <filter/msfilter/msdffimp.hxx>
40 #include <filter/msfilter/util.hxx>
41 #include <filter/msfilter/escherex.hxx>
42 
43 #include <com/sun/star/beans/XPropertySet.hpp>
44 #include <com/sun/star/beans/XPropertySetInfo.hpp>
45 #include <com/sun/star/drawing/XShape.hpp>
46 #include <com/sun/star/text/HoriOrientation.hpp>
47 #include <com/sun/star/text/VertOrientation.hpp>
48 #include <com/sun/star/text/RelOrientation.hpp>
49 
50 #include <cstdio>
51 
52 using namespace sax_fastparser;
53 using namespace oox::vml;
54 using namespace com::sun::star;
55 
56 static const sal_Int32 Tag_Container = 44444;
57 static const sal_Int32 Tag_Commit = 44445;
58 
VMLExport(::sax_fastparser::FSHelperPtr const & pSerializer,VMLTextExport * pTextExport)59 VMLExport::VMLExport( ::sax_fastparser::FSHelperPtr const & pSerializer, VMLTextExport* pTextExport )
60     : EscherEx( std::make_shared<EscherExGlobal>(), nullptr, /*bOOXML=*/true )
61     , m_pSerializer( pSerializer )
62     , m_pTextExport( pTextExport )
63     , m_eHOri( 0 )
64     , m_eVOri( 0 )
65     , m_eHRel( 0 )
66     , m_eVRel( 0 )
67     , m_bInline( false )
68     , m_pSdrObject( nullptr )
69     , m_pShapeAttrList( nullptr )
70     , m_nShapeType( ESCHER_ShpInst_Nil )
71     , m_nShapeFlags(ShapeFlag::NONE)
72     , m_ShapeStyle( 200 )
73     , m_aShapeTypeWritten( ESCHER_ShpInst_COUNT )
74     , m_bSkipwzName( false )
75     , m_bUseHashMarkForType( false )
76     , m_bOverrideShapeIdGeneration( false )
77     , m_nShapeIDCounter( 0 )
78 {
79     mnGroupLevel = 1;
80 }
81 
SetFS(const::sax_fastparser::FSHelperPtr & pSerializer)82 void VMLExport::SetFS( const ::sax_fastparser::FSHelperPtr& pSerializer )
83 {
84     m_pSerializer = pSerializer;
85 }
86 
~VMLExport()87 VMLExport::~VMLExport()
88 {
89     delete mpOutStrm;
90     mpOutStrm = nullptr;
91 }
92 
OpenContainer(sal_uInt16 nEscherContainer,int nRecInstance)93 void VMLExport::OpenContainer( sal_uInt16 nEscherContainer, int nRecInstance )
94 {
95     EscherEx::OpenContainer( nEscherContainer, nRecInstance );
96 
97     if ( nEscherContainer == ESCHER_SpContainer )
98     {
99         // opening a shape container
100         SAL_WARN_IF(m_nShapeType != ESCHER_ShpInst_Nil, "oox.vml", "opening shape inside of a shape!");
101         m_nShapeType = ESCHER_ShpInst_Nil;
102         m_pShapeAttrList = FastSerializerHelper::createAttrList();
103 
104         m_ShapeStyle.setLength(0);
105         m_ShapeStyle.ensureCapacity(200);
106 
107         // postpone the output so that we are able to write even the elements
108         // that we learn inside Commit()
109         m_pSerializer->mark(Tag_Container);
110     }
111 }
112 
CloseContainer()113 void VMLExport::CloseContainer()
114 {
115     if ( mRecTypes.back() == ESCHER_SpContainer )
116     {
117         // write the shape now when we have all the info
118         sal_Int32 nShapeElement = StartShape();
119 
120         m_pSerializer->mergeTopMarks(Tag_Container);
121 
122         EndShape( nShapeElement );
123 
124         // cleanup
125         m_nShapeType = ESCHER_ShpInst_Nil;
126         m_pShapeAttrList = nullptr;
127     }
128 
129     EscherEx::CloseContainer();
130 }
131 
EnterGroup(const OUString & rShapeName,const tools::Rectangle * pRect)132 sal_uInt32 VMLExport::EnterGroup( const OUString& rShapeName, const tools::Rectangle* pRect )
133 {
134     sal_uInt32 nShapeId = GenerateShapeId();
135 
136     OStringBuffer aStyle( 200 );
137     FastAttributeList *pAttrList = FastSerializerHelper::createAttrList();
138 
139     pAttrList->add( XML_id, ShapeIdString( nShapeId ) );
140 
141     if ( rShapeName.getLength() )
142         pAttrList->add( XML_alt, OUStringToOString( rShapeName, RTL_TEXTENCODING_UTF8 ) );
143 
144     bool rbAbsolutePos = true;
145     //editAs
146     OUString rEditAs = EscherEx::GetEditAs();
147     if (!rEditAs.isEmpty())
148     {
149         pAttrList->add(XML_editas, OUStringToOString( rEditAs, RTL_TEXTENCODING_UTF8 ));
150         rbAbsolutePos = false;
151     }
152 
153     // style
154     if ( pRect )
155         AddRectangleDimensions( aStyle, *pRect, rbAbsolutePos );
156 
157     if ( !aStyle.isEmpty() )
158         pAttrList->add( XML_style, aStyle.makeStringAndClear() );
159 
160     // coordorigin/coordsize
161     if ( pRect && ( mnGroupLevel == 1 ) )
162     {
163         pAttrList->add( XML_coordorigin,
164                 OStringBuffer( 20 ).append( sal_Int32( pRect->Left() ) )
165                 .append( "," ).append( sal_Int32( pRect->Top() ) )
166                 .makeStringAndClear() );
167 
168         pAttrList->add( XML_coordsize,
169                 OStringBuffer( 20 ).append( sal_Int32( pRect->Right() ) - sal_Int32( pRect->Left() ) )
170                 .append( "," ).append( sal_Int32( pRect->Bottom() ) - sal_Int32( pRect->Top() ) )
171                 .makeStringAndClear() );
172     }
173 
174     m_pSerializer->startElementNS( XML_v, XML_group, XFastAttributeListRef( pAttrList ) );
175 
176     mnGroupLevel++;
177     return nShapeId;
178 }
179 
LeaveGroup()180 void VMLExport::LeaveGroup()
181 {
182     --mnGroupLevel;
183     m_pSerializer->endElementNS( XML_v, XML_group );
184 }
185 
AddShape(sal_uInt32 nShapeType,ShapeFlag nShapeFlags,sal_uInt32 nShapeId)186 void VMLExport::AddShape( sal_uInt32 nShapeType, ShapeFlag nShapeFlags, sal_uInt32 nShapeId )
187 {
188     m_nShapeType = nShapeType;
189     m_nShapeFlags = nShapeFlags;
190 
191     m_sShapeId = ShapeIdString( nShapeId );
192     // If shape is a watermark object - should keep the original shape's name
193     // because Microsoft detects if it is a watermark by the actual name
194     if (!IsWaterMarkShape(m_pSdrObject->GetName()))
195     {
196         // Not a watermark object
197         m_pShapeAttrList->add( XML_id, m_sShapeId );
198     }
199     else
200     {
201         // A watermark object - store the optional shape ID
202         m_pShapeAttrList->add( XML_id, OUStringToOString(m_pSdrObject->GetName(), RTL_TEXTENCODING_UTF8) );
203         // also ('o:spid')
204         m_pShapeAttrList->addNS( XML_o, XML_spid, m_sShapeId );
205     }
206 }
207 
IsWaterMarkShape(const OUString & rStr)208 bool VMLExport::IsWaterMarkShape(const OUString& rStr)
209 {
210      if (rStr.isEmpty() )  return false;
211 
212      return rStr.match("PowerPlusWaterMarkObject") || rStr.match("WordPictureWatermark");
213 }
214 
OverrideShapeIDGen(bool bOverrideShapeIdGen,const OString & sShapeIDPrefix)215 void VMLExport::OverrideShapeIDGen(bool bOverrideShapeIdGen, const OString& sShapeIDPrefix)
216 {
217     m_bOverrideShapeIdGeneration = bOverrideShapeIdGen;
218     if(bOverrideShapeIdGen)
219     {
220         assert(!sShapeIDPrefix.isEmpty());
221         m_sShapeIDPrefix = sShapeIDPrefix;
222     }
223     else
224         m_sShapeIDPrefix.clear();
225 }
226 
impl_AddArrowHead(sax_fastparser::FastAttributeList * pAttrList,sal_Int32 nElement,sal_uInt32 nValue)227 static void impl_AddArrowHead( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue )
228 {
229     if ( !pAttrList )
230         return;
231 
232     const char *pArrowHead = nullptr;
233     switch ( nValue )
234     {
235         case ESCHER_LineNoEnd:           pArrowHead = "none"; break;
236         case ESCHER_LineArrowEnd:        pArrowHead = "block"; break;
237         case ESCHER_LineArrowStealthEnd: pArrowHead = "classic"; break;
238         case ESCHER_LineArrowDiamondEnd: pArrowHead = "diamond"; break;
239         case ESCHER_LineArrowOvalEnd:    pArrowHead = "oval"; break;
240         case ESCHER_LineArrowOpenEnd:    pArrowHead = "open"; break;
241     }
242 
243     if ( pArrowHead )
244         pAttrList->add( nElement, pArrowHead );
245 }
246 
impl_AddArrowLength(sax_fastparser::FastAttributeList * pAttrList,sal_Int32 nElement,sal_uInt32 nValue)247 static void impl_AddArrowLength( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue )
248 {
249     if ( !pAttrList )
250         return;
251 
252     const char *pArrowLength = nullptr;
253     switch ( nValue )
254     {
255         case ESCHER_LineShortArrow:     pArrowLength = "short"; break;
256         case ESCHER_LineMediumLenArrow: pArrowLength = "medium"; break;
257         case ESCHER_LineLongArrow:      pArrowLength = "long"; break;
258     }
259 
260     if ( pArrowLength )
261         pAttrList->add( nElement, pArrowLength );
262 }
263 
impl_AddArrowWidth(sax_fastparser::FastAttributeList * pAttrList,sal_Int32 nElement,sal_uInt32 nValue)264 static void impl_AddArrowWidth( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue )
265 {
266     if ( !pAttrList )
267         return;
268 
269     const char *pArrowWidth = nullptr;
270     switch ( nValue )
271     {
272         case ESCHER_LineNarrowArrow:      pArrowWidth = "narrow"; break;
273         case ESCHER_LineMediumWidthArrow: pArrowWidth = "medium"; break;
274         case ESCHER_LineWideArrow:        pArrowWidth = "wide"; break;
275     }
276 
277     if ( pArrowWidth )
278         pAttrList->add( nElement, pArrowWidth );
279 }
280 
impl_AddBool(sax_fastparser::FastAttributeList * pAttrList,sal_Int32 nElement,bool bValue)281 static void impl_AddBool( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, bool bValue )
282 {
283     if ( !pAttrList )
284         return;
285 
286     pAttrList->add( nElement, bValue? "t": "f" );
287 }
288 
impl_AddColor(sax_fastparser::FastAttributeList * pAttrList,sal_Int32 nElement,sal_uInt32 nColor)289 static void impl_AddColor( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nColor )
290 {
291 #if OSL_DEBUG_LEVEL > 0
292     if ( nColor & 0xFF000000 )
293         fprintf( stderr, "TODO: this is not a RGB value!\n" );
294 #endif
295 
296     if ( !pAttrList || ( nColor & 0xFF000000 ) )
297         return;
298 
299     nColor = ( ( nColor & 0xFF ) << 16 ) + ( nColor & 0xFF00 ) + ( ( nColor & 0xFF0000 ) >> 16 );
300 
301     const char *pColor = nullptr;
302     char pRgbColor[10];
303     switch ( nColor )
304     {
305         case 0x000000: pColor = "black"; break;
306         case 0xC0C0C0: pColor = "silver"; break;
307         case 0x808080: pColor = "gray"; break;
308         case 0xFFFFFF: pColor = "white"; break;
309         case 0x800000: pColor = "maroon"; break;
310         case 0xFF0000: pColor = "red"; break;
311         case 0x800080: pColor = "purple"; break;
312         case 0xFF00FF: pColor = "fuchsia"; break;
313         case 0x008000: pColor = "green"; break;
314         case 0x00FF00: pColor = "lime"; break;
315         case 0x808000: pColor = "olive"; break;
316         case 0xFFFF00: pColor = "yellow"; break;
317         case 0x000080: pColor = "navy"; break;
318         case 0x0000FF: pColor = "blue"; break;
319         case 0x008080: pColor = "teal"; break;
320         case 0x00FFFF: pColor = "aqua"; break;
321         default:
322             {
323                 snprintf( pRgbColor, sizeof( pRgbColor ), "#%06x", static_cast< unsigned int >( nColor ) ); // not too handy to use OString::valueOf() here :-(
324                 pColor = pRgbColor;
325             }
326             break;
327     }
328 
329     pAttrList->add( nElement, pColor );
330 }
331 
impl_AddInt(sax_fastparser::FastAttributeList * pAttrList,sal_Int32 nElement,sal_uInt32 nValue)332 static void impl_AddInt( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue )
333 {
334     if ( !pAttrList )
335         return;
336 
337     pAttrList->add( nElement, OString::number( nValue ).getStr() );
338 }
339 
impl_GetUInt16(const sal_uInt8 * & pVal)340 static sal_uInt16 impl_GetUInt16( const sal_uInt8* &pVal )
341 {
342     sal_uInt16 nRet = *pVal++;
343     nRet += ( *pVal++ ) << 8;
344     return nRet;
345 }
346 
impl_GetPointComponent(const sal_uInt8 * & pVal,sal_uInt16 nPointSize)347 static sal_Int32 impl_GetPointComponent( const sal_uInt8* &pVal, sal_uInt16 nPointSize )
348 {
349     sal_Int32 nRet = 0;
350     if ( ( nPointSize == 0xfff0 ) || ( nPointSize == 4 ) )
351     {
352         sal_uInt16 nUnsigned = *pVal++;
353         nUnsigned += ( *pVal++ ) << 8;
354 
355         nRet = sal_Int16( nUnsigned );
356     }
357     else if ( nPointSize == 8 )
358     {
359         sal_uInt32 nUnsigned = *pVal++;
360         nUnsigned += ( *pVal++ ) << 8;
361         nUnsigned += ( *pVal++ ) << 16;
362         nUnsigned += ( *pVal++ ) << 24;
363 
364         nRet = nUnsigned;
365     }
366 
367     return nRet;
368 }
369 
AddSdrObjectVMLObject(const SdrObject & rObj)370 void  VMLExport::AddSdrObjectVMLObject( const SdrObject& rObj)
371 {
372    m_pSdrObject = &rObj;
373 }
Commit(EscherPropertyContainer & rProps,const tools::Rectangle & rRect)374 void VMLExport::Commit( EscherPropertyContainer& rProps, const tools::Rectangle& rRect )
375 {
376     if ( m_nShapeType == ESCHER_ShpInst_Nil )
377         return;
378 
379     // postpone the output of the embedded elements so that they are written
380     // inside the shapes
381     m_pSerializer->mark(Tag_Commit);
382 
383     // dimensions
384     if ( m_nShapeType == ESCHER_ShpInst_Line )
385         AddLineDimensions( rRect );
386     else
387     {
388         if ( IsWaterMarkShape( m_pSdrObject->GetName() ) )
389         {
390             // Watermark need some padding to be compatible with MSO
391             long nPaddingY = 0;
392             const SfxItemSet& rSet = m_pSdrObject->GetMergedItemSet();
393             if ( const SdrMetricItem* pItem = rSet.GetItem( SDRATTR_TEXT_UPPERDIST ) )
394                 nPaddingY += pItem->GetValue();
395 
396             tools::Rectangle aRect( rRect );
397             aRect.setHeight( aRect.getHeight() + nPaddingY );
398             AddRectangleDimensions( m_ShapeStyle, aRect );
399         }
400         else
401             AddRectangleDimensions( m_ShapeStyle, rRect );
402     }
403 
404     // properties
405     bool bAlreadyWritten[ 0xFFF ] = {};
406     const EscherProperties &rOpts = rProps.GetOpts();
407     for (auto const& opt : rOpts)
408     {
409         sal_uInt16 nId = ( opt.nPropId & 0x0FFF );
410 
411         if ( bAlreadyWritten[ nId ] )
412             continue;
413 
414         switch ( nId )
415         {
416             case ESCHER_Prop_WrapText: // 133
417                 {
418                     const char *pWrapType = nullptr;
419                     switch ( opt.nPropValue )
420                     {
421                         case ESCHER_WrapSquare:
422                         case ESCHER_WrapByPoints:  pWrapType = "square"; break; // these two are equivalent according to the docu
423                         case ESCHER_WrapNone:      pWrapType = "none"; break;
424                         case ESCHER_WrapTopBottom: pWrapType = "topAndBottom"; break;
425                         case ESCHER_WrapThrough:   pWrapType = "through"; break;
426                     }
427                     if ( pWrapType )
428                         m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, pWrapType);
429                 }
430                 bAlreadyWritten[ ESCHER_Prop_WrapText ] = true;
431                 break;
432 
433             // coordorigin
434             case ESCHER_Prop_geoLeft: // 320
435             case ESCHER_Prop_geoTop: // 321
436                 {
437                     sal_uInt32 nLeft = 0, nTop = 0;
438 
439                     if ( nId == ESCHER_Prop_geoLeft )
440                     {
441                         nLeft = opt.nPropValue;
442                         rProps.GetOpt( ESCHER_Prop_geoTop, nTop );
443                     }
444                     else
445                     {
446                         nTop = opt.nPropValue;
447                         rProps.GetOpt( ESCHER_Prop_geoLeft, nLeft );
448                     }
449                     if(nTop!=0 && nLeft!=0)
450                         m_pShapeAttrList->add( XML_coordorigin,
451                                 OStringBuffer( 20 ).append( sal_Int32( nLeft ) )
452                                 .append( "," ).append( sal_Int32( nTop ) )
453                                 .makeStringAndClear() );
454                 }
455                 bAlreadyWritten[ ESCHER_Prop_geoLeft ] = true;
456                 bAlreadyWritten[ ESCHER_Prop_geoTop ] = true;
457                 break;
458 
459             // coordsize
460             case ESCHER_Prop_geoRight: // 322
461             case ESCHER_Prop_geoBottom: // 323
462                 {
463                     sal_uInt32 nLeft = 0, nRight = 0, nTop = 0, nBottom = 0;
464                     rProps.GetOpt( ESCHER_Prop_geoLeft, nLeft );
465                     rProps.GetOpt( ESCHER_Prop_geoTop, nTop );
466 
467                     if ( nId == ESCHER_Prop_geoRight )
468                     {
469                         nRight = opt.nPropValue;
470                         rProps.GetOpt( ESCHER_Prop_geoBottom, nBottom );
471                     }
472                     else
473                     {
474                         nBottom = opt.nPropValue;
475                         rProps.GetOpt( ESCHER_Prop_geoRight, nRight );
476                     }
477 
478                     if(nTop!=0 && nLeft!=0 &&  nBottom!=0 &&  nRight!=0 )
479                         m_pShapeAttrList->add( XML_coordsize,
480                                 OStringBuffer( 20 ).append( sal_Int32( nRight ) - sal_Int32( nLeft ) )
481                                 .append( "," ).append( sal_Int32( nBottom ) - sal_Int32( nTop ) )
482                                 .makeStringAndClear() );
483                 }
484                 bAlreadyWritten[ ESCHER_Prop_geoRight ] = true;
485                 bAlreadyWritten[ ESCHER_Prop_geoBottom ] = true;
486                 break;
487 
488             case ESCHER_Prop_pVertices: // 325
489             case ESCHER_Prop_pSegmentInfo: // 326
490                 {
491                     EscherPropSortStruct aVertices;
492                     EscherPropSortStruct aSegments;
493 
494                     if ( rProps.GetOpt( ESCHER_Prop_pVertices, aVertices ) &&
495                          rProps.GetOpt( ESCHER_Prop_pSegmentInfo, aSegments ) )
496                     {
497                         const sal_uInt8 *pVerticesIt = aVertices.nProp.data() + 6;
498                         const sal_uInt8 *pSegmentIt = aSegments.nProp.data();
499                         OStringBuffer aPath( 512 );
500 
501                         sal_uInt16 nPointSize = aVertices.nProp[4] + ( aVertices.nProp[5] << 8 );
502 
503                         // number of segments
504                         sal_uInt16 nSegments = impl_GetUInt16( pSegmentIt );
505                         pSegmentIt += 4;
506 
507                         for ( ; nSegments; --nSegments )
508                         {
509                             sal_uInt16 nSeg = impl_GetUInt16( pSegmentIt );
510 
511                             // The segment type is stored in the upper 3 bits
512                             // and segment count is stored in the lower 13
513                             // bits.
514                             unsigned char nSegmentType = (nSeg & 0xE000) >> 13;
515                             unsigned short nSegmentCount = nSeg & 0x03FF;
516 
517                             switch (nSegmentType)
518                             {
519                                 case msopathMoveTo:
520                                 {
521                                     sal_Int32 nX = impl_GetPointComponent( pVerticesIt, nPointSize );
522                                     sal_Int32 nY = impl_GetPointComponent( pVerticesIt, nPointSize );
523                                     if (nX >= 0 && nY >= 0 )
524                                         aPath.append( "m" ).append( nX ).append( "," ).append( nY );
525                                     break;
526                                 }
527                                 case msopathClientEscape:
528                                     break;
529                                 case msopathEscape:
530                                 {
531                                     // If the segment type is msopathEscape, the lower 13 bits are
532                                     // divided in a 5 bit escape code and 8 bit
533                                     // vertex count (not segment count!)
534                                     unsigned char nEscapeCode = (nSegmentCount & 0x1F00) >> 8;
535                                     unsigned char nVertexCount = nSegmentCount & 0x00FF;
536                                     pVerticesIt += nVertexCount;
537 
538                                     switch (nEscapeCode)
539                                     {
540                                         case 0xa: // nofill
541                                             aPath.append( "nf" );
542                                             break;
543                                         case 0xb: // nostroke
544                                             aPath.append( "ns" );
545                                             break;
546                                     }
547 
548                                     break;
549                                 }
550                                 case msopathLineTo:
551                                     for (unsigned short i = 0; i < nSegmentCount; ++i)
552                                     {
553                                         sal_Int32 nX = impl_GetPointComponent( pVerticesIt, nPointSize );
554                                         sal_Int32 nY = impl_GetPointComponent( pVerticesIt, nPointSize );
555                                         aPath.append( "l" ).append( nX ).append( "," ).append( nY );
556                                     }
557                                     break;
558                                 case msopathCurveTo:
559                                     for (unsigned short i = 0; i < nSegmentCount; ++i)
560                                     {
561                                         sal_Int32 nX1 = impl_GetPointComponent( pVerticesIt, nPointSize );
562                                         sal_Int32 nY1 = impl_GetPointComponent( pVerticesIt, nPointSize );
563                                         sal_Int32 nX2 = impl_GetPointComponent( pVerticesIt, nPointSize );
564                                         sal_Int32 nY2 = impl_GetPointComponent( pVerticesIt, nPointSize );
565                                         sal_Int32 nX3 = impl_GetPointComponent( pVerticesIt, nPointSize );
566                                         sal_Int32 nY3 = impl_GetPointComponent( pVerticesIt, nPointSize );
567                                         aPath.append( "c" ).append( nX1 ).append( "," ).append( nY1 ).append( "," )
568                                             .append( nX2 ).append( "," ).append( nY2 ).append( "," )
569                                             .append( nX3 ).append( "," ).append( nY3 );
570                                     }
571                                     break;
572                                 case msopathClose:
573                                     aPath.append( "x" );
574                                     break;
575                                 case msopathEnd:
576                                     aPath.append( "e" );
577                                     break;
578                                 default:
579                                     SAL_WARN("oox", "Totally b0rked");
580                                     break;
581                                 case msopathInvalid:
582                                     SAL_WARN("oox", "Invalid - should never be found");
583                                     break;
584                             }
585                         }
586                         OString pathString = aPath.makeStringAndClear();
587                         if ( !aPath.isEmpty() && pathString != "xe" )
588                             m_pShapeAttrList->add( XML_path, pathString );
589                     }
590                     else
591                         SAL_WARN("oox.vml", "unhandled shape path, missing either pVertices or pSegmentInfo.");
592                 }
593                 bAlreadyWritten[ ESCHER_Prop_pVertices ] = true;
594                 bAlreadyWritten[ ESCHER_Prop_pSegmentInfo ] = true;
595                 break;
596 
597             case ESCHER_Prop_fillType: // 384
598             case ESCHER_Prop_fillColor: // 385
599             case ESCHER_Prop_fillBackColor: // 387
600             case ESCHER_Prop_fillBlip: // 390
601             case ESCHER_Prop_fNoFillHitTest: // 447
602             case ESCHER_Prop_fillOpacity: // 386
603                 {
604                     sal_uInt32 nValue;
605                     sax_fastparser::FastAttributeList* pAttrList
606                         = FastSerializerHelper::createAttrList();
607 
608                     bool imageData = false;
609                     EscherPropSortStruct aStruct;
610                     const SdrGrafObj* pSdrGrafObj = dynamic_cast<const SdrGrafObj*>(m_pSdrObject);
611 
612                     if (pSdrGrafObj && pSdrGrafObj->isSignatureLine() && m_pTextExport)
613                     {
614                         sax_fastparser::FastAttributeList* pAttrListSignatureLine
615                             = FastSerializerHelper::createAttrList();
616                         pAttrListSignatureLine->add(XML_issignatureline, "t");
617                         if (!pSdrGrafObj->getSignatureLineId().isEmpty())
618                         {
619                             pAttrListSignatureLine->add(
620                                 XML_id, OUStringToOString(pSdrGrafObj->getSignatureLineId(),
621                                                           RTL_TEXTENCODING_UTF8));
622                         }
623                         if (!pSdrGrafObj->getSignatureLineSuggestedSignerName().isEmpty())
624                         {
625                             pAttrListSignatureLine->add(
626                                 FSNS(XML_o, XML_suggestedsigner),
627                                 OUStringToOString(
628                                     pSdrGrafObj->getSignatureLineSuggestedSignerName(),
629                                     RTL_TEXTENCODING_UTF8));
630                         }
631                         if (!pSdrGrafObj->getSignatureLineSuggestedSignerTitle().isEmpty())
632                         {
633                             pAttrListSignatureLine->add(
634                                 FSNS(XML_o, XML_suggestedsigner2),
635                                 OUStringToOString(
636                                     pSdrGrafObj->getSignatureLineSuggestedSignerTitle(),
637                                     RTL_TEXTENCODING_UTF8));
638                         }
639                         if (!pSdrGrafObj->getSignatureLineSuggestedSignerEmail().isEmpty())
640                         {
641                             pAttrListSignatureLine->add(
642                                 FSNS(XML_o, XML_suggestedsigneremail),
643                                 OUStringToOString(
644                                     pSdrGrafObj->getSignatureLineSuggestedSignerEmail(),
645                                     RTL_TEXTENCODING_UTF8));
646                         }
647                         if (!pSdrGrafObj->getSignatureLineSigningInstructions().isEmpty())
648                         {
649                             pAttrListSignatureLine->add(XML_signinginstructionsset, "t");
650                             pAttrListSignatureLine->add(
651                                 FSNS(XML_o, XML_signinginstructions),
652                                 OUStringToOString(
653                                     pSdrGrafObj->getSignatureLineSigningInstructions(),
654                                     RTL_TEXTENCODING_UTF8));
655                         }
656                         pAttrListSignatureLine->add(
657                             XML_showsigndate,
658                             pSdrGrafObj->isSignatureLineShowSignDate() ? "t" : "f");
659                         pAttrListSignatureLine->add(
660                             XML_allowcomments,
661                             pSdrGrafObj->isSignatureLineCanAddComment() ? "t" : "f");
662 
663                         m_pSerializer->singleElementNS(
664                             XML_o, XML_signatureline,
665                             XFastAttributeListRef(pAttrListSignatureLine));
666 
667                         // Get signature line graphic
668                         const uno::Reference<graphic::XGraphic>& xGraphic
669                             = pSdrGrafObj->getSignatureLineUnsignedGraphic();
670                         Graphic aGraphic(xGraphic);
671 
672                         BitmapChecksum nChecksum = aGraphic.GetChecksum();
673                         OUString aImageId = m_pTextExport->FindRelId(nChecksum);
674                         if (aImageId.isEmpty())
675                         {
676                             aImageId = m_pTextExport->GetDrawingML().WriteImage(aGraphic);
677                             m_pTextExport->CacheRelId(nChecksum, aImageId);
678                         }
679                         pAttrList->add(FSNS(XML_r, XML_id),
680                                        OUStringToOString(aImageId, RTL_TEXTENCODING_UTF8));
681                         imageData = true;
682                     }
683                     else if (rProps.GetOpt(ESCHER_Prop_fillBlip, aStruct) && m_pTextExport)
684                     {
685                         SvMemoryStream aStream;
686                         // The first bytes are WW8-specific, we're only interested in the PNG
687                         int nHeaderSize = 25;
688                         aStream.WriteBytes(aStruct.nProp.data() + nHeaderSize,
689                                            aStruct.nProp.size() - nHeaderSize);
690                         aStream.Seek(0);
691                         Graphic aGraphic;
692                         GraphicConverter::Import(aStream, aGraphic);
693 
694                         BitmapChecksum nChecksum = aGraphic.GetChecksum();
695                         OUString aImageId = m_pTextExport->FindRelId(nChecksum);
696                         if (aImageId.isEmpty())
697                         {
698                             aImageId = m_pTextExport->GetDrawingML().WriteImage(aGraphic);
699                             m_pTextExport->CacheRelId(nChecksum, aImageId);
700                         }
701                         pAttrList->add(FSNS(XML_r, XML_id),
702                                        OUStringToOString(aImageId, RTL_TEXTENCODING_UTF8));
703                         imageData = true;
704                     }
705 
706                     if (rProps.GetOpt(ESCHER_Prop_fNoFillHitTest, nValue))
707                         impl_AddBool(pAttrList, FSNS(XML_o, XML_detectmouseclick), nValue != 0);
708 
709                     if (imageData)
710                         m_pSerializer->singleElementNS( XML_v, XML_imagedata, XFastAttributeListRef( pAttrList ) );
711                     else
712                     {
713                         if ( rProps.GetOpt( ESCHER_Prop_fillType, nValue ) )
714                         {
715                             const char *pFillType = nullptr;
716                             switch ( nValue )
717                             {
718                                 case ESCHER_FillSolid:       pFillType = "solid"; break;
719                                 // TODO case ESCHER_FillPattern:     pFillType = ""; break;
720                                 case ESCHER_FillTexture:     pFillType = "tile"; break;
721                                 // TODO case ESCHER_FillPicture:     pFillType = ""; break;
722                                 // TODO case ESCHER_FillShade:       pFillType = ""; break;
723                                 // TODO case ESCHER_FillShadeCenter: pFillType = ""; break;
724                                 // TODO case ESCHER_FillShadeShape:  pFillType = ""; break;
725                                 // TODO case ESCHER_FillShadeScale:  pFillType = ""; break;
726                                 // TODO case ESCHER_FillShadeTitle:  pFillType = ""; break;
727                                 // TODO case ESCHER_FillBackground:  pFillType = ""; break;
728                                 default:
729                                     SAL_INFO("oox.vml", "Unhandled fill type: " << nValue);
730                                     break;
731                             }
732                             if ( pFillType )
733                                 pAttrList->add( XML_type, pFillType );
734                         }
735                         else if (!rProps.GetOpt(ESCHER_Prop_fillColor, nValue))
736                             pAttrList->add( XML_on, "false" );
737 
738                         if ( rProps.GetOpt( ESCHER_Prop_fillColor, nValue ) )
739                             impl_AddColor( m_pShapeAttrList, XML_fillcolor, nValue );
740 
741                         if ( rProps.GetOpt( ESCHER_Prop_fillBackColor, nValue ) )
742                             impl_AddColor( pAttrList, XML_color2, nValue );
743 
744 
745                         if (rProps.GetOpt(ESCHER_Prop_fillOpacity, nValue))
746                             // Partly undo the transformation at the end of EscherPropertyContainer::CreateFillProperties(): VML opacity is 0..1.
747                             pAttrList->add(XML_opacity, OString::number(double((nValue * 100) >> 16) / 100));
748                         m_pSerializer->singleElementNS( XML_v, XML_fill, XFastAttributeListRef( pAttrList ) );
749 
750                     }
751                 }
752                 bAlreadyWritten[ ESCHER_Prop_fillType ] = true;
753                 bAlreadyWritten[ ESCHER_Prop_fillColor ] = true;
754                 bAlreadyWritten[ ESCHER_Prop_fillBackColor ] = true;
755                 bAlreadyWritten[ ESCHER_Prop_fillBlip ] = true;
756                 bAlreadyWritten[ ESCHER_Prop_fNoFillHitTest ] = true;
757                 bAlreadyWritten[ ESCHER_Prop_fillOpacity ] = true;
758                 break;
759 
760             case ESCHER_Prop_lineColor: // 448
761             case ESCHER_Prop_lineWidth: // 459
762             case ESCHER_Prop_lineDashing: // 462
763             case ESCHER_Prop_lineStartArrowhead: // 464
764             case ESCHER_Prop_lineEndArrowhead: // 465
765             case ESCHER_Prop_lineStartArrowWidth: // 466
766             case ESCHER_Prop_lineStartArrowLength: // 467
767             case ESCHER_Prop_lineEndArrowWidth: // 468
768             case ESCHER_Prop_lineEndArrowLength: // 469
769             case ESCHER_Prop_lineJoinStyle: // 470
770             case ESCHER_Prop_lineEndCapStyle: // 471
771                 {
772                     sal_uInt32 nValue;
773                     sax_fastparser::FastAttributeList *pAttrList = FastSerializerHelper::createAttrList();
774 
775                     if ( rProps.GetOpt( ESCHER_Prop_lineColor, nValue ) )
776                         impl_AddColor( pAttrList, XML_color, nValue );
777 
778                     if ( rProps.GetOpt( ESCHER_Prop_lineWidth, nValue ) )
779                         impl_AddInt( pAttrList, XML_weight, nValue );
780 
781                     if ( rProps.GetOpt( ESCHER_Prop_lineDashing, nValue ) )
782                     {
783                         const char *pDashStyle = nullptr;
784                         switch ( nValue )
785                         {
786                             case ESCHER_LineSolid:             pDashStyle = "solid"; break;
787                             case ESCHER_LineDashSys:           pDashStyle = "shortdash"; break;
788                             case ESCHER_LineDotSys:            pDashStyle = "shortdot"; break;
789                             case ESCHER_LineDashDotSys:        pDashStyle = "shortdashdot"; break;
790                             case ESCHER_LineDashDotDotSys:     pDashStyle = "shortdashdotdot"; break;
791                             case ESCHER_LineDotGEL:            pDashStyle = "dot"; break;
792                             case ESCHER_LineDashGEL:           pDashStyle = "dash"; break;
793                             case ESCHER_LineLongDashGEL:       pDashStyle = "longdash"; break;
794                             case ESCHER_LineDashDotGEL:        pDashStyle = "dashdot"; break;
795                             case ESCHER_LineLongDashDotGEL:    pDashStyle = "longdashdot"; break;
796                             case ESCHER_LineLongDashDotDotGEL: pDashStyle = "longdashdotdot"; break;
797                         }
798                         if ( pDashStyle )
799                             pAttrList->add( XML_dashstyle, pDashStyle );
800                     }
801 
802                     if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowhead, nValue ) )
803                         impl_AddArrowHead( pAttrList, XML_startarrow, nValue );
804 
805                     if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowhead, nValue ) )
806                         impl_AddArrowHead( pAttrList, XML_endarrow, nValue );
807 
808                     if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowWidth, nValue ) )
809                         impl_AddArrowWidth( pAttrList, XML_startarrowwidth, nValue );
810 
811                     if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowLength, nValue ) )
812                         impl_AddArrowLength( pAttrList, XML_startarrowlength, nValue );
813 
814                     if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowWidth, nValue ) )
815                         impl_AddArrowWidth( pAttrList, XML_endarrowwidth, nValue );
816 
817                     if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowLength, nValue ) )
818                         impl_AddArrowLength( pAttrList, XML_endarrowlength, nValue );
819 
820                     if ( rProps.GetOpt( ESCHER_Prop_lineJoinStyle, nValue ) )
821                     {
822                         const char *pJoinStyle = nullptr;
823                         switch ( nValue )
824                         {
825                             case ESCHER_LineJoinBevel: pJoinStyle = "bevel"; break;
826                             case ESCHER_LineJoinMiter: pJoinStyle = "miter"; break;
827                             case ESCHER_LineJoinRound: pJoinStyle = "round"; break;
828                         }
829                         if ( pJoinStyle )
830                             pAttrList->add( XML_joinstyle, pJoinStyle );
831                     }
832 
833                     if ( rProps.GetOpt( ESCHER_Prop_lineEndCapStyle, nValue ) )
834                     {
835                         const char *pEndCap = nullptr;
836                         switch ( nValue )
837                         {
838                             case ESCHER_LineEndCapRound:  pEndCap = "round"; break;
839                             case ESCHER_LineEndCapSquare: pEndCap = "square"; break;
840                             case ESCHER_LineEndCapFlat:   pEndCap = "flat"; break;
841                         }
842                         if ( pEndCap )
843                             pAttrList->add( XML_endcap, pEndCap );
844                     }
845 
846                     m_pSerializer->singleElementNS( XML_v, XML_stroke, XFastAttributeListRef( pAttrList ) );
847                 }
848                 bAlreadyWritten[ ESCHER_Prop_lineColor ] = true;
849                 bAlreadyWritten[ ESCHER_Prop_lineWidth ] = true;
850                 bAlreadyWritten[ ESCHER_Prop_lineDashing ] = true;
851                 bAlreadyWritten[ ESCHER_Prop_lineStartArrowhead ] = true;
852                 bAlreadyWritten[ ESCHER_Prop_lineEndArrowhead ] = true;
853                 bAlreadyWritten[ ESCHER_Prop_lineStartArrowWidth ] = true;
854                 bAlreadyWritten[ ESCHER_Prop_lineStartArrowLength ] = true;
855                 bAlreadyWritten[ ESCHER_Prop_lineEndArrowWidth ] = true;
856                 bAlreadyWritten[ ESCHER_Prop_lineEndArrowLength ] = true;
857                 bAlreadyWritten[ ESCHER_Prop_lineJoinStyle ] = true;
858                 bAlreadyWritten[ ESCHER_Prop_lineEndCapStyle ] = true;
859                 break;
860 
861             case ESCHER_Prop_fHidden:
862                 if ( !opt.nPropValue )
863                     m_ShapeStyle.append( ";visibility:hidden" );
864                 break;
865             case ESCHER_Prop_shadowColor:
866             case ESCHER_Prop_fshadowObscured:
867                 {
868                     sal_uInt32 nValue = 0;
869                     bool bShadow = false;
870                     bool bObscured = false;
871                     if ( rProps.GetOpt( ESCHER_Prop_fshadowObscured, nValue ) )
872                     {
873                         bShadow = (( nValue & 0x20002 ) == 0x20002 );
874                         bObscured = (( nValue & 0x10001 ) == 0x10001 );
875                     }
876                     if ( bShadow )
877                     {
878                         sax_fastparser::FastAttributeList *pAttrList = FastSerializerHelper::createAttrList();
879                         impl_AddBool( pAttrList, XML_on, bShadow );
880                         impl_AddBool( pAttrList, XML_obscured, bObscured );
881 
882                         if ( rProps.GetOpt( ESCHER_Prop_shadowColor, nValue ) )
883                             impl_AddColor( pAttrList, XML_color, nValue );
884 
885                         m_pSerializer->singleElementNS( XML_v, XML_shadow, XFastAttributeListRef( pAttrList ) );
886                         bAlreadyWritten[ ESCHER_Prop_fshadowObscured ] = true;
887                         bAlreadyWritten[ ESCHER_Prop_shadowColor ] = true;
888                     }
889                 }
890                 break;
891             case ESCHER_Prop_gtextUNICODE:
892             case ESCHER_Prop_gtextFont:
893                 {
894                     EscherPropSortStruct aUnicode;
895                     if (rProps.GetOpt(ESCHER_Prop_gtextUNICODE, aUnicode))
896                     {
897                         SvMemoryStream aStream;
898 
899                         if(!opt.nProp.empty())
900                         {
901                             aStream.WriteBytes(opt.nProp.data(), opt.nProp.size());
902                         }
903 
904                         aStream.Seek(0);
905                         OUString aTextPathString = SvxMSDffManager::MSDFFReadZString(aStream, opt.nProp.size(), true);
906                         aStream.Seek(0);
907 
908                         m_pSerializer->singleElementNS(XML_v, XML_path, XML_textpathok, "t");
909 
910                         sax_fastparser::FastAttributeList* pAttrList = FastSerializerHelper::createAttrList();
911                         pAttrList->add(XML_on, "t");
912                         pAttrList->add(XML_fitshape, "t");
913                         pAttrList->add(XML_string, OUStringToOString(aTextPathString, RTL_TEXTENCODING_UTF8));
914                         EscherPropSortStruct aFont;
915                         OUString aStyle;
916                         if (rProps.GetOpt(ESCHER_Prop_gtextFont, aFont))
917                         {
918                             aStream.WriteBytes(aFont.nProp.data(), aFont.nProp.size());
919                             aStream.Seek(0);
920                             OUString aTextPathFont = SvxMSDffManager::MSDFFReadZString(aStream, aFont.nProp.size(), true);
921                             aStyle += "font-family:\"" + aTextPathFont + "\"";
922                         }
923                         sal_uInt32 nSize;
924                         if (rProps.GetOpt(ESCHER_Prop_gtextSize, nSize))
925                         {
926                             float nSizeF = static_cast<sal_Int32>(nSize) / 65536.0;
927                             OUString aSize = OUString::number(nSizeF);
928                             aStyle += ";font-size:" + aSize + "pt";
929                         }
930                         if (IsWaterMarkShape(m_pSdrObject->GetName()))
931                             pAttrList->add(XML_trim, "t");
932 
933                         if (!aStyle.isEmpty())
934                             pAttrList->add(XML_style, OUStringToOString(aStyle, RTL_TEXTENCODING_UTF8));
935                         m_pSerializer->singleElementNS(XML_v, XML_textpath, XFastAttributeListRef(pAttrList));
936                     }
937 
938                     bAlreadyWritten[ESCHER_Prop_gtextUNICODE] = true;
939                     bAlreadyWritten[ESCHER_Prop_gtextFont] = true;
940                 }
941                 break;
942             case ESCHER_Prop_Rotation:
943                 {
944                     // The higher half of the variable contains the angle.
945                     m_ShapeStyle.append(";rotation:").append(double(opt.nPropValue >> 16));
946                     bAlreadyWritten[ESCHER_Prop_Rotation] = true;
947                 }
948                 break;
949             case ESCHER_Prop_fNoLineDrawDash:
950                 {
951                     // See DffPropertyReader::ApplyLineAttributes().
952                     impl_AddBool( m_pShapeAttrList, XML_stroked, (opt.nPropValue & 8) != 0 );
953                     bAlreadyWritten[ESCHER_Prop_fNoLineDrawDash] = true;
954                 }
955                 break;
956             case ESCHER_Prop_wzName:
957                 {
958                     SvMemoryStream aStream;
959 
960                     if(!opt.nProp.empty())
961                     {
962                         aStream.WriteBytes(opt.nProp.data(), opt.nProp.size());
963                     }
964 
965                     aStream.Seek(0);
966                     OUString idStr = SvxMSDffManager::MSDFFReadZString(aStream, opt.nProp.size(), true);
967                     aStream.Seek(0);
968                     if (!IsWaterMarkShape(m_pSdrObject->GetName()) && !m_bSkipwzName)
969                          m_pShapeAttrList->add(XML_ID, OUStringToOString(idStr, RTL_TEXTENCODING_UTF8).getStr());
970 
971                     bAlreadyWritten[ESCHER_Prop_wzName] = true;
972                 }
973                 break;
974             default:
975 #if OSL_DEBUG_LEVEL > 0
976                 const size_t opt_nProp_size(opt.nProp.size());
977                 const sal_uInt8 opt_nProp_empty(0);
978                 fprintf( stderr, "TODO VMLExport::Commit(), unimplemented id: %d, value: %" SAL_PRIuUINT32 ", data: [%zu, %p]\n",
979                         nId,
980                         opt.nPropValue,
981                         opt_nProp_size,
982                         0 == opt_nProp_size ? &opt_nProp_empty : opt.nProp.data());
983                 if ( opt.nProp.size() )
984                 {
985                     const sal_uInt8 *pIt = opt.nProp.data();
986                     fprintf( stderr, "    ( " );
987                     for ( int nCount = opt.nProp.size(); nCount; --nCount )
988                     {
989                         fprintf( stderr, "%02x ", *pIt );
990                         ++pIt;
991                     }
992                     fprintf( stderr, ")\n" );
993                 }
994 #endif
995                 break;
996         }
997     }
998 
999     m_pSerializer->mergeTopMarks(Tag_Commit, sax_fastparser::MergeMarks::POSTPONE );
1000 }
1001 
ShapeIdString(sal_uInt32 nId)1002 OString VMLExport::ShapeIdString( sal_uInt32 nId )
1003 {
1004     if(m_bOverrideShapeIdGeneration)
1005         return m_sShapeIDPrefix + OString::number( nId );
1006     else
1007         return "shape_" + OString::number( nId );
1008 }
1009 
AddFlipXY()1010 void VMLExport::AddFlipXY( )
1011 {
1012     if (m_nShapeFlags & (ShapeFlag::FlipH | ShapeFlag::FlipV))
1013     {
1014         m_ShapeStyle.append( ";flip:" );
1015 
1016         if (m_nShapeFlags & ShapeFlag::FlipH)
1017             m_ShapeStyle.append( "x" );
1018 
1019         if (m_nShapeFlags & ShapeFlag::FlipV)
1020             m_ShapeStyle.append( "y" );
1021     }
1022 }
1023 
AddLineDimensions(const tools::Rectangle & rRectangle)1024 void VMLExport::AddLineDimensions( const tools::Rectangle& rRectangle )
1025 {
1026     // style
1027     if (!m_ShapeStyle.isEmpty())
1028         m_ShapeStyle.append( ";" );
1029 
1030     m_ShapeStyle.append( "position:absolute" );
1031 
1032     AddFlipXY();
1033 
1034     // the actual dimensions
1035     OString aLeft, aTop, aRight, aBottom;
1036 
1037     if ( mnGroupLevel == 1 )
1038     {
1039         const OString aPt( "pt" );
1040         aLeft = OString::number( double( rRectangle.Left() ) / 20 ) + aPt;
1041         aTop = OString::number( double( rRectangle.Top() ) / 20 ) + aPt;
1042         aRight = OString::number( double( rRectangle.Right() ) / 20 ) + aPt;
1043         aBottom = OString::number( double( rRectangle.Bottom() ) / 20 ) + aPt;
1044     }
1045     else
1046     {
1047         aLeft = OString::number( rRectangle.Left() );
1048         aTop = OString::number( rRectangle.Top() );
1049         aRight = OString::number( rRectangle.Right() );
1050         aBottom = OString::number( rRectangle.Bottom() );
1051     }
1052 
1053     m_pShapeAttrList->add( XML_from,
1054             OStringBuffer( 20 ).append( aLeft )
1055             .append( "," ).append( aTop )
1056             .makeStringAndClear() );
1057 
1058     m_pShapeAttrList->add( XML_to,
1059             OStringBuffer( 20 ).append( aRight )
1060             .append( "," ).append( aBottom )
1061             .makeStringAndClear() );
1062 }
1063 
AddRectangleDimensions(OStringBuffer & rBuffer,const tools::Rectangle & rRectangle,bool rbAbsolutePos)1064 void VMLExport::AddRectangleDimensions( OStringBuffer& rBuffer, const tools::Rectangle& rRectangle, bool rbAbsolutePos)
1065 {
1066     if ( !rBuffer.isEmpty() )
1067         rBuffer.append( ";" );
1068 
1069     if (rbAbsolutePos && !m_bInline)
1070     {
1071         rBuffer.append( "position:absolute;" );
1072     }
1073 
1074     if(m_bInline)
1075     {
1076         rBuffer.append( "width:" ).append( double( rRectangle.Right() - rRectangle.Left() ) / 20 )
1077             .append( "pt;height:" ).append( double( rRectangle.Bottom() - rRectangle.Top() ) / 20 )
1078             .append( "pt" );
1079     }
1080     else if ( mnGroupLevel == 1 )
1081     {
1082         rBuffer.append( "margin-left:" ).append( double( rRectangle.Left() ) / 20 )
1083             .append( "pt;margin-top:" ).append( double( rRectangle.Top() ) / 20 )
1084             .append( "pt;width:" ).append( double( rRectangle.Right() - rRectangle.Left() ) / 20 )
1085             .append( "pt;height:" ).append( double( rRectangle.Bottom() - rRectangle.Top() ) / 20 )
1086             .append( "pt" );
1087     }
1088     else
1089     {
1090         rBuffer.append( "left:" ).append( rRectangle.Left() )
1091             .append( ";top:" ).append( rRectangle.Top() )
1092             .append( ";width:" ).append( rRectangle.Right() - rRectangle.Left() )
1093             .append( ";height:" ).append( rRectangle.Bottom() - rRectangle.Top() );
1094     }
1095 
1096     AddFlipXY();
1097 }
1098 
AddShapeAttribute(sal_Int32 nAttribute,const OString & rValue)1099 void VMLExport::AddShapeAttribute( sal_Int32 nAttribute, const OString& rValue )
1100 {
1101     m_pShapeAttrList->add( nAttribute, rValue );
1102 }
1103 
lcl_getShapeTypes()1104 static std::vector<OString> lcl_getShapeTypes()
1105 {
1106     std::vector<OString> aRet;
1107 
1108     OUString aPath("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/filter/vml-shape-types");
1109     rtl::Bootstrap::expandMacros(aPath);
1110     SvFileStream aStream(aPath, StreamMode::READ);
1111     if (aStream.GetError() != ERRCODE_NONE)
1112         SAL_WARN("oox", "failed to open vml-shape-types");
1113     OString aLine;
1114     bool bNotDone = aStream.ReadLine(aLine);
1115     while (bNotDone)
1116     {
1117         // Filter out comments.
1118         if (!aLine.startsWith("/"))
1119             aRet.push_back(aLine);
1120         bNotDone = aStream.ReadLine(aLine);
1121     }
1122     return aRet;
1123 }
1124 
lcl_isTextBox(const SdrObject * pSdrObject)1125 static bool lcl_isTextBox(const SdrObject* pSdrObject)
1126 {
1127     uno::Reference<beans::XPropertySet> xPropertySet(const_cast<SdrObject*>(pSdrObject)->getUnoShape(), uno::UNO_QUERY);
1128     if (xPropertySet.is())
1129     {
1130         uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
1131         return xPropertySetInfo->hasPropertyByName("TextBox") && xPropertySet->getPropertyValue("TextBox").get<bool>();
1132     }
1133     return false;
1134 }
1135 
lcl_getAnchorIdFromGrabBag(const SdrObject * pSdrObject)1136 static OUString lcl_getAnchorIdFromGrabBag(const SdrObject* pSdrObject)
1137 {
1138     OUString aResult;
1139 
1140     uno::Reference<beans::XPropertySet> xShape(const_cast<SdrObject*>(pSdrObject)->getUnoShape(), uno::UNO_QUERY);
1141     if (xShape->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"))
1142     {
1143         comphelper::SequenceAsHashMap aInteropGrabBag(xShape->getPropertyValue("InteropGrabBag"));
1144         if (aInteropGrabBag.find("AnchorId") != aInteropGrabBag.end())
1145             aInteropGrabBag["AnchorId"] >>= aResult;
1146     }
1147 
1148     return aResult;
1149 }
1150 
GenerateShapeId()1151 sal_uInt32 VMLExport::GenerateShapeId()
1152 {
1153     if(!m_bOverrideShapeIdGeneration)
1154         return EscherEx::GenerateShapeId();
1155     else
1156         return m_nShapeIDCounter++;
1157 }
1158 
StartShape()1159 sal_Int32 VMLExport::StartShape()
1160 {
1161     if ( m_nShapeType == ESCHER_ShpInst_Nil )
1162         return -1;
1163 
1164     // some of the shapes have their own name ;-)
1165     sal_Int32 nShapeElement = -1;
1166     bool bReferToShapeType = false;
1167     switch ( m_nShapeType )
1168     {
1169         case ESCHER_ShpInst_NotPrimitive:   nShapeElement = XML_shape;     break;
1170         case ESCHER_ShpInst_Rectangle:      nShapeElement = XML_rect;      break;
1171         case ESCHER_ShpInst_RoundRectangle: nShapeElement = XML_roundrect; break;
1172         case ESCHER_ShpInst_Ellipse:        nShapeElement = XML_oval;      break;
1173         case ESCHER_ShpInst_Arc:            nShapeElement = XML_arc;       break;
1174         case ESCHER_ShpInst_Line:           nShapeElement = XML_line;      break;
1175         case ESCHER_ShpInst_HostControl:
1176         {
1177             // We don't have a shape definition for host control in presetShapeDefinitions.xml
1178             // So use a definition copied from DOCX file created with MSO
1179             bReferToShapeType = true;
1180             nShapeElement = XML_shape;
1181             if ( !m_aShapeTypeWritten[ m_nShapeType ] )
1182             {
1183                 OString sShapeType =
1184                     "<v:shapetype id=\"shapetype_" + OString::number(m_nShapeType) +
1185                         "\" coordsize=\"21600,21600\" o:spt=\"" + OString::number(m_nShapeType) +
1186                         "\" path=\"m,l,21600l21600,21600l21600,xe\">\n"
1187                         "<v:stroke joinstyle=\"miter\"/>\n"
1188                         "<v:path shadowok=\"f\" o:extrusionok=\"f\" strokeok=\"f\" fillok=\"f\" o:connecttype=\"rect\"/>\n"
1189                         "<o:lock v:ext=\"edit\" shapetype=\"t\"/>\n"
1190                     "</v:shapetype>";
1191                 m_pSerializer->write(sShapeType);
1192                 m_aShapeTypeWritten[ m_nShapeType ] = true;
1193             }
1194             break;
1195         }
1196         case ESCHER_ShpInst_PictureFrame:
1197         {
1198             // We don't have a shape definition for picture frame in presetShapeDefinitions.xml
1199             // So use a definition copied from DOCX file created with MSO
1200             bReferToShapeType = true;
1201             nShapeElement = XML_shape;
1202             if ( !m_aShapeTypeWritten[ m_nShapeType ] )
1203             {
1204                 OString sShapeType =
1205                     "<v:shapetype id=\"shapetype_" + OString::number(m_nShapeType) +
1206                         "\" coordsize=\"21600,21600\" o:spt=\"" + OString::number(m_nShapeType) +
1207                         "\" o:preferrelative=\"t\" path=\"m@4@5l@4@11@9@11@9@5xe\" filled=\"f\" stroked=\"f\">\n"
1208                         "<v:stroke joinstyle=\"miter\"/>\n"
1209                         "<v:formulas>\n"
1210                             "<v:f eqn=\"if lineDrawn pixelLineWidth 0\"/>\n"
1211                             "<v:f eqn=\"sum @0 1 0\"/>\n"
1212                             "<v:f eqn=\"sum 0 0 @1\"/>\n"
1213                             "<v:f eqn=\"prod @2 1 2\"/>\n"
1214                             "<v:f eqn=\"prod @3 21600 pixelWidth\"/>\n"
1215                             "<v:f eqn=\"prod @3 21600 pixelHeight\"/>\n"
1216                             "<v:f eqn=\"sum @0 0 1\"/>\n"
1217                             "<v:f eqn=\"prod @6 1 2\"/>\n"
1218                             "<v:f eqn=\"prod @7 21600 pixelWidth\"/>\n"
1219                             "<v:f eqn=\"sum @8 21600 0\"/>\n"
1220                             "<v:f eqn=\"prod @7 21600 pixelHeight\"/>\n"
1221                             "<v:f eqn=\"sum @10 21600 0\"/>\n"
1222                         "</v:formulas>\n"
1223                         "<v:path o:extrusionok=\"f\" gradientshapeok=\"t\" o:connecttype=\"rect\"/>\n"
1224                         "<o:lock v:ext=\"edit\" aspectratio=\"t\"/>\n"
1225                         "</v:shapetype>";
1226                 m_pSerializer->write(sShapeType);
1227                 m_aShapeTypeWritten[ m_nShapeType ] = true;
1228             }
1229             break;
1230         }
1231         default:
1232             if ( m_nShapeType < ESCHER_ShpInst_COUNT )
1233             {
1234                 nShapeElement = XML_shape;
1235 
1236                 // a predefined shape?
1237                 static std::vector<OString> aShapeTypes = lcl_getShapeTypes();
1238                 OString aShapeType = aShapeTypes[ m_nShapeType ];
1239                 if ( aShapeType != "NULL" )
1240                 {
1241                     bReferToShapeType = true;
1242                     if ( !m_aShapeTypeWritten[ m_nShapeType ] )
1243                     {
1244                         m_pSerializer->write( aShapeType.getStr() );
1245                         m_aShapeTypeWritten[ m_nShapeType ] = true;
1246                     }
1247                 }
1248                 else
1249                 {
1250                     // rectangle is probably the best fallback...
1251                     nShapeElement = XML_rect;
1252                 }
1253             }
1254             break;
1255     }
1256 
1257     // anchoring
1258     switch (m_eHOri)
1259     {
1260         case text::HoriOrientation::LEFT:
1261             m_ShapeStyle.append(";mso-position-horizontal:left");
1262             break;
1263         case text::HoriOrientation::CENTER:
1264             m_ShapeStyle.append(";mso-position-horizontal:center");
1265             break;
1266         case text::HoriOrientation::RIGHT:
1267             m_ShapeStyle.append(";mso-position-horizontal:right");
1268             break;
1269         case text::HoriOrientation::INSIDE:
1270             m_ShapeStyle.append(";mso-position-horizontal:inside");
1271             break;
1272         case text::HoriOrientation::OUTSIDE:
1273             m_ShapeStyle.append(";mso-position-horizontal:outside");
1274             break;
1275         default:
1276         case text::HoriOrientation::NONE:
1277             break;
1278     }
1279     switch (m_eHRel)
1280     {
1281         case text::RelOrientation::PAGE_PRINT_AREA:
1282             m_ShapeStyle.append(";mso-position-horizontal-relative:margin");
1283             break;
1284         case text::RelOrientation::PAGE_FRAME:
1285         case text::RelOrientation::PAGE_LEFT:
1286         case text::RelOrientation::PAGE_RIGHT:
1287             m_ShapeStyle.append(";mso-position-horizontal-relative:page");
1288             break;
1289         case text::RelOrientation::CHAR:
1290             m_ShapeStyle.append(";mso-position-horizontal-relative:char");
1291             break;
1292         default:
1293             break;
1294     }
1295 
1296     switch (m_eVOri)
1297     {
1298         case text::VertOrientation::TOP:
1299         case text::VertOrientation::LINE_TOP:
1300         case text::VertOrientation::CHAR_TOP:
1301             m_ShapeStyle.append(";mso-position-vertical:top");
1302             break;
1303         case text::VertOrientation::CENTER:
1304         case text::VertOrientation::LINE_CENTER:
1305             m_ShapeStyle.append(";mso-position-vertical:center");
1306             break;
1307         case text::VertOrientation::BOTTOM:
1308         case text::VertOrientation::LINE_BOTTOM:
1309         case text::VertOrientation::CHAR_BOTTOM:
1310             m_ShapeStyle.append(";mso-position-vertical:bottom");
1311             break;
1312         default:
1313         case text::VertOrientation::NONE:
1314             break;
1315     }
1316     switch (m_eVRel)
1317     {
1318         case text::RelOrientation::PAGE_PRINT_AREA:
1319             m_ShapeStyle.append(";mso-position-vertical-relative:margin");
1320             break;
1321         case text::RelOrientation::PAGE_FRAME:
1322             m_ShapeStyle.append(";mso-position-vertical-relative:page");
1323             break;
1324         default:
1325             break;
1326     }
1327 
1328     // add style
1329     m_pShapeAttrList->add( XML_style, m_ShapeStyle.makeStringAndClear() );
1330 
1331     OUString sAnchorId = lcl_getAnchorIdFromGrabBag(m_pSdrObject);
1332     if (!sAnchorId.isEmpty())
1333         m_pShapeAttrList->addNS(XML_wp14, XML_anchorId, OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8));
1334 
1335     if ( nShapeElement >= 0 && !m_pShapeAttrList->hasAttribute( XML_type ) && bReferToShapeType )
1336     {
1337         OStringBuffer sTypeBuffer( 20 );
1338         if (m_bUseHashMarkForType)
1339             sTypeBuffer.append("#");
1340         m_pShapeAttrList->add( XML_type, sTypeBuffer
1341                 .append( "shapetype_" ).append( sal_Int32( m_nShapeType ) )
1342                 .makeStringAndClear() );
1343     }
1344 
1345     // start of the shape
1346     m_pSerializer->startElementNS( XML_v, nShapeElement, XFastAttributeListRef( m_pShapeAttrList ) );
1347 
1348     // now check if we have some editeng text (not associated textbox) and we have a text exporter registered
1349     const SdrTextObj* pTxtObj = dynamic_cast<const SdrTextObj*>( m_pSdrObject );
1350     if (pTxtObj && m_pTextExport && msfilter::util::HasTextBoxContent(m_nShapeType) && !IsWaterMarkShape(m_pSdrObject->GetName()) && !lcl_isTextBox(m_pSdrObject))
1351     {
1352         const OutlinerParaObject* pParaObj = nullptr;
1353         bool bOwnParaObj = false;
1354 
1355         /*
1356         #i13885#
1357         When the object is actively being edited, that text is not set into
1358         the objects normal text object, but lives in a separate object.
1359         */
1360         if (pTxtObj->IsTextEditActive())
1361         {
1362             pParaObj = pTxtObj->CreateEditOutlinerParaObject().release();
1363             bOwnParaObj = true;
1364         }
1365         else
1366         {
1367             pParaObj = pTxtObj->GetOutlinerParaObject();
1368         }
1369 
1370         if( pParaObj )
1371         {
1372             // this is reached only in case some text is attached to the shape
1373             m_pSerializer->startElementNS(XML_v, XML_textbox);
1374             m_pTextExport->WriteOutliner(*pParaObj);
1375             m_pSerializer->endElementNS(XML_v, XML_textbox);
1376             if( bOwnParaObj )
1377                 delete pParaObj;
1378         }
1379     }
1380 
1381     return nShapeElement;
1382 }
1383 
EndShape(sal_Int32 nShapeElement)1384 void VMLExport::EndShape( sal_Int32 nShapeElement )
1385 {
1386     if ( nShapeElement >= 0 )
1387     {
1388         if (m_pTextExport && lcl_isTextBox(m_pSdrObject))
1389         {
1390             uno::Reference<beans::XPropertySet> xPropertySet(const_cast<SdrObject*>(m_pSdrObject)->getUnoShape(), uno::UNO_QUERY);
1391             comphelper::SequenceAsHashMap aCustomShapeProperties(xPropertySet->getPropertyValue("CustomShapeGeometry"));
1392             sax_fastparser::FastAttributeList* pTextboxAttrList = FastSerializerHelper::createAttrList();
1393             if (aCustomShapeProperties.find("TextPreRotateAngle") != aCustomShapeProperties.end())
1394             {
1395                 sal_Int32 nTextRotateAngle = aCustomShapeProperties["TextPreRotateAngle"].get<sal_Int32>();
1396                 if (nTextRotateAngle == -270)
1397                     pTextboxAttrList->add(XML_style, "mso-layout-flow-alt:bottom-to-top");
1398             }
1399             sax_fastparser::XFastAttributeListRef xTextboxAttrList(pTextboxAttrList);
1400             pTextboxAttrList = nullptr;
1401             m_pSerializer->startElementNS(XML_v, XML_textbox, xTextboxAttrList);
1402 
1403             m_pTextExport->WriteVMLTextBox(uno::Reference<drawing::XShape>(xPropertySet, uno::UNO_QUERY_THROW));
1404 
1405             m_pSerializer->endElementNS(XML_v, XML_textbox);
1406         }
1407 
1408         // end of the shape
1409         m_pSerializer->endElementNS( XML_v, nShapeElement );
1410     }
1411 }
1412 
AddSdrObject(const SdrObject & rObj,sal_Int16 eHOri,sal_Int16 eVOri,sal_Int16 eHRel,sal_Int16 eVRel,const bool bOOxmlExport)1413 OString const & VMLExport::AddSdrObject( const SdrObject& rObj, sal_Int16 eHOri, sal_Int16 eVOri, sal_Int16 eHRel, sal_Int16 eVRel, const bool bOOxmlExport )
1414 {
1415     m_pSdrObject = &rObj;
1416     m_eHOri = eHOri;
1417     m_eVOri = eVOri;
1418     m_eHRel = eHRel;
1419     m_eVRel = eVRel;
1420     m_bInline = false;
1421     EscherEx::AddSdrObject(rObj, bOOxmlExport);
1422     return m_sShapeId;
1423 }
1424 
AddInlineSdrObject(const SdrObject & rObj,const bool bOOxmlExport)1425 OString const & VMLExport::AddInlineSdrObject( const SdrObject& rObj, const bool bOOxmlExport )
1426 {
1427     m_pSdrObject = &rObj;
1428     m_eHOri = -1;
1429     m_eVOri = -1;
1430     m_eHRel = -1;
1431     m_eVRel = -1;
1432     m_bInline = true;
1433     EscherEx::AddSdrObject(rObj, bOOxmlExport);
1434     return m_sShapeId;
1435 }
1436 
1437 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1438