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 <svl/itemset.hxx>
23 #include <oox/export/drawingml.hxx>
24 #include <oox/export/vmlexport.hxx>
25 #include <sax/fastattribs.hxx>
27 #include <oox/token/tokens.hxx>
29 #include <rtl/strbuf.hxx>
30 #include <rtl/ustring.hxx>
31 #include <sal/log.hxx>
33 #include <tools/stream.hxx>
34 #include <comphelper/sequenceashashmap.hxx>
35 #include <svx/msdffdef.hxx>
36 #include <svx/svdotext.hxx>
37 #include <svx/svdograf.hxx>
38 #include <svx/sdmetitm.hxx>
40 #include <vcl/cvtgrf.hxx>
41 #include <filter/msfilter/msdffimp.hxx>
42 #include <filter/msfilter/util.hxx>
43 #include <filter/msfilter/escherex.hxx>
44 #include <o3tl/string_view.hxx>
45 #include <drawingml/fontworkhelpers.hxx>
47 #include <com/sun/star/beans/XPropertySet.hpp>
48 #include <com/sun/star/beans/XPropertySetInfo.hpp>
49 #include <com/sun/star/drawing/XShape.hpp>
50 #include <com/sun/star/text/HoriOrientation.hpp>
51 #include <com/sun/star/text/VertOrientation.hpp>
52 #include <com/sun/star/text/RelOrientation.hpp>
53 #include <com/sun/star/text/WritingMode2.hpp>
54 #include <com/sun/star/text/XTextFrame.hpp>
58 using namespace sax_fastparser
;
59 using namespace oox::vml
;
60 using namespace com::sun::star
;
62 const sal_Int32 Tag_Container
= 44444;
63 const sal_Int32 Tag_Commit
= 44445;
65 VMLExport::VMLExport( ::sax_fastparser::FSHelperPtr pSerializer
, VMLTextExport
* pTextExport
)
66 : EscherEx( std::make_shared
<EscherExGlobal
>(), nullptr, /*bOOXML=*/true )
67 , m_pSerializer(std::move( pSerializer
))
68 , m_pTextExport( pTextExport
)
74 , m_pSdrObject( nullptr )
75 , m_nShapeType( ESCHER_ShpInst_Nil
)
76 , m_nShapeFlags(ShapeFlag::NONE
)
78 , m_aShapeTypeWritten( ESCHER_ShpInst_COUNT
)
79 , m_bSkipwzName( false )
80 , m_bUseHashMarkForType( false )
81 , m_bOverrideShapeIdGeneration( false )
82 , m_nShapeIDCounter( 0 )
87 void VMLExport::SetFS( const ::sax_fastparser::FSHelperPtr
& pSerializer
)
89 m_pSerializer
= pSerializer
;
92 VMLExport::~VMLExport()
96 void VMLExport::OpenContainer( sal_uInt16 nEscherContainer
, int nRecInstance
)
98 EscherEx::OpenContainer( nEscherContainer
, nRecInstance
);
100 if ( nEscherContainer
!= ESCHER_SpContainer
)
103 // opening a shape container
104 SAL_WARN_IF(m_nShapeType
!= ESCHER_ShpInst_Nil
, "oox.vml", "opening shape inside of a shape!");
105 m_nShapeType
= ESCHER_ShpInst_Nil
;
106 m_pShapeAttrList
= FastSerializerHelper::createAttrList();
108 m_ShapeStyle
.setLength(0);
109 m_ShapeStyle
.ensureCapacity(200);
111 // postpone the output so that we are able to write even the elements
112 // that we learn inside Commit()
113 m_pSerializer
->mark(Tag_Container
);
116 void VMLExport::CloseContainer()
118 if ( mRecTypes
.back() == ESCHER_SpContainer
)
120 // write the shape now when we have all the info
121 sal_Int32 nShapeElement
= StartShape();
123 m_pSerializer
->mergeTopMarks(Tag_Container
);
125 EndShape( nShapeElement
);
128 m_nShapeType
= ESCHER_ShpInst_Nil
;
129 m_pShapeAttrList
= nullptr;
132 EscherEx::CloseContainer();
135 sal_uInt32
VMLExport::EnterGroup( const OUString
& rShapeName
, const tools::Rectangle
* pRect
)
137 sal_uInt32 nShapeId
= GenerateShapeId();
139 OStringBuffer
aStyle( 200 );
140 rtl::Reference
<FastAttributeList
> pAttrList
= FastSerializerHelper::createAttrList();
142 pAttrList
->add( XML_id
, ShapeIdString( nShapeId
) );
144 if ( rShapeName
.getLength() )
145 pAttrList
->add( XML_alt
, rShapeName
);
147 bool rbAbsolutePos
= true;
149 OUString rEditAs
= EscherEx::GetEditAs();
150 if (!rEditAs
.isEmpty())
152 pAttrList
->add(XML_editas
, rEditAs
);
153 rbAbsolutePos
= false;
158 AddRectangleDimensions( aStyle
, *pRect
, rbAbsolutePos
);
160 if ( !aStyle
.isEmpty() )
161 pAttrList
->add( XML_style
, aStyle
);
163 // coordorigin/coordsize
164 if ( pRect
&& ( mnGroupLevel
== 1 ) )
166 pAttrList
->add( XML_coordorigin
,
167 OString::number( pRect
->Left() ) + "," + OString::number( pRect
->Top() ) );
169 pAttrList
->add( XML_coordsize
,
170 OString::number( pRect
->Right() - pRect
->Left() ) + "," +
171 OString::number( pRect
->Bottom() - pRect
->Top() ) );
174 m_pSerializer
->startElementNS( XML_v
, XML_group
, pAttrList
);
180 void VMLExport::LeaveGroup()
183 m_pSerializer
->endElementNS( XML_v
, XML_group
);
186 void VMLExport::AddShape( sal_uInt32 nShapeType
, ShapeFlag nShapeFlags
, sal_uInt32 nShapeId
)
188 m_nShapeType
= nShapeType
;
189 m_nShapeFlags
= nShapeFlags
;
191 m_sShapeId
= ShapeIdString( nShapeId
);
192 if (m_sShapeId
.startsWith("_x0000_"))
194 // xml_id must be set elsewhere. The id is critical for matching VBA macros etc,
195 // and the spid is critical to link to the shape number elsewhere.
196 m_pShapeAttrList
->addNS( XML_o
, XML_spid
, m_sShapeId
);
198 else if (IsWaterMarkShape(m_pSdrObject
->GetName()))
200 // Shape is a watermark object - keep the original shape's name
201 // because Microsoft detects if it is a watermark by the actual name
202 m_pShapeAttrList
->add( XML_id
, m_pSdrObject
->GetName() );
204 m_pShapeAttrList
->addNS( XML_o
, XML_spid
, m_sShapeId
);
208 m_pShapeAttrList
->add(XML_id
, m_sShapeId
);
212 bool VMLExport::IsWaterMarkShape(std::u16string_view rStr
)
214 if (rStr
.empty() ) return false;
216 return o3tl::starts_with(rStr
, u
"PowerPlusWaterMarkObject") || o3tl::starts_with(rStr
, u
"WordPictureWatermark");
219 void VMLExport::OverrideShapeIDGen(bool bOverrideShapeIdGen
, const OString
& sShapeIDPrefix
)
221 m_bOverrideShapeIdGeneration
= bOverrideShapeIdGen
;
222 if(bOverrideShapeIdGen
)
224 assert(!sShapeIDPrefix
.isEmpty());
225 m_sShapeIDPrefix
= sShapeIDPrefix
;
228 m_sShapeIDPrefix
.clear();
231 static void impl_AddArrowHead( sax_fastparser::FastAttributeList
*pAttrList
, sal_Int32 nElement
, sal_uInt32 nValue
)
236 const char *pArrowHead
= nullptr;
239 case ESCHER_LineNoEnd
: pArrowHead
= "none"; break;
240 case ESCHER_LineArrowEnd
: pArrowHead
= "block"; break;
241 case ESCHER_LineArrowStealthEnd
: pArrowHead
= "classic"; break;
242 case ESCHER_LineArrowDiamondEnd
: pArrowHead
= "diamond"; break;
243 case ESCHER_LineArrowOvalEnd
: pArrowHead
= "oval"; break;
244 case ESCHER_LineArrowOpenEnd
: pArrowHead
= "open"; break;
248 pAttrList
->add( nElement
, pArrowHead
);
251 static void impl_AddArrowLength( sax_fastparser::FastAttributeList
*pAttrList
, sal_Int32 nElement
, sal_uInt32 nValue
)
256 const char *pArrowLength
= nullptr;
259 case ESCHER_LineShortArrow
: pArrowLength
= "short"; break;
260 case ESCHER_LineMediumLenArrow
: pArrowLength
= "medium"; break;
261 case ESCHER_LineLongArrow
: pArrowLength
= "long"; break;
265 pAttrList
->add( nElement
, pArrowLength
);
268 static void impl_AddArrowWidth( sax_fastparser::FastAttributeList
*pAttrList
, sal_Int32 nElement
, sal_uInt32 nValue
)
273 const char *pArrowWidth
= nullptr;
276 case ESCHER_LineNarrowArrow
: pArrowWidth
= "narrow"; break;
277 case ESCHER_LineMediumWidthArrow
: pArrowWidth
= "medium"; break;
278 case ESCHER_LineWideArrow
: pArrowWidth
= "wide"; break;
282 pAttrList
->add( nElement
, pArrowWidth
);
285 static void impl_AddBool( sax_fastparser::FastAttributeList
*pAttrList
, sal_Int32 nElement
, bool bValue
)
290 pAttrList
->add( nElement
, bValue
? "t": "f" );
293 static void impl_AddColor( sax_fastparser::FastAttributeList
*pAttrList
, sal_Int32 nElement
, sal_uInt32 nColor
)
295 SAL_WARN_IF( nColor
& 0xFF000000 , "oox.vml" , "TODO: this is not a RGB value!");
297 if ( !pAttrList
|| ( nColor
& 0xFF000000 ) )
300 nColor
= ( ( nColor
& 0xFF ) << 16 ) + ( nColor
& 0xFF00 ) + ( ( nColor
& 0xFF0000 ) >> 16 );
302 const char *pColor
= nullptr;
306 case 0x000000: pColor
= "black"; break;
307 case 0xC0C0C0: pColor
= "silver"; break;
308 case 0x808080: pColor
= "gray"; break;
309 case 0xFFFFFF: pColor
= "white"; break;
310 case 0x800000: pColor
= "maroon"; break;
311 case 0xFF0000: pColor
= "red"; break;
312 case 0x800080: pColor
= "purple"; break;
313 case 0xFF00FF: pColor
= "fuchsia"; break;
314 case 0x008000: pColor
= "green"; break;
315 case 0x00FF00: pColor
= "lime"; break;
316 case 0x808000: pColor
= "olive"; break;
317 case 0xFFFF00: pColor
= "yellow"; break;
318 case 0x000080: pColor
= "navy"; break;
319 case 0x0000FF: pColor
= "blue"; break;
320 case 0x008080: pColor
= "teal"; break;
321 case 0x00FFFF: pColor
= "aqua"; break;
324 snprintf( pRgbColor
, sizeof( pRgbColor
), "#%06x", static_cast< unsigned int >( nColor
) ); // not too handy to use OString::valueOf() here :-(
330 pAttrList
->add( nElement
, pColor
);
333 static void impl_AddInt( sax_fastparser::FastAttributeList
*pAttrList
, sal_Int32 nElement
, sal_uInt32 nValue
)
338 pAttrList
->add( nElement
, OString::number( nValue
) );
341 static sal_uInt16
impl_GetUInt16( const sal_uInt8
* &pVal
)
343 sal_uInt16 nRet
= *pVal
++;
344 nRet
+= ( *pVal
++ ) << 8;
348 static sal_Int32
impl_GetPointComponent( const sal_uInt8
* &pVal
, sal_uInt16 nPointSize
)
351 if ( ( nPointSize
== 0xfff0 ) || ( nPointSize
== 4 ) )
353 sal_uInt16 nUnsigned
= *pVal
++;
354 nUnsigned
+= ( *pVal
++ ) << 8;
356 nRet
= sal_Int16( nUnsigned
);
358 else if ( nPointSize
== 8 )
360 sal_uInt32 nUnsigned
= *pVal
++;
361 nUnsigned
+= ( *pVal
++ ) << 8;
362 nUnsigned
+= ( *pVal
++ ) << 16;
363 nUnsigned
+= ( *pVal
++ ) << 24;
371 void VMLExport::AddSdrObjectVMLObject( const SdrObject
& rObj
)
373 m_pSdrObject
= &rObj
;
375 void VMLExport::Commit( EscherPropertyContainer
& rProps
, const tools::Rectangle
& rRect
)
377 if ( m_nShapeType
== ESCHER_ShpInst_Nil
)
380 // postpone the output of the embedded elements so that they are written
382 m_pSerializer
->mark(Tag_Commit
);
385 if ( m_nShapeType
== ESCHER_ShpInst_Line
)
386 AddLineDimensions( rRect
);
389 if ( IsWaterMarkShape( m_pSdrObject
->GetName() ) )
391 // Watermark need some padding to be compatible with MSO
392 tools::Long nPaddingY
= 0;
393 const SfxItemSet
& rSet
= m_pSdrObject
->GetMergedItemSet();
394 if ( const SdrMetricItem
* pItem
= rSet
.GetItem( SDRATTR_TEXT_UPPERDIST
) )
395 nPaddingY
+= pItem
->GetValue();
397 tools::Rectangle
aRect( rRect
);
398 aRect
.setHeight( aRect
.getOpenHeight() + nPaddingY
);
399 AddRectangleDimensions( m_ShapeStyle
, aRect
);
402 AddRectangleDimensions( m_ShapeStyle
, rRect
);
406 // The numbers of defines ESCHER_Prop_foo and DFF_Prop_foo correspond to the PIDs in
407 // 'Microsoft Office Drawing 97-2007 Binary Format Specification'.
408 // The property values are set by EscherPropertyContainer::CreateCustomShapeProperties() method.
409 bool bAlreadyWritten
[ 0xFFF ] = {};
410 const EscherProperties
&rOpts
= rProps
.GetOpts();
411 for (auto const& opt
: rOpts
)
413 sal_uInt16 nId
= ( opt
.nPropId
& 0x0FFF );
415 if ( bAlreadyWritten
[ nId
] )
420 case ESCHER_Prop_WrapText
: // 133
422 const char *pWrapType
= nullptr;
423 switch ( opt
.nPropValue
)
425 case ESCHER_WrapSquare
:
426 case ESCHER_WrapByPoints
: pWrapType
= "square"; break; // these two are equivalent according to the docu
427 case ESCHER_WrapNone
: pWrapType
= "none"; break;
428 case ESCHER_WrapTopBottom
:
429 case ESCHER_WrapThrough
:
430 break; // last two are *undefined* in MS-ODRAW, don't exist in VML
434 m_ShapeStyle
.append(";mso-wrap-style:");
435 m_ShapeStyle
.append(pWrapType
);
438 bAlreadyWritten
[ ESCHER_Prop_WrapText
] = true;
441 case ESCHER_Prop_AnchorText
: // 135
443 char const* pValue(nullptr);
444 switch (opt
.nPropValue
)
446 case ESCHER_AnchorTop
:
449 case ESCHER_AnchorMiddle
:
452 case ESCHER_AnchorBottom
:
455 case ESCHER_AnchorTopCentered
:
456 pValue
= "top-center";
458 case ESCHER_AnchorMiddleCentered
:
459 pValue
= "middle-center";
461 case ESCHER_AnchorBottomCentered
:
462 pValue
= "bottom-center";
464 case ESCHER_AnchorTopBaseline
:
465 pValue
= "top-baseline";
467 case ESCHER_AnchorBottomBaseline
:
468 pValue
= "bottom-baseline";
470 case ESCHER_AnchorTopCenteredBaseline
:
471 pValue
= "top-center-baseline";
473 case ESCHER_AnchorBottomCenteredBaseline
:
474 pValue
= "bottom-center-baseline";
477 m_ShapeStyle
.append(";v-text-anchor:");
478 m_ShapeStyle
.append(pValue
);
482 case ESCHER_Prop_txflTextFlow
: // 136
484 // at least "bottom-to-top" only has an effect when it's on the v:textbox element, not on v:shape
485 assert(m_TextboxStyle
.isEmpty());
486 switch (opt
.nPropValue
)
488 case ESCHER_txflHorzN
:
489 m_TextboxStyle
.append("layout-flow:horizontal");
491 case ESCHER_txflTtoBA
:
492 m_TextboxStyle
.append("layout-flow:vertical");
494 case ESCHER_txflBtoT
:
495 m_TextboxStyle
.append("mso-layout-flow-alt:bottom-to-top");
498 assert(false); // unimplemented in escher export
505 case ESCHER_Prop_geoLeft
: // 320
506 case ESCHER_Prop_geoTop
: // 321
508 sal_uInt32 nLeft
= 0, nTop
= 0;
510 if ( nId
== ESCHER_Prop_geoLeft
)
512 nLeft
= opt
.nPropValue
;
513 rProps
.GetOpt( ESCHER_Prop_geoTop
, nTop
);
517 nTop
= opt
.nPropValue
;
518 rProps
.GetOpt( ESCHER_Prop_geoLeft
, nLeft
);
520 if(nTop
!=0 && nLeft
!=0)
521 m_pShapeAttrList
->add( XML_coordorigin
,
522 OString::number( nLeft
) + "," + OString::number( nTop
) );
524 bAlreadyWritten
[ ESCHER_Prop_geoLeft
] = true;
525 bAlreadyWritten
[ ESCHER_Prop_geoTop
] = true;
529 case ESCHER_Prop_geoRight
: // 322
530 case ESCHER_Prop_geoBottom
: // 323
532 sal_uInt32 nLeft
= 0, nRight
= 0, nTop
= 0, nBottom
= 0;
533 rProps
.GetOpt( ESCHER_Prop_geoLeft
, nLeft
);
534 rProps
.GetOpt( ESCHER_Prop_geoTop
, nTop
);
536 if ( nId
== ESCHER_Prop_geoRight
)
538 nRight
= opt
.nPropValue
;
539 rProps
.GetOpt( ESCHER_Prop_geoBottom
, nBottom
);
543 nBottom
= opt
.nPropValue
;
544 rProps
.GetOpt( ESCHER_Prop_geoRight
, nRight
);
547 if(nBottom
!=0 && nRight
!=0 )
548 m_pShapeAttrList
->add( XML_coordsize
,
549 OString::number( nRight
- nLeft
) + "," + OString::number( nBottom
- nTop
) );
551 bAlreadyWritten
[ ESCHER_Prop_geoRight
] = true;
552 bAlreadyWritten
[ ESCHER_Prop_geoBottom
] = true;
555 case ESCHER_Prop_pVertices
: // 325
556 case ESCHER_Prop_pSegmentInfo
: // 326
558 EscherPropSortStruct aVertices
;
559 EscherPropSortStruct aSegments
;
561 if ( rProps
.GetOpt( ESCHER_Prop_pVertices
, aVertices
) &&
562 rProps
.GetOpt( ESCHER_Prop_pSegmentInfo
, aSegments
) )
564 const sal_uInt8
*pVerticesIt
= aVertices
.nProp
.data() + 6;
565 const sal_uInt8
*pSegmentIt
= aSegments
.nProp
.data();
566 OStringBuffer
aPath( 512 );
568 sal_uInt16 nPointSize
= aVertices
.nProp
[4] + ( aVertices
.nProp
[5] << 8 );
570 // number of segments
571 sal_uInt16 nSegments
= impl_GetUInt16( pSegmentIt
);
574 for ( ; nSegments
; --nSegments
)
576 sal_uInt16 nSeg
= impl_GetUInt16( pSegmentIt
);
578 // The segment type is stored in the upper 3 bits
579 // and segment count is stored in the lower 13
581 unsigned char nSegmentType
= (nSeg
& 0xE000) >> 13;
582 unsigned short nSegmentCount
= nSeg
& 0x03FF;
584 switch (nSegmentType
)
588 sal_Int32 nX
= impl_GetPointComponent( pVerticesIt
, nPointSize
);
589 sal_Int32 nY
= impl_GetPointComponent( pVerticesIt
, nPointSize
);
590 if (nX
>= 0 && nY
>= 0 )
591 aPath
.append( "m" + OString::number( nX
) + "," + OString::number( nY
) );
594 case msopathClientEscape
:
598 // If the segment type is msopathEscape, the lower 13 bits are
599 // divided in a 5 bit escape code and 8 bit
600 // vertex count (not segment count!)
601 unsigned char nEscapeCode
= (nSegmentCount
& 0x1F00) >> 8;
602 unsigned char nVertexCount
= nSegmentCount
& 0x00FF;
603 pVerticesIt
+= nVertexCount
;
608 aPath
.append( "nf" );
610 case 0xb: // nostroke
611 aPath
.append( "ns" );
618 for (unsigned short i
= 0; i
< nSegmentCount
; ++i
)
620 sal_Int32 nX
= impl_GetPointComponent( pVerticesIt
, nPointSize
);
621 sal_Int32 nY
= impl_GetPointComponent( pVerticesIt
, nPointSize
);
622 aPath
.append( "l" + OString::number( nX
) + "," + OString::number( nY
) );
626 for (unsigned short i
= 0; i
< nSegmentCount
; ++i
)
628 sal_Int32 nX1
= impl_GetPointComponent( pVerticesIt
, nPointSize
);
629 sal_Int32 nY1
= impl_GetPointComponent( pVerticesIt
, nPointSize
);
630 sal_Int32 nX2
= impl_GetPointComponent( pVerticesIt
, nPointSize
);
631 sal_Int32 nY2
= impl_GetPointComponent( pVerticesIt
, nPointSize
);
632 sal_Int32 nX3
= impl_GetPointComponent( pVerticesIt
, nPointSize
);
633 sal_Int32 nY3
= impl_GetPointComponent( pVerticesIt
, nPointSize
);
634 aPath
.append( "c" + OString::number( nX1
) + "," + OString::number( nY1
) + "," +
635 OString::number( nX2
) + "," + OString::number( nY2
) + "," +
636 OString::number( nX3
) + "," + OString::number( nY3
) );
646 SAL_WARN("oox", "Totally b0rked");
649 SAL_WARN("oox", "Invalid - should never be found");
653 OString pathString
= aPath
.makeStringAndClear();
654 if ( !pathString
.isEmpty() && pathString
!= "xe" )
655 m_pShapeAttrList
->add( XML_path
, pathString
);
658 SAL_WARN("oox.vml", "unhandled shape path, missing either pVertices or pSegmentInfo.");
660 bAlreadyWritten
[ ESCHER_Prop_pVertices
] = true;
661 bAlreadyWritten
[ ESCHER_Prop_pSegmentInfo
] = true;
664 case ESCHER_Prop_fillType
: // 384
665 case ESCHER_Prop_fillColor
: // 385
666 case ESCHER_Prop_fillBackColor
: // 387
667 case ESCHER_Prop_fillBlip
: // 390
668 case ESCHER_Prop_fNoFillHitTest
: // 447
669 case ESCHER_Prop_fillOpacity
: // 386
672 rtl::Reference
<sax_fastparser::FastAttributeList
> pAttrList
673 = FastSerializerHelper::createAttrList();
675 bool imageData
= false;
676 EscherPropSortStruct aStruct
;
677 const SdrGrafObj
* pSdrGrafObj
= dynamic_cast<const SdrGrafObj
*>(m_pSdrObject
);
679 if (pSdrGrafObj
&& pSdrGrafObj
->isSignatureLine() && m_pTextExport
)
681 rtl::Reference
<sax_fastparser::FastAttributeList
> pAttrListSignatureLine
682 = FastSerializerHelper::createAttrList();
683 pAttrListSignatureLine
->add(XML_issignatureline
, "t");
684 if (!pSdrGrafObj
->getSignatureLineId().isEmpty())
686 pAttrListSignatureLine
->add(
687 XML_id
, pSdrGrafObj
->getSignatureLineId());
689 if (!pSdrGrafObj
->getSignatureLineSuggestedSignerName().isEmpty())
691 pAttrListSignatureLine
->add(
692 FSNS(XML_o
, XML_suggestedsigner
),
693 pSdrGrafObj
->getSignatureLineSuggestedSignerName());
695 if (!pSdrGrafObj
->getSignatureLineSuggestedSignerTitle().isEmpty())
697 pAttrListSignatureLine
->add(
698 FSNS(XML_o
, XML_suggestedsigner2
),
699 pSdrGrafObj
->getSignatureLineSuggestedSignerTitle());
701 if (!pSdrGrafObj
->getSignatureLineSuggestedSignerEmail().isEmpty())
703 pAttrListSignatureLine
->add(
704 FSNS(XML_o
, XML_suggestedsigneremail
),
705 pSdrGrafObj
->getSignatureLineSuggestedSignerEmail());
707 if (!pSdrGrafObj
->getSignatureLineSigningInstructions().isEmpty())
709 pAttrListSignatureLine
->add(XML_signinginstructionsset
, "t");
710 pAttrListSignatureLine
->add(
711 FSNS(XML_o
, XML_signinginstructions
),
712 pSdrGrafObj
->getSignatureLineSigningInstructions());
714 pAttrListSignatureLine
->add(
716 pSdrGrafObj
->isSignatureLineShowSignDate() ? "t" : "f");
717 pAttrListSignatureLine
->add(
719 pSdrGrafObj
->isSignatureLineCanAddComment() ? "t" : "f");
721 m_pSerializer
->singleElementNS(
722 XML_o
, XML_signatureline
,
723 pAttrListSignatureLine
);
725 // Get signature line graphic
726 const uno::Reference
<graphic::XGraphic
>& xGraphic
727 = pSdrGrafObj
->getSignatureLineUnsignedGraphic();
728 Graphic
aGraphic(xGraphic
);
729 OUString aImageId
= m_pTextExport
->GetDrawingML().writeGraphicToStorage(aGraphic
, false);
730 pAttrList
->add(FSNS(XML_r
, XML_id
), aImageId
);
733 else if (rProps
.GetOpt(ESCHER_Prop_fillBlip
, aStruct
) && m_pTextExport
)
735 SvMemoryStream aStream
;
736 // The first bytes are WW8-specific, we're only interested in the PNG
737 int nHeaderSize
= 25;
738 aStream
.WriteBytes(aStruct
.nProp
.data() + nHeaderSize
,
739 aStruct
.nProp
.size() - nHeaderSize
);
742 GraphicConverter::Import(aStream
, aGraphic
);
743 OUString aImageId
= m_pTextExport
->GetDrawingML().writeGraphicToStorage(aGraphic
, false);
744 if (!aImageId
.isEmpty())
746 pAttrList
->add(FSNS(XML_r
, XML_id
), aImageId
);
751 if (rProps
.GetOpt(ESCHER_Prop_fNoFillHitTest
, nValue
))
752 impl_AddBool(pAttrList
.get(), FSNS(XML_o
, XML_detectmouseclick
), nValue
!= 0);
754 if (imageData
&& ((pSdrGrafObj
&& pSdrGrafObj
->isSignatureLine())
755 || m_nShapeType
== ESCHER_ShpInst_PictureFrame
))
756 m_pSerializer
->singleElementNS( XML_v
, XML_imagedata
, pAttrList
);
759 if ( rProps
.GetOpt( ESCHER_Prop_fillType
, nValue
) )
761 const char *pFillType
= nullptr;
764 case ESCHER_FillSolid
: pFillType
= "solid"; break;
765 // TODO case ESCHER_FillPattern: pFillType = ""; break;
766 case ESCHER_FillTexture
: pFillType
= "tile"; break;
767 case ESCHER_FillPicture
: pFillType
= "frame"; break;
768 // TODO case ESCHER_FillShade: pFillType = ""; break;
769 // TODO case ESCHER_FillShadeCenter: pFillType = ""; break;
770 // TODO case ESCHER_FillShadeShape: pFillType = ""; break;
771 // TODO case ESCHER_FillShadeScale: pFillType = ""; break;
772 // TODO case ESCHER_FillShadeTitle: pFillType = ""; break;
773 // TODO case ESCHER_FillBackground: pFillType = ""; break;
775 SAL_INFO("oox.vml", "Unhandled fill type: " << nValue
);
779 pAttrList
->add( XML_type
, pFillType
);
781 else if (!rProps
.GetOpt(ESCHER_Prop_fillColor
, nValue
))
782 pAttrList
->add( XML_on
, "false" );
784 if ( rProps
.GetOpt( ESCHER_Prop_fillColor
, nValue
) )
785 impl_AddColor( m_pShapeAttrList
.get(), XML_fillcolor
, nValue
);
787 if ( rProps
.GetOpt( ESCHER_Prop_fillBackColor
, nValue
) )
788 impl_AddColor( pAttrList
.get(), XML_color2
, nValue
);
790 if (rProps
.GetOpt(ESCHER_Prop_fillOpacity
, nValue
))
791 // Partly undo the transformation at the end of EscherPropertyContainer::CreateFillProperties(): VML opacity is 0..1.
792 pAttrList
->add(XML_opacity
, OString::number(double((nValue
* 100) >> 16) / 100));
793 m_pSerializer
->singleElementNS( XML_v
, XML_fill
, pAttrList
);
797 bAlreadyWritten
[ ESCHER_Prop_fillType
] = true;
798 bAlreadyWritten
[ ESCHER_Prop_fillColor
] = true;
799 bAlreadyWritten
[ ESCHER_Prop_fillBackColor
] = true;
800 bAlreadyWritten
[ ESCHER_Prop_fillBlip
] = true;
801 bAlreadyWritten
[ ESCHER_Prop_fNoFillHitTest
] = true;
802 bAlreadyWritten
[ ESCHER_Prop_fillOpacity
] = true;
805 case ESCHER_Prop_lineColor
: // 448
806 case ESCHER_Prop_lineWidth
: // 459
807 case ESCHER_Prop_lineDashing
: // 462
808 case ESCHER_Prop_lineStartArrowhead
: // 464
809 case ESCHER_Prop_lineEndArrowhead
: // 465
810 case ESCHER_Prop_lineStartArrowWidth
: // 466
811 case ESCHER_Prop_lineStartArrowLength
: // 467
812 case ESCHER_Prop_lineEndArrowWidth
: // 468
813 case ESCHER_Prop_lineEndArrowLength
: // 469
814 case ESCHER_Prop_lineJoinStyle
: // 470
815 case ESCHER_Prop_lineEndCapStyle
: // 471
818 rtl::Reference
<sax_fastparser::FastAttributeList
> pAttrList
= FastSerializerHelper::createAttrList();
820 if ( rProps
.GetOpt( ESCHER_Prop_lineColor
, nValue
) )
821 impl_AddColor( pAttrList
.get(), XML_color
, nValue
);
823 if ( rProps
.GetOpt( ESCHER_Prop_lineWidth
, nValue
) )
824 impl_AddInt( pAttrList
.get(), XML_weight
, nValue
);
826 if ( rProps
.GetOpt( ESCHER_Prop_lineDashing
, nValue
) )
828 const char *pDashStyle
= nullptr;
831 case ESCHER_LineSolid
: pDashStyle
= "solid"; break;
832 case ESCHER_LineDashSys
: pDashStyle
= "shortdash"; break;
833 case ESCHER_LineDotSys
: pDashStyle
= "shortdot"; break;
834 case ESCHER_LineDashDotSys
: pDashStyle
= "shortdashdot"; break;
835 case ESCHER_LineDashDotDotSys
: pDashStyle
= "shortdashdotdot"; break;
836 case ESCHER_LineDotGEL
: pDashStyle
= "dot"; break;
837 case ESCHER_LineDashGEL
: pDashStyle
= "dash"; break;
838 case ESCHER_LineLongDashGEL
: pDashStyle
= "longdash"; break;
839 case ESCHER_LineDashDotGEL
: pDashStyle
= "dashdot"; break;
840 case ESCHER_LineLongDashDotGEL
: pDashStyle
= "longdashdot"; break;
841 case ESCHER_LineLongDashDotDotGEL
: pDashStyle
= "longdashdotdot"; break;
844 pAttrList
->add( XML_dashstyle
, pDashStyle
);
847 if ( rProps
.GetOpt( ESCHER_Prop_lineStartArrowhead
, nValue
) )
848 impl_AddArrowHead( pAttrList
.get(), XML_startarrow
, nValue
);
850 if ( rProps
.GetOpt( ESCHER_Prop_lineEndArrowhead
, nValue
) )
851 impl_AddArrowHead( pAttrList
.get(), XML_endarrow
, nValue
);
853 if ( rProps
.GetOpt( ESCHER_Prop_lineStartArrowWidth
, nValue
) )
854 impl_AddArrowWidth( pAttrList
.get(), XML_startarrowwidth
, nValue
);
856 if ( rProps
.GetOpt( ESCHER_Prop_lineStartArrowLength
, nValue
) )
857 impl_AddArrowLength( pAttrList
.get(), XML_startarrowlength
, nValue
);
859 if ( rProps
.GetOpt( ESCHER_Prop_lineEndArrowWidth
, nValue
) )
860 impl_AddArrowWidth( pAttrList
.get(), XML_endarrowwidth
, nValue
);
862 if ( rProps
.GetOpt( ESCHER_Prop_lineEndArrowLength
, nValue
) )
863 impl_AddArrowLength( pAttrList
.get(), XML_endarrowlength
, nValue
);
865 if ( rProps
.GetOpt( ESCHER_Prop_lineJoinStyle
, nValue
) )
867 const char *pJoinStyle
= nullptr;
870 case ESCHER_LineJoinBevel
: pJoinStyle
= "bevel"; break;
871 case ESCHER_LineJoinMiter
: pJoinStyle
= "miter"; break;
872 case ESCHER_LineJoinRound
: pJoinStyle
= "round"; break;
875 pAttrList
->add( XML_joinstyle
, pJoinStyle
);
878 if ( rProps
.GetOpt( ESCHER_Prop_lineEndCapStyle
, nValue
) )
880 const char *pEndCap
= nullptr;
883 case ESCHER_LineEndCapRound
: pEndCap
= "round"; break;
884 case ESCHER_LineEndCapSquare
: pEndCap
= "square"; break;
885 case ESCHER_LineEndCapFlat
: pEndCap
= "flat"; break;
888 pAttrList
->add( XML_endcap
, pEndCap
);
891 m_pSerializer
->singleElementNS( XML_v
, XML_stroke
, pAttrList
);
893 bAlreadyWritten
[ ESCHER_Prop_lineColor
] = true;
894 bAlreadyWritten
[ ESCHER_Prop_lineWidth
] = true;
895 bAlreadyWritten
[ ESCHER_Prop_lineDashing
] = true;
896 bAlreadyWritten
[ ESCHER_Prop_lineStartArrowhead
] = true;
897 bAlreadyWritten
[ ESCHER_Prop_lineEndArrowhead
] = true;
898 bAlreadyWritten
[ ESCHER_Prop_lineStartArrowWidth
] = true;
899 bAlreadyWritten
[ ESCHER_Prop_lineStartArrowLength
] = true;
900 bAlreadyWritten
[ ESCHER_Prop_lineEndArrowWidth
] = true;
901 bAlreadyWritten
[ ESCHER_Prop_lineEndArrowLength
] = true;
902 bAlreadyWritten
[ ESCHER_Prop_lineJoinStyle
] = true;
903 bAlreadyWritten
[ ESCHER_Prop_lineEndCapStyle
] = true;
906 case ESCHER_Prop_fHidden
:
907 if ( !opt
.nPropValue
)
908 m_ShapeStyle
.append( ";visibility:hidden" );
910 case ESCHER_Prop_shadowColor
:
911 case ESCHER_Prop_fshadowObscured
:
913 sal_uInt32 nValue
= 0;
914 bool bShadow
= false;
915 bool bObscured
= false;
916 if ( rProps
.GetOpt( ESCHER_Prop_fshadowObscured
, nValue
) )
918 bShadow
= (( nValue
& 0x20002 ) == 0x20002 );
919 bObscured
= (( nValue
& 0x10001 ) == 0x10001 );
923 rtl::Reference
<sax_fastparser::FastAttributeList
> pAttrList
= FastSerializerHelper::createAttrList();
924 impl_AddBool( pAttrList
.get(), XML_on
, bShadow
);
925 impl_AddBool( pAttrList
.get(), XML_obscured
, bObscured
);
927 if ( rProps
.GetOpt( ESCHER_Prop_shadowColor
, nValue
) )
928 impl_AddColor( pAttrList
.get(), XML_color
, nValue
);
930 m_pSerializer
->singleElementNS( XML_v
, XML_shadow
, pAttrList
);
931 bAlreadyWritten
[ ESCHER_Prop_fshadowObscured
] = true;
932 bAlreadyWritten
[ ESCHER_Prop_shadowColor
] = true;
936 case ESCHER_Prop_gtextUNICODE
:
937 case ESCHER_Prop_gtextFont
:
939 EscherPropSortStruct aUnicode
;
940 if (rProps
.GetOpt(ESCHER_Prop_gtextUNICODE
, aUnicode
))
942 SvMemoryStream aStream
;
944 if(!opt
.nProp
.empty())
946 aStream
.WriteBytes(opt
.nProp
.data(), opt
.nProp
.size());
950 OUString aTextPathString
= SvxMSDffManager::MSDFFReadZString(aStream
, opt
.nProp
.size(), true);
953 m_pSerializer
->singleElementNS(XML_v
, XML_path
, XML_textpathok
, "t");
955 rtl::Reference
<sax_fastparser::FastAttributeList
> pAttrList
= FastSerializerHelper::createAttrList();
956 pAttrList
->add(XML_on
, "t");
957 pAttrList
->add(XML_fitshape
, "t");
958 pAttrList
->add(XML_string
, aTextPathString
);
959 EscherPropSortStruct aFont
;
961 if (rProps
.GetOpt(ESCHER_Prop_gtextFont
, aFont
))
963 aStream
.WriteBytes(aFont
.nProp
.data(), aFont
.nProp
.size());
965 OUString aTextPathFont
= SvxMSDffManager::MSDFFReadZString(aStream
, aFont
.nProp
.size(), true);
966 aStyle
+= "font-family:\"" + aTextPathFont
+ "\"";
969 if (rProps
.GetOpt(ESCHER_Prop_gtextSize
, nSize
))
971 float nSizeF
= static_cast<sal_Int32
>(nSize
) / 65536.0;
972 OUString aSize
= OUString::number(nSizeF
);
973 aStyle
+= ";font-size:" + aSize
+ "pt";
976 sal_uInt32 nGtextFlags
;
977 if (rProps
.GetOpt(DFF_Prop_gtextFStrikethrough
/*255*/, nGtextFlags
))
979 // The property is in fact a collection of flags. Two bytes contain the
980 // fUsegtextF* flags and the other two bytes at same place the associated
981 // On/Off flags. See '2.3.22.10 Geometry Text Boolean Properties' section
983 if ((nGtextFlags
& 0x00200020) == 0x00200020) // DFF_Prop_gtextFBold = 250
984 aStyle
+= ";font-weight:bold";
985 if ((nGtextFlags
& 0x00100010) == 0x00100010) // DFF_Prop_gtextFItalic = 251
986 aStyle
+= ";font-style:italic";
987 if ((nGtextFlags
& 0x00800080) == 0x00800080) // no DFF, PID gtextFNormalize = 248
988 aStyle
+= ";v-same-letter-heights:t";
990 // The value 'Fontwork character spacing' in LO is bound to field 'Scaling'
991 // not to 'Spacing' in character properties. In fact the characters are
992 // rendered with changed distance and width. The method in escherex.cxx has
993 // put a rounded value of 'CharScaleWidth' API property to
994 // DFF_Prop_gtextSpacing (=196) as integer part of 16.16 fixed point format.
995 // fUsegtextFTight and gtextFTight (244) of MS binary format are not used.
996 sal_uInt32 nGtextSpacing
;
997 if (rProps
.GetOpt(DFF_Prop_gtextSpacing
, nGtextSpacing
))
998 aStyle
+= ";v-text-spacing:" + OUString::number(nGtextSpacing
) + "f";
1001 if (!aStyle
.isEmpty())
1002 pAttrList
->add(XML_style
, aStyle
);
1004 // tdf#153260. LO renders all Fontwork shapes as if trim="t" is set. Default
1005 // value is "f". So always write out "t", otherwise import will reduce the
1006 // shape height as workaround for "f".
1007 pAttrList
->add(XML_trim
, "t");
1009 m_pSerializer
->singleElementNS(XML_v
, XML_textpath
, pAttrList
);
1012 bAlreadyWritten
[ESCHER_Prop_gtextUNICODE
] = true;
1013 bAlreadyWritten
[ESCHER_Prop_gtextFont
] = true;
1016 case DFF_Prop_adjustValue
:
1017 case DFF_Prop_adjust2Value
:
1019 // FIXME: tdf#153296: The currently exported markup for <v:shapetype> is based on
1020 // OOXML presets and unusable in regard to handles. Fontwork shapes use dedicated
1021 // own markup, see FontworkHelpers::GetVMLFontworkShapetypeMarkup.
1022 // Thus this is restricted to preset Fontwork shapes. Such have maximal two
1023 // adjustment values.
1024 if ((mso_sptTextSimple
<= m_nShapeType
&& m_nShapeType
<= mso_sptTextOnRing
)
1025 || (mso_sptTextPlainText
<= m_nShapeType
&& m_nShapeType
<= mso_sptTextCanDown
))
1029 if (rProps
.GetOpt(DFF_Prop_adjustValue
, nValue
))
1031 sAdj
= OString::number(static_cast<sal_Int32
>(nValue
));
1032 if (rProps
.GetOpt(DFF_Prop_adjust2Value
, nValue
))
1033 sAdj
+= "," + OString::number(static_cast<sal_Int32
>(nValue
));
1035 if (!sAdj
.isEmpty())
1036 m_pShapeAttrList
->add(XML_adj
, sAdj
);
1037 bAlreadyWritten
[DFF_Prop_adjustValue
] = true;
1038 bAlreadyWritten
[DFF_Prop_adjust2Value
] = true;
1042 case ESCHER_Prop_Rotation
:
1044 // The higher half of the variable contains the angle.
1045 m_ShapeStyle
.append(";rotation:" + OString::number(double(opt
.nPropValue
>> 16)));
1046 bAlreadyWritten
[ESCHER_Prop_Rotation
] = true;
1049 case ESCHER_Prop_fNoLineDrawDash
:
1051 // See DffPropertyReader::ApplyLineAttributes().
1052 impl_AddBool( m_pShapeAttrList
.get(), XML_stroked
, (opt
.nPropValue
& 8) != 0 );
1053 bAlreadyWritten
[ESCHER_Prop_fNoLineDrawDash
] = true;
1056 case ESCHER_Prop_wzName
:
1058 SvMemoryStream aStream
;
1060 if(!opt
.nProp
.empty())
1062 aStream
.WriteBytes(opt
.nProp
.data(), opt
.nProp
.size());
1066 OUString idStr
= SvxMSDffManager::MSDFFReadZString(aStream
, opt
.nProp
.size(), true);
1068 if (!IsWaterMarkShape(m_pSdrObject
->GetName()) && !m_bSkipwzName
)
1069 m_pShapeAttrList
->add(XML_ID
, idStr
);
1071 // note that XML_ID is different from XML_id (although it looks like a LO
1072 // implementation distinction without valid justification to me).
1073 // FIXME: XML_ID produces invalid file, see tdf#153183
1074 bAlreadyWritten
[ESCHER_Prop_wzName
] = true;
1078 #if OSL_DEBUG_LEVEL > 0
1079 const size_t opt_nProp_size(opt
.nProp
.size());
1080 SAL_WARN( "oox.vml", "TODO VMLExport::Commit(), unimplemented id: " << nId
1081 << ", value: " << opt
.nPropValue
1082 << ", data: [" << opt_nProp_size
<< "]");
1083 if ( opt
.nProp
.size() )
1085 const sal_uInt8
*pIt
= opt
.nProp
.data();
1086 OStringBuffer
buf( " ( " );
1087 for ( int nCount
= opt
.nProp
.size(); nCount
; --nCount
)
1089 buf
.append( OString::number(static_cast<sal_Int32
>(*pIt
), 16) + " ");
1093 SAL_WARN("oox.vml", std::string_view(buf
));
1100 m_pSerializer
->mergeTopMarks(Tag_Commit
, sax_fastparser::MergeMarks::POSTPONE
);
1103 OString
VMLExport::ShapeIdString( sal_uInt32 nId
)
1105 if(m_bOverrideShapeIdGeneration
)
1106 return m_sShapeIDPrefix
+ OString::number( nId
);
1108 return "shape_" + OString::number( nId
);
1111 void VMLExport::AddFlipXY( )
1113 if (m_nShapeFlags
& (ShapeFlag::FlipH
| ShapeFlag::FlipV
))
1115 m_ShapeStyle
.append( ";flip:" );
1117 if (m_nShapeFlags
& ShapeFlag::FlipH
)
1118 m_ShapeStyle
.append( "x" );
1120 if (m_nShapeFlags
& ShapeFlag::FlipV
)
1121 m_ShapeStyle
.append( "y" );
1125 void VMLExport::AddLineDimensions( const tools::Rectangle
& rRectangle
)
1128 if (!m_ShapeStyle
.isEmpty())
1129 m_ShapeStyle
.append( ";" );
1131 m_ShapeStyle
.append( "position:absolute" );
1135 // the actual dimensions
1136 OString aLeft
, aTop
, aRight
, aBottom
;
1138 if ( mnGroupLevel
== 1 )
1140 static constexpr OString
aPt( "pt"_ostr
);
1141 aLeft
= OString::number( double( rRectangle
.Left() ) / 20 ) + aPt
;
1142 aTop
= OString::number( double( rRectangle
.Top() ) / 20 ) + aPt
;
1143 aRight
= OString::number( double( rRectangle
.Right() ) / 20 ) + aPt
;
1144 aBottom
= OString::number( double( rRectangle
.Bottom() ) / 20 ) + aPt
;
1148 aLeft
= OString::number( rRectangle
.Left() );
1149 aTop
= OString::number( rRectangle
.Top() );
1150 aRight
= OString::number( rRectangle
.Right() );
1151 aBottom
= OString::number( rRectangle
.Bottom() );
1154 m_pShapeAttrList
->add( XML_from
, aLeft
+ "," + aTop
);
1156 m_pShapeAttrList
->add( XML_to
, aRight
+ "," + aBottom
);
1159 void VMLExport::AddRectangleDimensions( OStringBuffer
& rBuffer
, const tools::Rectangle
& rRectangle
, bool rbAbsolutePos
)
1161 if ( !rBuffer
.isEmpty() )
1162 rBuffer
.append( ";" );
1164 if (rbAbsolutePos
&& !m_bInline
)
1166 rBuffer
.append( "position:absolute;" );
1171 rBuffer
.append( "width:" + OString::number( double( rRectangle
.Right() - rRectangle
.Left() ) / 20 ) +
1172 "pt;height:" + OString::number( double( rRectangle
.Bottom() - rRectangle
.Top() ) / 20 ) +
1175 else if ( mnGroupLevel
== 1 )
1177 rBuffer
.append( "margin-left:" + OString::number( double( rRectangle
.Left() ) / 20 ) +
1178 "pt;margin-top:" + OString::number( double( rRectangle
.Top() ) / 20 ) +
1179 "pt;width:" + OString::number( double( rRectangle
.Right() - rRectangle
.Left() ) / 20 ) +
1180 "pt;height:" + OString::number( double( rRectangle
.Bottom() - rRectangle
.Top() ) / 20 ) +
1185 rBuffer
.append( "left:" + OString::number( rRectangle
.Left() ) +
1186 ";top:" + OString::number( rRectangle
.Top() ) +
1187 ";width:" + OString::number( rRectangle
.Right() - rRectangle
.Left() ) +
1188 ";height:" + OString::number( rRectangle
.Bottom() - rRectangle
.Top() ) );
1194 void VMLExport::AddShapeAttribute( sal_Int32 nAttribute
, std::string_view rValue
)
1196 m_pShapeAttrList
->add( nAttribute
, rValue
);
1199 static std::vector
<OString
> lcl_getShapeTypes()
1201 std::vector
<OString
> aRet
;
1203 OUString
aPath("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER
"/filter/vml-shape-types");
1204 rtl::Bootstrap::expandMacros(aPath
);
1205 SvFileStream
aStream(aPath
, StreamMode::READ
);
1206 if (aStream
.GetError() != ERRCODE_NONE
)
1207 SAL_WARN("oox", "failed to open vml-shape-types");
1208 OStringBuffer aLine
;
1209 bool bNotDone
= aStream
.ReadLine(aLine
);
1212 // Filter out comments.
1213 if (!o3tl::starts_with(aLine
, "/"))
1214 aRet
.push_back(OString(aLine
));
1215 bNotDone
= aStream
.ReadLine(aLine
);
1220 static bool lcl_isTextBox(const SdrObject
* pSdrObject
)
1222 uno::Reference
<beans::XPropertySet
> xPropertySet(const_cast<SdrObject
*>(pSdrObject
)->getUnoShape(), uno::UNO_QUERY
);
1223 if (!xPropertySet
.is())
1225 uno::Reference
<beans::XPropertySetInfo
> xPropertySetInfo
= xPropertySet
->getPropertySetInfo();
1226 if (!xPropertySetInfo
->hasPropertyByName("TextBox"))
1228 css::uno::Any
aTextBox(xPropertySet
->getPropertyValue("TextBox"));
1229 if (!aTextBox
.hasValue())
1231 return aTextBox
.get
<bool>();
1234 static OUString
lcl_getAnchorIdFromGrabBag(const SdrObject
* pSdrObject
)
1238 uno::Reference
<beans::XPropertySet
> xShape(const_cast<SdrObject
*>(pSdrObject
)->getUnoShape(), uno::UNO_QUERY
);
1239 if (xShape
->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"))
1241 comphelper::SequenceAsHashMap
aInteropGrabBag(xShape
->getPropertyValue("InteropGrabBag"));
1242 auto it
= aInteropGrabBag
.find("AnchorId");
1243 if (it
!= aInteropGrabBag
.end())
1244 it
->second
>>= aResult
;
1250 sal_uInt32
VMLExport::GenerateShapeId()
1252 if(!m_bOverrideShapeIdGeneration
)
1253 return EscherEx::GenerateShapeId();
1255 return m_nShapeIDCounter
++;
1258 OString
VMLExport::GetVMLShapeTypeDefinition(
1259 std::string_view sShapeID
, const bool bIsPictureFrame
)
1262 if ( !bIsPictureFrame
)
1263 // We don't have a shape definition for host control in presetShapeDefinitions.xml
1264 // So use a definition copied from DOCX file created with MSO
1265 sShapeType
= OString::Concat("<v:shapetype id=\"_x0000_t") + sShapeID
+
1266 "\" coordsize=\"21600,21600\" o:spt=\"" + sShapeID
+
1267 "\" path=\"m,l,21600l21600,21600l21600,xe\">\n"
1268 "<v:stroke joinstyle=\"miter\"/>\n"
1269 "<v:path shadowok=\"f\" o:extrusionok=\"f\" strokeok=\"f\" fillok=\"f\" o:connecttype=\"rect\"/>\n"
1270 "<o:lock v:ext=\"edit\" shapetype=\"t\"/>\n"
1273 // We don't have a shape definition for picture frame in presetShapeDefinitions.xml
1274 // So use a definition copied from DOCX file created with MSO
1275 sShapeType
= OString::Concat("<v:shapetype id=\"_x0000_t") + sShapeID
+
1276 "\" coordsize=\"21600,21600\" o:spt=\"" + sShapeID
+
1277 "\" o:preferrelative=\"t\" path=\"m@4@5l@4@11@9@11@9@5xe\" filled=\"f\" stroked=\"f\">\n"
1278 "<v:stroke joinstyle=\"miter\"/>\n"
1280 "<v:f eqn=\"if lineDrawn pixelLineWidth 0\"/>\n"
1281 "<v:f eqn=\"sum @0 1 0\"/>\n"
1282 "<v:f eqn=\"sum 0 0 @1\"/>\n"
1283 "<v:f eqn=\"prod @2 1 2\"/>\n"
1284 "<v:f eqn=\"prod @3 21600 pixelWidth\"/>\n"
1285 "<v:f eqn=\"prod @3 21600 pixelHeight\"/>\n"
1286 "<v:f eqn=\"sum @0 0 1\"/>\n"
1287 "<v:f eqn=\"prod @6 1 2\"/>\n"
1288 "<v:f eqn=\"prod @7 21600 pixelWidth\"/>\n"
1289 "<v:f eqn=\"sum @8 21600 0\"/>\n"
1290 "<v:f eqn=\"prod @7 21600 pixelHeight\"/>\n"
1291 "<v:f eqn=\"sum @10 21600 0\"/>\n"
1293 "<v:path o:extrusionok=\"f\" gradientshapeok=\"t\" o:connecttype=\"rect\"/>\n"
1294 "<o:lock v:ext=\"edit\" aspectratio=\"t\"/>\n"
1299 sal_Int32
VMLExport::StartShape()
1301 if ( m_nShapeType
== ESCHER_ShpInst_Nil
)
1304 // some of the shapes have their own name ;-)
1305 sal_Int32 nShapeElement
= -1;
1306 bool bReferToShapeType
= false;
1307 switch ( m_nShapeType
)
1309 case ESCHER_ShpInst_NotPrimitive
: nShapeElement
= XML_shape
; break;
1310 case ESCHER_ShpInst_Rectangle
: nShapeElement
= XML_rect
; break;
1311 case ESCHER_ShpInst_RoundRectangle
: nShapeElement
= XML_roundrect
; break;
1312 case ESCHER_ShpInst_Ellipse
: nShapeElement
= XML_oval
; break;
1313 case ESCHER_ShpInst_Arc
: nShapeElement
= XML_arc
; break;
1314 case ESCHER_ShpInst_Line
: nShapeElement
= XML_line
; break;
1315 case ESCHER_ShpInst_HostControl
:
1317 bReferToShapeType
= true;
1318 nShapeElement
= XML_shape
;
1319 if ( !m_aShapeTypeWritten
[ m_nShapeType
] )
1321 m_pSerializer
->write(GetVMLShapeTypeDefinition(OString::number(m_nShapeType
), false));
1322 m_aShapeTypeWritten
[ m_nShapeType
] = true;
1326 case ESCHER_ShpInst_PictureFrame
:
1328 bReferToShapeType
= true;
1329 nShapeElement
= XML_shape
;
1330 if ( !m_aShapeTypeWritten
[ m_nShapeType
] )
1332 m_pSerializer
->write(GetVMLShapeTypeDefinition(OString::number(m_nShapeType
), true));
1333 m_aShapeTypeWritten
[ m_nShapeType
] = true;
1338 nShapeElement
= XML_shape
;
1339 if (m_pSdrObject
->IsTextPath())
1341 bReferToShapeType
= m_aShapeTypeWritten
[m_nShapeType
];
1342 if (!bReferToShapeType
)
1344 // Does a predefined markup exist at all?
1345 OString sMarkup
= FontworkHelpers::GetVMLFontworkShapetypeMarkup(
1346 static_cast<MSO_SPT
>(m_nShapeType
));
1347 if (!sMarkup
.isEmpty())
1349 m_pSerializer
->write(sMarkup
);
1350 m_aShapeTypeWritten
[m_nShapeType
] = true;
1351 bReferToShapeType
= true;
1354 // ToDo: The case bReferToShapeType==false happens for 'non-primitive' shapes for
1355 // example. We need to get the geometry from CustomShapeGeometry in these cases.
1357 else if ( m_nShapeType
< ESCHER_ShpInst_COUNT
)
1359 // a predefined shape?
1360 static std::vector
<OString
> aShapeTypes
= lcl_getShapeTypes();
1361 SAL_WARN_IF(m_nShapeType
>= aShapeTypes
.size(), "oox.vml", "Unknown shape type!");
1362 if (m_nShapeType
< aShapeTypes
.size() && aShapeTypes
[m_nShapeType
] != "NULL")
1364 bReferToShapeType
= true;
1365 if ( !m_aShapeTypeWritten
[ m_nShapeType
] )
1367 m_pSerializer
->write(aShapeTypes
[m_nShapeType
]);
1368 m_aShapeTypeWritten
[ m_nShapeType
] = true;
1373 // rectangle is probably the best fallback...
1374 nShapeElement
= XML_rect
;
1383 case text::HoriOrientation::LEFT
:
1384 m_ShapeStyle
.append(";mso-position-horizontal:left");
1386 case text::HoriOrientation::CENTER
:
1387 m_ShapeStyle
.append(";mso-position-horizontal:center");
1389 case text::HoriOrientation::RIGHT
:
1390 m_ShapeStyle
.append(";mso-position-horizontal:right");
1392 case text::HoriOrientation::INSIDE
:
1393 m_ShapeStyle
.append(";mso-position-horizontal:inside");
1395 case text::HoriOrientation::OUTSIDE
:
1396 m_ShapeStyle
.append(";mso-position-horizontal:outside");
1399 case text::HoriOrientation::NONE
:
1404 case text::RelOrientation::PAGE_PRINT_AREA
:
1405 m_ShapeStyle
.append(";mso-position-horizontal-relative:margin");
1407 case text::RelOrientation::PAGE_FRAME
:
1408 case text::RelOrientation::PAGE_LEFT
:
1409 case text::RelOrientation::PAGE_RIGHT
:
1410 m_ShapeStyle
.append(";mso-position-horizontal-relative:page");
1412 case text::RelOrientation::CHAR
:
1413 m_ShapeStyle
.append(";mso-position-horizontal-relative:char");
1421 case text::VertOrientation::TOP
:
1422 case text::VertOrientation::LINE_TOP
:
1423 case text::VertOrientation::CHAR_TOP
:
1424 m_ShapeStyle
.append(";mso-position-vertical:top");
1426 case text::VertOrientation::CENTER
:
1427 case text::VertOrientation::LINE_CENTER
:
1428 m_ShapeStyle
.append(";mso-position-vertical:center");
1430 case text::VertOrientation::BOTTOM
:
1431 case text::VertOrientation::LINE_BOTTOM
:
1432 case text::VertOrientation::CHAR_BOTTOM
:
1433 m_ShapeStyle
.append(";mso-position-vertical:bottom");
1436 case text::VertOrientation::NONE
:
1441 case text::RelOrientation::PAGE_PRINT_AREA
:
1442 m_ShapeStyle
.append(";mso-position-vertical-relative:margin");
1444 case text::RelOrientation::PAGE_FRAME
:
1445 m_ShapeStyle
.append(";mso-position-vertical-relative:page");
1451 if (!m_pSdrObject
->getHyperlink().isEmpty())
1452 m_pShapeAttrList
->add(
1453 XML_href
, m_pSdrObject
->getHyperlink());
1455 m_pShapeAttrList
->addNS(XML_o
, XML_allowincell
, m_IsFollowingTextFlow
? "t" : "f");
1458 m_pShapeAttrList
->add( XML_style
, m_ShapeStyle
.makeStringAndClear() );
1460 OUString sAnchorId
= lcl_getAnchorIdFromGrabBag(m_pSdrObject
);
1461 if (!sAnchorId
.isEmpty())
1462 m_pShapeAttrList
->addNS(XML_wp14
, XML_anchorId
, sAnchorId
);
1464 if ( nShapeElement
>= 0 && !m_pShapeAttrList
->hasAttribute( XML_type
) && bReferToShapeType
)
1467 if (m_bUseHashMarkForType
)
1469 m_pShapeAttrList
->add( XML_type
, sType
+
1470 "_x0000_t" + OString::number( m_nShapeType
) );
1473 // allow legacy id (which in form controls and textboxes
1474 // by definition seems to have this otherwise illegal name).
1475 m_pSerializer
->setAllowXEscape(!m_sShapeIDPrefix
.startsWith("_x0000_"));
1477 // start of the shape
1478 m_pSerializer
->startElementNS( XML_v
, nShapeElement
, m_pShapeAttrList
);
1479 m_pSerializer
->setAllowXEscape(true);
1481 OString
const textboxStyle(m_TextboxStyle
.makeStringAndClear());
1483 // now check if we have some editeng text (not associated textbox) and we have a text exporter registered
1484 const SdrTextObj
* pTxtObj
= DynCastSdrTextObj( m_pSdrObject
);
1485 if (pTxtObj
&& m_pTextExport
&& !m_pSdrObject
->IsTextPath()
1486 && !IsWaterMarkShape(m_pSdrObject
->GetName()) && !lcl_isTextBox(m_pSdrObject
))
1488 std::optional
<OutlinerParaObject
> pParaObj
;
1492 When the object is actively being edited, that text is not set into
1493 the objects normal text object, but lives in a separate object.
1495 if (pTxtObj
->IsTextEditActive())
1497 pParaObj
= pTxtObj
->CreateEditOutlinerParaObject();
1499 else if (pTxtObj
->GetOutlinerParaObject())
1501 pParaObj
= *pTxtObj
->GetOutlinerParaObject();
1506 rtl::Reference
<sax_fastparser::FastAttributeList
> pTextboxAttrList
= FastSerializerHelper::createAttrList();
1507 if (!textboxStyle
.isEmpty())
1509 pTextboxAttrList
->add(XML_style
, textboxStyle
);
1512 // this is reached only in case some text is attached to the shape
1513 m_pSerializer
->startElementNS(XML_v
, XML_textbox
, pTextboxAttrList
);
1514 m_pTextExport
->WriteOutliner(*pParaObj
);
1515 m_pSerializer
->endElementNS(XML_v
, XML_textbox
);
1519 return nShapeElement
;
1522 void VMLExport::EndShape( sal_Int32 nShapeElement
)
1524 if ( nShapeElement
< 0 )
1527 if (m_pTextExport
&& lcl_isTextBox(m_pSdrObject
))
1529 uno::Reference
<drawing::XShape
> xShape
{const_cast<SdrObject
*>(m_pSdrObject
)->getUnoShape(), uno::UNO_QUERY
};
1530 uno::Reference
<beans::XPropertySet
> xPropertySet(xShape
, uno::UNO_QUERY
);
1531 uno::Reference
<beans::XPropertySetInfo
> xPropertySetInfo
= xPropertySet
->getPropertySetInfo();
1532 bool bBottomToTop
= false;
1533 if (xPropertySetInfo
->hasPropertyByName("CustomShapeGeometry"))
1535 // In this case a DrawingML DOCX was imported.
1536 auto aAny
= xPropertySet
->getPropertyValue("WritingMode");
1537 sal_Int16 nWritingMode
;
1538 if ((aAny
>>= nWritingMode
) && nWritingMode
== text::WritingMode2::BT_LR
)
1539 bBottomToTop
= true;
1543 // In this case a pure VML DOCX was imported, so there is no CustomShapeGeometry.
1544 auto pTextExport
= m_pTextExport
->GetDrawingML().GetTextExport();
1545 // FIXME: somewhy pTextExport is always nullptr, we should find its reason
1548 auto xTextFrame
= pTextExport
->GetUnoTextFrame(xShape
);
1549 uno::Reference
<beans::XPropertySet
> xPropSet(xTextFrame
, uno::UNO_QUERY
);
1550 auto aAny
= xPropSet
->getPropertyValue("WritingMode");
1551 sal_Int16 nWritingMode
;
1552 if (aAny
>>= nWritingMode
)
1554 switch (nWritingMode
)
1556 case text::WritingMode2::BT_LR
:
1557 bBottomToTop
= true;
1565 rtl::Reference
<sax_fastparser::FastAttributeList
> pTextboxAttrList
= FastSerializerHelper::createAttrList();
1567 pTextboxAttrList
->add(XML_style
, "mso-layout-flow-alt:bottom-to-top");
1568 m_pSerializer
->startElementNS(XML_v
, XML_textbox
, pTextboxAttrList
);
1570 m_pTextExport
->WriteVMLTextBox(uno::Reference
<drawing::XShape
>(xPropertySet
, uno::UNO_QUERY_THROW
));
1572 m_pSerializer
->endElementNS(XML_v
, XML_textbox
);
1575 if (m_pWrapAttrList
)
1577 m_pSerializer
->singleElementNS(XML_w10
, XML_wrap
, m_pWrapAttrList
);
1581 m_pSerializer
->endElementNS( XML_v
, nShapeElement
);
1584 OString
const & VMLExport::AddSdrObject( const SdrObject
& rObj
,
1585 bool const bIsFollowingTextFlow
,
1586 sal_Int16 eHOri
, sal_Int16 eVOri
, sal_Int16 eHRel
, sal_Int16 eVRel
,
1587 FastAttributeList
* pWrapAttrList
,
1588 const bool bOOxmlExport
, sal_uInt32 nId
)
1590 m_pSdrObject
= &rObj
;
1595 m_pWrapAttrList
= pWrapAttrList
;
1597 m_IsFollowingTextFlow
= bIsFollowingTextFlow
;
1598 EscherEx::AddSdrObject(rObj
, bOOxmlExport
, nId
);
1602 OString
const & VMLExport::AddInlineSdrObject( const SdrObject
& rObj
, const bool bOOxmlExport
)
1604 m_pSdrObject
= &rObj
;
1609 m_pWrapAttrList
.clear();
1611 m_IsFollowingTextFlow
= true;
1612 EscherEx::AddSdrObject(rObj
, bOOxmlExport
);
1616 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */