1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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
)
62 , m_pNdTopLeft( nullptr )
63 , m_pSdrObject( nullptr )
64 , m_pShapeAttrList( nullptr )
65 , m_nShapeType( ESCHER_ShpInst_Nil
)
68 , m_aShapeTypeWritten( ESCHER_ShpInst_COUNT
)
73 void VMLExport::SetFS( const ::sax_fastparser::FSHelperPtr
& pSerializer
)
75 m_pSerializer
= pSerializer
;
78 VMLExport::~VMLExport()
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" );
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
);
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;
140 OUString rEditAs
= EscherEx::GetEditAs();
141 if (!rEditAs
.isEmpty())
143 pAttrList
->add(XML_editas
, OUStringToOString( rEditAs
, RTL_TEXTENCODING_UTF8
));
144 rbAbsolutePos
= false;
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
) );
174 void VMLExport::LeaveGroup()
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
) );
193 // A watermark object - store the optional shape ID
194 m_pShapeAttrList
->add( XML_id
, OUStringToOString(m_pSdrObject
->GetName(), RTL_TEXTENCODING_UTF8
) );
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
)
212 const char *pArrowHead
= nullptr;
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;
224 pAttrList
->add( nElement
, pArrowHead
);
227 static void impl_AddArrowLength( sax_fastparser::FastAttributeList
*pAttrList
, sal_Int32 nElement
, sal_uInt32 nValue
)
232 const char *pArrowLength
= nullptr;
235 case ESCHER_LineShortArrow
: pArrowLength
= "short"; break;
236 case ESCHER_LineMediumLenArrow
: pArrowLength
= "medium"; break;
237 case ESCHER_LineLongArrow
: pArrowLength
= "long"; break;
241 pAttrList
->add( nElement
, pArrowLength
);
244 static void impl_AddArrowWidth( sax_fastparser::FastAttributeList
*pAttrList
, sal_Int32 nElement
, sal_uInt32 nValue
)
249 const char *pArrowWidth
= nullptr;
252 case ESCHER_LineNarrowArrow
: pArrowWidth
= "narrow"; break;
253 case ESCHER_LineMediumWidthArrow
: pArrowWidth
= "medium"; break;
254 case ESCHER_LineWideArrow
: pArrowWidth
= "wide"; break;
258 pAttrList
->add( nElement
, pArrowWidth
);
261 static void impl_AddBool( sax_fastparser::FastAttributeList
*pAttrList
, sal_Int32 nElement
, bool bValue
)
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" );
276 if ( !pAttrList
|| ( nColor
& 0xFF000000 ) )
279 nColor
= ( ( nColor
& 0xFF ) << 16 ) + ( nColor
& 0xFF00 ) + ( ( nColor
& 0xFF0000 ) >> 16 );
281 const char *pColor
= nullptr;
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;
303 snprintf( pRgbColor
, sizeof( pRgbColor
), "#%06x", static_cast< unsigned int >( nColor
) ); // not too handy to use OString::valueOf() here :-(
309 pAttrList
->add( nElement
, pColor
);
312 static void impl_AddInt( sax_fastparser::FastAttributeList
*pAttrList
, sal_Int32 nElement
, sal_uInt32 nValue
)
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;
327 inline sal_Int32
impl_GetPointComponent( const sal_uInt8
* &pVal
, sal_uInt16 nPointSize
)
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;
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
)
359 // postpone the output of the embedded elements so that they are written
361 m_pSerializer
->mark(Tag_Commit
);
364 if ( m_nShapeType
== ESCHER_ShpInst_Line
)
365 AddLineDimensions( rRect
);
367 AddRectangleDimensions( m_ShapeStyle
, rRect
);
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
] )
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;
394 m_pSerializer
->singleElementNS( XML_w10
, XML_wrap
,
398 bAlreadyWritten
[ ESCHER_Prop_WrapText
] = true;
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
);
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;
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
);
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;
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
);
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
482 unsigned char nSegmentType
= (nSeg
& 0xE000) >> 13;
483 unsigned short nSegmentCount
= nSeg
& 0x03FF;
485 switch (nSegmentType
)
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
);
495 case msopathClientEscape
:
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
;
509 aPath
.append( "nf" );
511 case 0xb: // nostroke
512 aPath
.append( "ns" );
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
);
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
);
547 SAL_WARN("oox", "Totally b0rked");
550 SAL_WARN("oox", "Invalid - should never be found");
554 OString pathString
= aPath
.makeStringAndClear();
555 if ( !aPath
.isEmpty() && pathString
!= "xe" )
556 m_pShapeAttrList
->add( XML_path
, pathString
);
558 #if OSL_DEBUG_LEVEL > 0
560 fprintf( stderr
, "TODO: unhandled shape path, missing either pVertices or pSegmentInfo.\n" );
563 bAlreadyWritten
[ ESCHER_Prop_pVertices
] = true;
564 bAlreadyWritten
[ ESCHER_Prop_pSegmentInfo
] = true;
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
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
);
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
));
599 if ( rProps
.GetOpt( ESCHER_Prop_fNoFillHitTest
, nValue
) )
600 impl_AddBool( pAttrList
, FSNS(XML_o
, XML_detectmouseclick
), nValue
!= 0 );
603 m_pSerializer
->singleElementNS( XML_v
, XML_imagedata
, XFastAttributeListRef( pAttrList
) );
606 if ( rProps
.GetOpt( ESCHER_Prop_fillType
, nValue
) )
608 const char *pFillType
= nullptr;
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;
622 #if OSL_DEBUG_LEVEL > 0
623 fprintf( stderr
, "TODO: unhandled fill type\n" );
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;
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
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;
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;
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;
720 case ESCHER_LineJoinBevel
: pJoinStyle
= "bevel"; break;
721 case ESCHER_LineJoinMiter
: pJoinStyle
= "miter"; break;
722 case ESCHER_LineJoinRound
: pJoinStyle
= "round"; break;
725 pAttrList
->add( XML_joinstyle
, pJoinStyle
);
728 if ( rProps
.GetOpt( ESCHER_Prop_lineEndCapStyle
, nValue
) )
730 const char *pEndCap
= nullptr;
733 case ESCHER_LineEndCapRound
: pEndCap
= "round"; break;
734 case ESCHER_LineEndCapSquare
: pEndCap
= "square"; break;
735 case ESCHER_LineEndCapFlat
: pEndCap
= "flat"; break;
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;
756 case ESCHER_Prop_fHidden
:
757 if ( !it
->nPropValue
)
758 m_ShapeStyle
.append( ";visibility:hidden" );
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 );
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;
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
);
795 OUString aTextPathString
= SvxMSDffManager::MSDFFReadZString(aStream
, it
->nPropSize
, true);
798 m_pSerializer
->singleElementNS( XML_v
, XML_path
,
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
;
808 if (rProps
.GetOpt(ESCHER_Prop_gtextFont
, aFont
))
810 aStream
.WriteBytes(aFont
.pBuf
, aFont
.nPropSize
);
812 OUString aTextPathFont
= SvxMSDffManager::MSDFFReadZString(aStream
, aFont
.nPropSize
, true);
813 aStyle
+= "font-family:\"" + aTextPathFont
+ "\"";
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;
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;
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;
845 case ESCHER_Prop_wzName
:
847 SvMemoryStream aStream
;
848 aStream
.WriteBytes(it
->pBuf
, it
->nPropSize
);
850 OUString idStr
= SvxMSDffManager::MSDFFReadZString(aStream
, it
->nPropSize
, true);
852 if (!IsWaterMarkShape(m_pSdrObject
->GetName()))
853 m_pShapeAttrList
->add(XML_ID
, OUStringToOString(idStr
, RTL_TEXTENCODING_UTF8
).getStr());
855 bAlreadyWritten
[ESCHER_Prop_wzName
] = true;
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
);
864 const sal_uInt8
*pIt
= it
->pBuf
;
865 fprintf( stderr
, " ( " );
866 for ( int nCount
= it
->nPropSize
; nCount
; --nCount
)
868 fprintf( stderr
, "%02x ", *pIt
);
871 fprintf( stderr
, ")\n" );
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
)
900 if (!m_ShapeStyle
.isEmpty())
901 m_ShapeStyle
.append( ";" );
903 m_ShapeStyle
.append( "position:absolute" );
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
;
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( ";" );
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 )
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() );
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");
981 bool bNotDone
= aStream
.ReadLine(aLine
);
984 // Filter out comments.
985 if (!aLine
.startsWith("/"))
986 aRet
.push_back(aLine
);
987 bNotDone
= aStream
.ReadLine(aLine
);
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>();
1003 OUString
lcl_getAnchorIdFromGrabBag(const SdrObject
* pSdrObject
)
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
;
1018 sal_Int32
VMLExport::StartShape()
1020 if ( m_nShapeType
== ESCHER_ShpInst_Nil
)
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;
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;
1053 // rectangle is probably the best fallback...
1054 nShapeElement
= XML_rect
;
1063 case text::HoriOrientation::LEFT
:
1064 m_ShapeStyle
.append(";mso-position-horizontal:left");
1066 case text::HoriOrientation::CENTER
:
1067 m_ShapeStyle
.append(";mso-position-horizontal:center");
1069 case text::HoriOrientation::RIGHT
:
1070 m_ShapeStyle
.append(";mso-position-horizontal:right");
1072 case text::HoriOrientation::INSIDE
:
1073 m_ShapeStyle
.append(";mso-position-horizontal:inside");
1075 case text::HoriOrientation::OUTSIDE
:
1076 m_ShapeStyle
.append(";mso-position-horizontal:outside");
1079 case text::HoriOrientation::NONE
:
1084 case text::RelOrientation::PAGE_PRINT_AREA
:
1085 m_ShapeStyle
.append(";mso-position-horizontal-relative:margin");
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");
1092 case text::RelOrientation::CHAR
:
1093 m_ShapeStyle
.append(";mso-position-horizontal-relative:char");
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");
1106 case text::VertOrientation::CENTER
:
1107 case text::VertOrientation::LINE_CENTER
:
1108 m_ShapeStyle
.append(";mso-position-vertical:center");
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");
1116 case text::VertOrientation::NONE
:
1121 case text::RelOrientation::PAGE_PRINT_AREA
:
1122 m_ShapeStyle
.append(";mso-position-vertical-relative:margin");
1124 case text::RelOrientation::PAGE_FRAME
:
1125 m_ShapeStyle
.append(";mso-position-vertical-relative:page");
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;
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();
1167 pParaObj
= pTxtObj
->GetOutlinerParaObject();
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
);
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
);
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
;
1220 m_pNdTopLeft
= pNdTopLeft
;
1221 EscherEx::AddSdrObject(rObj
, bOOxmlExport
);
1224 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */