summaryrefslogtreecommitdiffstats
path: root/oox/source/export/vmlexport.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'oox/source/export/vmlexport.cxx')
-rw-r--r--oox/source/export/vmlexport.cxx1529
1 files changed, 1529 insertions, 0 deletions
diff --git a/oox/source/export/vmlexport.cxx b/oox/source/export/vmlexport.cxx
new file mode 100644
index 000000000..4f2654fae
--- /dev/null
+++ b/oox/source/export/vmlexport.cxx
@@ -0,0 +1,1529 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+#include <rtl/bootstrap.hxx>
+#include <svl/itemset.hxx>
+#include <oox/export/drawingml.hxx>
+#include <oox/export/vmlexport.hxx>
+#include <sax/fastattribs.hxx>
+
+#include <oox/token/tokens.hxx>
+
+#include <rtl/strbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include <tools/stream.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/sdmetitm.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <filter/msfilter/msdffimp.hxx>
+#include <filter/msfilter/util.hxx>
+#include <filter/msfilter/escherex.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/text/XTextFrame.hpp>
+
+#include <cstdio>
+
+using namespace sax_fastparser;
+using namespace oox::vml;
+using namespace com::sun::star;
+
+const sal_Int32 Tag_Container = 44444;
+const sal_Int32 Tag_Commit = 44445;
+
+VMLExport::VMLExport( ::sax_fastparser::FSHelperPtr const & pSerializer, VMLTextExport* pTextExport )
+ : EscherEx( std::make_shared<EscherExGlobal>(), nullptr, /*bOOXML=*/true )
+ , m_pSerializer( pSerializer )
+ , m_pTextExport( pTextExport )
+ , m_eHOri( 0 )
+ , m_eVOri( 0 )
+ , m_eHRel( 0 )
+ , m_eVRel( 0 )
+ , m_bInline( false )
+ , m_pSdrObject( nullptr )
+ , m_nShapeType( ESCHER_ShpInst_Nil )
+ , m_nShapeFlags(ShapeFlag::NONE)
+ , m_ShapeStyle( 200 )
+ , m_aShapeTypeWritten( ESCHER_ShpInst_COUNT )
+ , m_bSkipwzName( false )
+ , m_bUseHashMarkForType( false )
+ , m_bOverrideShapeIdGeneration( false )
+ , m_nShapeIDCounter( 0 )
+{
+ mnGroupLevel = 1;
+}
+
+void VMLExport::SetFS( const ::sax_fastparser::FSHelperPtr& pSerializer )
+{
+ m_pSerializer = pSerializer;
+}
+
+VMLExport::~VMLExport()
+{
+ delete mpOutStrm;
+ mpOutStrm = nullptr;
+}
+
+void VMLExport::OpenContainer( sal_uInt16 nEscherContainer, int nRecInstance )
+{
+ EscherEx::OpenContainer( nEscherContainer, nRecInstance );
+
+ if ( nEscherContainer != ESCHER_SpContainer )
+ return;
+
+ // opening a shape container
+ SAL_WARN_IF(m_nShapeType != ESCHER_ShpInst_Nil, "oox.vml", "opening shape inside of a shape!");
+ m_nShapeType = ESCHER_ShpInst_Nil;
+ m_pShapeAttrList = FastSerializerHelper::createAttrList();
+
+ m_ShapeStyle.setLength(0);
+ m_ShapeStyle.ensureCapacity(200);
+
+ // postpone the output so that we are able to write even the elements
+ // that we learn inside Commit()
+ m_pSerializer->mark(Tag_Container);
+}
+
+void VMLExport::CloseContainer()
+{
+ if ( mRecTypes.back() == ESCHER_SpContainer )
+ {
+ // write the shape now when we have all the info
+ sal_Int32 nShapeElement = StartShape();
+
+ m_pSerializer->mergeTopMarks(Tag_Container);
+
+ EndShape( nShapeElement );
+
+ // cleanup
+ m_nShapeType = ESCHER_ShpInst_Nil;
+ m_pShapeAttrList = nullptr;
+ }
+
+ EscherEx::CloseContainer();
+}
+
+sal_uInt32 VMLExport::EnterGroup( const OUString& rShapeName, const tools::Rectangle* pRect )
+{
+ sal_uInt32 nShapeId = GenerateShapeId();
+
+ OStringBuffer aStyle( 200 );
+ rtl::Reference<FastAttributeList> pAttrList = FastSerializerHelper::createAttrList();
+
+ pAttrList->add( XML_id, ShapeIdString( nShapeId ) );
+
+ if ( rShapeName.getLength() )
+ pAttrList->add( XML_alt, rShapeName );
+
+ bool rbAbsolutePos = true;
+ //editAs
+ OUString rEditAs = EscherEx::GetEditAs();
+ if (!rEditAs.isEmpty())
+ {
+ pAttrList->add(XML_editas, rEditAs);
+ rbAbsolutePos = false;
+ }
+
+ // style
+ if ( pRect )
+ AddRectangleDimensions( aStyle, *pRect, rbAbsolutePos );
+
+ if ( !aStyle.isEmpty() )
+ pAttrList->add( XML_style, aStyle.makeStringAndClear() );
+
+ // coordorigin/coordsize
+ if ( pRect && ( mnGroupLevel == 1 ) )
+ {
+ pAttrList->add( XML_coordorigin,
+ OString::number( pRect->Left() ) + "," + OString::number( pRect->Top() ) );
+
+ pAttrList->add( XML_coordsize,
+ OString::number( pRect->Right() - pRect->Left() ) + "," +
+ OString::number( pRect->Bottom() - pRect->Top() ) );
+ }
+
+ m_pSerializer->startElementNS( XML_v, XML_group, pAttrList );
+
+ mnGroupLevel++;
+ return nShapeId;
+}
+
+void VMLExport::LeaveGroup()
+{
+ --mnGroupLevel;
+ m_pSerializer->endElementNS( XML_v, XML_group );
+}
+
+void VMLExport::AddShape( sal_uInt32 nShapeType, ShapeFlag nShapeFlags, sal_uInt32 nShapeId )
+{
+ m_nShapeType = nShapeType;
+ m_nShapeFlags = nShapeFlags;
+
+ m_sShapeId = ShapeIdString( nShapeId );
+ // If shape is a watermark object - should keep the original shape's name
+ // because Microsoft detects if it is a watermark by the actual name
+ if (!IsWaterMarkShape(m_pSdrObject->GetName()))
+ {
+ // Not a watermark object
+ m_pShapeAttrList->add( XML_id, m_sShapeId );
+ }
+ else
+ {
+ // A watermark object - store the optional shape ID
+ m_pShapeAttrList->add( XML_id, m_pSdrObject->GetName() );
+ // also ('o:spid')
+ m_pShapeAttrList->addNS( XML_o, XML_spid, m_sShapeId );
+ }
+}
+
+bool VMLExport::IsWaterMarkShape(std::u16string_view rStr)
+{
+ if (rStr.empty() ) return false;
+
+ return o3tl::starts_with(rStr, u"PowerPlusWaterMarkObject") || o3tl::starts_with(rStr, u"WordPictureWatermark");
+}
+
+void VMLExport::OverrideShapeIDGen(bool bOverrideShapeIdGen, const OString& sShapeIDPrefix)
+{
+ m_bOverrideShapeIdGeneration = bOverrideShapeIdGen;
+ if(bOverrideShapeIdGen)
+ {
+ assert(!sShapeIDPrefix.isEmpty());
+ m_sShapeIDPrefix = sShapeIDPrefix;
+ }
+ else
+ m_sShapeIDPrefix.clear();
+}
+
+static void impl_AddArrowHead( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue )
+{
+ if ( !pAttrList )
+ return;
+
+ const char *pArrowHead = nullptr;
+ switch ( nValue )
+ {
+ case ESCHER_LineNoEnd: pArrowHead = "none"; break;
+ case ESCHER_LineArrowEnd: pArrowHead = "block"; break;
+ case ESCHER_LineArrowStealthEnd: pArrowHead = "classic"; break;
+ case ESCHER_LineArrowDiamondEnd: pArrowHead = "diamond"; break;
+ case ESCHER_LineArrowOvalEnd: pArrowHead = "oval"; break;
+ case ESCHER_LineArrowOpenEnd: pArrowHead = "open"; break;
+ }
+
+ if ( pArrowHead )
+ pAttrList->add( nElement, pArrowHead );
+}
+
+static void impl_AddArrowLength( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue )
+{
+ if ( !pAttrList )
+ return;
+
+ const char *pArrowLength = nullptr;
+ switch ( nValue )
+ {
+ case ESCHER_LineShortArrow: pArrowLength = "short"; break;
+ case ESCHER_LineMediumLenArrow: pArrowLength = "medium"; break;
+ case ESCHER_LineLongArrow: pArrowLength = "long"; break;
+ }
+
+ if ( pArrowLength )
+ pAttrList->add( nElement, pArrowLength );
+}
+
+static void impl_AddArrowWidth( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue )
+{
+ if ( !pAttrList )
+ return;
+
+ const char *pArrowWidth = nullptr;
+ switch ( nValue )
+ {
+ case ESCHER_LineNarrowArrow: pArrowWidth = "narrow"; break;
+ case ESCHER_LineMediumWidthArrow: pArrowWidth = "medium"; break;
+ case ESCHER_LineWideArrow: pArrowWidth = "wide"; break;
+ }
+
+ if ( pArrowWidth )
+ pAttrList->add( nElement, pArrowWidth );
+}
+
+static void impl_AddBool( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, bool bValue )
+{
+ if ( !pAttrList )
+ return;
+
+ pAttrList->add( nElement, bValue? "t": "f" );
+}
+
+static void impl_AddColor( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nColor )
+{
+ SAL_WARN_IF( nColor & 0xFF000000 , "oox.vml" , "TODO: this is not a RGB value!");
+
+ if ( !pAttrList || ( nColor & 0xFF000000 ) )
+ return;
+
+ nColor = ( ( nColor & 0xFF ) << 16 ) + ( nColor & 0xFF00 ) + ( ( nColor & 0xFF0000 ) >> 16 );
+
+ const char *pColor = nullptr;
+ char pRgbColor[10];
+ switch ( nColor )
+ {
+ case 0x000000: pColor = "black"; break;
+ case 0xC0C0C0: pColor = "silver"; break;
+ case 0x808080: pColor = "gray"; break;
+ case 0xFFFFFF: pColor = "white"; break;
+ case 0x800000: pColor = "maroon"; break;
+ case 0xFF0000: pColor = "red"; break;
+ case 0x800080: pColor = "purple"; break;
+ case 0xFF00FF: pColor = "fuchsia"; break;
+ case 0x008000: pColor = "green"; break;
+ case 0x00FF00: pColor = "lime"; break;
+ case 0x808000: pColor = "olive"; break;
+ case 0xFFFF00: pColor = "yellow"; break;
+ case 0x000080: pColor = "navy"; break;
+ case 0x0000FF: pColor = "blue"; break;
+ case 0x008080: pColor = "teal"; break;
+ case 0x00FFFF: pColor = "aqua"; break;
+ default:
+ {
+ snprintf( pRgbColor, sizeof( pRgbColor ), "#%06x", static_cast< unsigned int >( nColor ) ); // not too handy to use OString::valueOf() here :-(
+ pColor = pRgbColor;
+ }
+ break;
+ }
+
+ pAttrList->add( nElement, pColor );
+}
+
+static void impl_AddInt( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue )
+{
+ if ( !pAttrList )
+ return;
+
+ pAttrList->add( nElement, OString::number( nValue ).getStr() );
+}
+
+static sal_uInt16 impl_GetUInt16( const sal_uInt8* &pVal )
+{
+ sal_uInt16 nRet = *pVal++;
+ nRet += ( *pVal++ ) << 8;
+ return nRet;
+}
+
+static sal_Int32 impl_GetPointComponent( const sal_uInt8* &pVal, sal_uInt16 nPointSize )
+{
+ sal_Int32 nRet = 0;
+ if ( ( nPointSize == 0xfff0 ) || ( nPointSize == 4 ) )
+ {
+ sal_uInt16 nUnsigned = *pVal++;
+ nUnsigned += ( *pVal++ ) << 8;
+
+ nRet = sal_Int16( nUnsigned );
+ }
+ else if ( nPointSize == 8 )
+ {
+ sal_uInt32 nUnsigned = *pVal++;
+ nUnsigned += ( *pVal++ ) << 8;
+ nUnsigned += ( *pVal++ ) << 16;
+ nUnsigned += ( *pVal++ ) << 24;
+
+ nRet = nUnsigned;
+ }
+
+ return nRet;
+}
+
+void VMLExport::AddSdrObjectVMLObject( const SdrObject& rObj)
+{
+ m_pSdrObject = &rObj;
+}
+void VMLExport::Commit( EscherPropertyContainer& rProps, const tools::Rectangle& rRect )
+{
+ if ( m_nShapeType == ESCHER_ShpInst_Nil )
+ return;
+
+ // postpone the output of the embedded elements so that they are written
+ // inside the shapes
+ m_pSerializer->mark(Tag_Commit);
+
+ // dimensions
+ if ( m_nShapeType == ESCHER_ShpInst_Line )
+ AddLineDimensions( rRect );
+ else
+ {
+ if ( IsWaterMarkShape( m_pSdrObject->GetName() ) )
+ {
+ // Watermark need some padding to be compatible with MSO
+ tools::Long nPaddingY = 0;
+ const SfxItemSet& rSet = m_pSdrObject->GetMergedItemSet();
+ if ( const SdrMetricItem* pItem = rSet.GetItem( SDRATTR_TEXT_UPPERDIST ) )
+ nPaddingY += pItem->GetValue();
+
+ tools::Rectangle aRect( rRect );
+ aRect.setHeight( aRect.getHeight() + nPaddingY );
+ AddRectangleDimensions( m_ShapeStyle, aRect );
+ }
+ else
+ AddRectangleDimensions( m_ShapeStyle, rRect );
+ }
+
+ // properties
+ bool bAlreadyWritten[ 0xFFF ] = {};
+ const EscherProperties &rOpts = rProps.GetOpts();
+ for (auto const& opt : rOpts)
+ {
+ sal_uInt16 nId = ( opt.nPropId & 0x0FFF );
+
+ if ( bAlreadyWritten[ nId ] )
+ continue;
+
+ switch ( nId )
+ {
+ case ESCHER_Prop_WrapText: // 133
+ {
+ const char *pWrapType = nullptr;
+ switch ( opt.nPropValue )
+ {
+ case ESCHER_WrapSquare:
+ case ESCHER_WrapByPoints: pWrapType = "square"; break; // these two are equivalent according to the docu
+ case ESCHER_WrapNone: pWrapType = "none"; break;
+ case ESCHER_WrapTopBottom:
+ case ESCHER_WrapThrough:
+ break; // last two are *undefined* in MS-ODRAW, don't exist in VML
+ }
+ if ( pWrapType )
+ {
+ m_ShapeStyle.append(";mso-wrap-style:");
+ m_ShapeStyle.append(pWrapType);
+ }
+ }
+ bAlreadyWritten[ ESCHER_Prop_WrapText ] = true;
+ break;
+
+ case ESCHER_Prop_AnchorText: // 135
+ {
+ char const* pValue(nullptr);
+ switch (opt.nPropValue)
+ {
+ case ESCHER_AnchorTop:
+ pValue = "top";
+ break;
+ case ESCHER_AnchorMiddle:
+ pValue = "middle";
+ break;
+ case ESCHER_AnchorBottom:
+ pValue = "bottom";
+ break;
+ case ESCHER_AnchorTopCentered:
+ pValue = "top-center";
+ break;
+ case ESCHER_AnchorMiddleCentered:
+ pValue = "middle-center";
+ break;
+ case ESCHER_AnchorBottomCentered:
+ pValue = "bottom-center";
+ break;
+ case ESCHER_AnchorTopBaseline:
+ pValue = "top-baseline";
+ break;
+ case ESCHER_AnchorBottomBaseline:
+ pValue = "bottom-baseline";
+ break;
+ case ESCHER_AnchorTopCenteredBaseline:
+ pValue = "top-center-baseline";
+ break;
+ case ESCHER_AnchorBottomCenteredBaseline:
+ pValue = "bottom-center-baseline";
+ break;
+ }
+ m_ShapeStyle.append(";v-text-anchor:");
+ m_ShapeStyle.append(pValue);
+ }
+ break;
+
+ case ESCHER_Prop_txflTextFlow: // 136
+ {
+ // at least "bottom-to-top" only has an effect when it's on the v:textbox element, not on v:shape
+ assert(m_TextboxStyle.isEmpty());
+ switch (opt.nPropValue)
+ {
+ case ESCHER_txflHorzN:
+ m_TextboxStyle.append("layout-flow:horizontal");
+ break;
+ case ESCHER_txflTtoBA:
+ m_TextboxStyle.append("layout-flow:vertical");
+ break;
+ case ESCHER_txflBtoT:
+ m_TextboxStyle.append("mso-layout-flow-alt:bottom-to-top");
+ break;
+ default:
+ assert(false); // unimplemented in escher export
+ break;
+ }
+ }
+ break;
+
+ // coordorigin
+ case ESCHER_Prop_geoLeft: // 320
+ case ESCHER_Prop_geoTop: // 321
+ {
+ sal_uInt32 nLeft = 0, nTop = 0;
+
+ if ( nId == ESCHER_Prop_geoLeft )
+ {
+ nLeft = opt.nPropValue;
+ rProps.GetOpt( ESCHER_Prop_geoTop, nTop );
+ }
+ else
+ {
+ nTop = opt.nPropValue;
+ rProps.GetOpt( ESCHER_Prop_geoLeft, nLeft );
+ }
+ if(nTop!=0 && nLeft!=0)
+ m_pShapeAttrList->add( XML_coordorigin,
+ OString::number( nLeft ) + "," + OString::number( nTop ) );
+ }
+ bAlreadyWritten[ ESCHER_Prop_geoLeft ] = true;
+ bAlreadyWritten[ ESCHER_Prop_geoTop ] = true;
+ break;
+
+ // coordsize
+ case ESCHER_Prop_geoRight: // 322
+ case ESCHER_Prop_geoBottom: // 323
+ {
+ sal_uInt32 nLeft = 0, nRight = 0, nTop = 0, nBottom = 0;
+ rProps.GetOpt( ESCHER_Prop_geoLeft, nLeft );
+ rProps.GetOpt( ESCHER_Prop_geoTop, nTop );
+
+ if ( nId == ESCHER_Prop_geoRight )
+ {
+ nRight = opt.nPropValue;
+ rProps.GetOpt( ESCHER_Prop_geoBottom, nBottom );
+ }
+ else
+ {
+ nBottom = opt.nPropValue;
+ rProps.GetOpt( ESCHER_Prop_geoRight, nRight );
+ }
+
+ if(nBottom!=0 && nRight!=0 )
+ m_pShapeAttrList->add( XML_coordsize,
+ OString::number( nRight - nLeft ) + "," + OString::number( nBottom - nTop ) );
+ }
+ bAlreadyWritten[ ESCHER_Prop_geoRight ] = true;
+ bAlreadyWritten[ ESCHER_Prop_geoBottom ] = true;
+ break;
+
+ case ESCHER_Prop_pVertices: // 325
+ case ESCHER_Prop_pSegmentInfo: // 326
+ {
+ EscherPropSortStruct aVertices;
+ EscherPropSortStruct aSegments;
+
+ if ( rProps.GetOpt( ESCHER_Prop_pVertices, aVertices ) &&
+ rProps.GetOpt( ESCHER_Prop_pSegmentInfo, aSegments ) )
+ {
+ const sal_uInt8 *pVerticesIt = aVertices.nProp.data() + 6;
+ const sal_uInt8 *pSegmentIt = aSegments.nProp.data();
+ OStringBuffer aPath( 512 );
+
+ sal_uInt16 nPointSize = aVertices.nProp[4] + ( aVertices.nProp[5] << 8 );
+
+ // number of segments
+ sal_uInt16 nSegments = impl_GetUInt16( pSegmentIt );
+ pSegmentIt += 4;
+
+ for ( ; nSegments; --nSegments )
+ {
+ sal_uInt16 nSeg = impl_GetUInt16( pSegmentIt );
+
+ // The segment type is stored in the upper 3 bits
+ // and segment count is stored in the lower 13
+ // bits.
+ unsigned char nSegmentType = (nSeg & 0xE000) >> 13;
+ unsigned short nSegmentCount = nSeg & 0x03FF;
+
+ switch (nSegmentType)
+ {
+ case msopathMoveTo:
+ {
+ sal_Int32 nX = impl_GetPointComponent( pVerticesIt, nPointSize );
+ sal_Int32 nY = impl_GetPointComponent( pVerticesIt, nPointSize );
+ if (nX >= 0 && nY >= 0 )
+ aPath.append( "m" + OString::number( nX ) + "," + OString::number( nY ) );
+ break;
+ }
+ case msopathClientEscape:
+ break;
+ case msopathEscape:
+ {
+ // If the segment type is msopathEscape, the lower 13 bits are
+ // divided in a 5 bit escape code and 8 bit
+ // vertex count (not segment count!)
+ unsigned char nEscapeCode = (nSegmentCount & 0x1F00) >> 8;
+ unsigned char nVertexCount = nSegmentCount & 0x00FF;
+ pVerticesIt += nVertexCount;
+
+ switch (nEscapeCode)
+ {
+ case 0xa: // nofill
+ aPath.append( "nf" );
+ break;
+ case 0xb: // nostroke
+ aPath.append( "ns" );
+ break;
+ }
+
+ break;
+ }
+ case msopathLineTo:
+ for (unsigned short i = 0; i < nSegmentCount; ++i)
+ {
+ sal_Int32 nX = impl_GetPointComponent( pVerticesIt, nPointSize );
+ sal_Int32 nY = impl_GetPointComponent( pVerticesIt, nPointSize );
+ aPath.append( "l" + OString::number( nX ) + "," + OString::number( nY ) );
+ }
+ break;
+ case msopathCurveTo:
+ for (unsigned short i = 0; i < nSegmentCount; ++i)
+ {
+ sal_Int32 nX1 = impl_GetPointComponent( pVerticesIt, nPointSize );
+ sal_Int32 nY1 = impl_GetPointComponent( pVerticesIt, nPointSize );
+ sal_Int32 nX2 = impl_GetPointComponent( pVerticesIt, nPointSize );
+ sal_Int32 nY2 = impl_GetPointComponent( pVerticesIt, nPointSize );
+ sal_Int32 nX3 = impl_GetPointComponent( pVerticesIt, nPointSize );
+ sal_Int32 nY3 = impl_GetPointComponent( pVerticesIt, nPointSize );
+ aPath.append( "c" + OString::number( nX1 ) + "," + OString::number( nY1 ) + "," +
+ OString::number( nX2 ) + "," + OString::number( nY2 ) + "," +
+ OString::number( nX3 ) + "," + OString::number( nY3 ) );
+ }
+ break;
+ case msopathClose:
+ aPath.append( "x" );
+ break;
+ case msopathEnd:
+ aPath.append( "e" );
+ break;
+ default:
+ SAL_WARN("oox", "Totally b0rked");
+ break;
+ case msopathInvalid:
+ SAL_WARN("oox", "Invalid - should never be found");
+ break;
+ }
+ }
+ OString pathString = aPath.makeStringAndClear();
+ if ( !pathString.isEmpty() && pathString != "xe" )
+ m_pShapeAttrList->add( XML_path, pathString );
+ }
+ else
+ SAL_WARN("oox.vml", "unhandled shape path, missing either pVertices or pSegmentInfo.");
+ }
+ bAlreadyWritten[ ESCHER_Prop_pVertices ] = true;
+ bAlreadyWritten[ ESCHER_Prop_pSegmentInfo ] = true;
+ break;
+
+ case ESCHER_Prop_fillType: // 384
+ case ESCHER_Prop_fillColor: // 385
+ case ESCHER_Prop_fillBackColor: // 387
+ case ESCHER_Prop_fillBlip: // 390
+ case ESCHER_Prop_fNoFillHitTest: // 447
+ case ESCHER_Prop_fillOpacity: // 386
+ {
+ sal_uInt32 nValue;
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList
+ = FastSerializerHelper::createAttrList();
+
+ bool imageData = false;
+ EscherPropSortStruct aStruct;
+ const SdrGrafObj* pSdrGrafObj = dynamic_cast<const SdrGrafObj*>(m_pSdrObject);
+
+ if (pSdrGrafObj && pSdrGrafObj->isSignatureLine() && m_pTextExport)
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrListSignatureLine
+ = FastSerializerHelper::createAttrList();
+ pAttrListSignatureLine->add(XML_issignatureline, "t");
+ if (!pSdrGrafObj->getSignatureLineId().isEmpty())
+ {
+ pAttrListSignatureLine->add(
+ XML_id, pSdrGrafObj->getSignatureLineId());
+ }
+ if (!pSdrGrafObj->getSignatureLineSuggestedSignerName().isEmpty())
+ {
+ pAttrListSignatureLine->add(
+ FSNS(XML_o, XML_suggestedsigner),
+ pSdrGrafObj->getSignatureLineSuggestedSignerName());
+ }
+ if (!pSdrGrafObj->getSignatureLineSuggestedSignerTitle().isEmpty())
+ {
+ pAttrListSignatureLine->add(
+ FSNS(XML_o, XML_suggestedsigner2),
+ pSdrGrafObj->getSignatureLineSuggestedSignerTitle());
+ }
+ if (!pSdrGrafObj->getSignatureLineSuggestedSignerEmail().isEmpty())
+ {
+ pAttrListSignatureLine->add(
+ FSNS(XML_o, XML_suggestedsigneremail),
+ pSdrGrafObj->getSignatureLineSuggestedSignerEmail());
+ }
+ if (!pSdrGrafObj->getSignatureLineSigningInstructions().isEmpty())
+ {
+ pAttrListSignatureLine->add(XML_signinginstructionsset, "t");
+ pAttrListSignatureLine->add(
+ FSNS(XML_o, XML_signinginstructions),
+ pSdrGrafObj->getSignatureLineSigningInstructions());
+ }
+ pAttrListSignatureLine->add(
+ XML_showsigndate,
+ pSdrGrafObj->isSignatureLineShowSignDate() ? "t" : "f");
+ pAttrListSignatureLine->add(
+ XML_allowcomments,
+ pSdrGrafObj->isSignatureLineCanAddComment() ? "t" : "f");
+
+ m_pSerializer->singleElementNS(
+ XML_o, XML_signatureline,
+ pAttrListSignatureLine);
+
+ // Get signature line graphic
+ const uno::Reference<graphic::XGraphic>& xGraphic
+ = pSdrGrafObj->getSignatureLineUnsignedGraphic();
+ Graphic aGraphic(xGraphic);
+ OUString aImageId = m_pTextExport->GetDrawingML().WriteImage(aGraphic, false);
+ pAttrList->add(FSNS(XML_r, XML_id), aImageId);
+ imageData = true;
+ }
+ else if (rProps.GetOpt(ESCHER_Prop_fillBlip, aStruct) && m_pTextExport)
+ {
+ SvMemoryStream aStream;
+ // The first bytes are WW8-specific, we're only interested in the PNG
+ int nHeaderSize = 25;
+ aStream.WriteBytes(aStruct.nProp.data() + nHeaderSize,
+ aStruct.nProp.size() - nHeaderSize);
+ aStream.Seek(0);
+ Graphic aGraphic;
+ GraphicConverter::Import(aStream, aGraphic);
+ OUString aImageId = m_pTextExport->GetDrawingML().WriteImage(aGraphic, false);
+ pAttrList->add(FSNS(XML_r, XML_id), aImageId);
+ imageData = true;
+ }
+
+ if (rProps.GetOpt(ESCHER_Prop_fNoFillHitTest, nValue))
+ impl_AddBool(pAttrList.get(), FSNS(XML_o, XML_detectmouseclick), nValue != 0);
+
+ if (imageData)
+ m_pSerializer->singleElementNS( XML_v, XML_imagedata, pAttrList );
+ else
+ {
+ if ( rProps.GetOpt( ESCHER_Prop_fillType, nValue ) )
+ {
+ const char *pFillType = nullptr;
+ switch ( nValue )
+ {
+ case ESCHER_FillSolid: pFillType = "solid"; break;
+ // TODO case ESCHER_FillPattern: pFillType = ""; break;
+ case ESCHER_FillTexture: pFillType = "tile"; break;
+ // TODO case ESCHER_FillPicture: pFillType = ""; break;
+ // TODO case ESCHER_FillShade: pFillType = ""; break;
+ // TODO case ESCHER_FillShadeCenter: pFillType = ""; break;
+ // TODO case ESCHER_FillShadeShape: pFillType = ""; break;
+ // TODO case ESCHER_FillShadeScale: pFillType = ""; break;
+ // TODO case ESCHER_FillShadeTitle: pFillType = ""; break;
+ // TODO case ESCHER_FillBackground: pFillType = ""; break;
+ default:
+ SAL_INFO("oox.vml", "Unhandled fill type: " << nValue);
+ break;
+ }
+ if ( pFillType )
+ pAttrList->add( XML_type, pFillType );
+ }
+ else if (!rProps.GetOpt(ESCHER_Prop_fillColor, nValue))
+ pAttrList->add( XML_on, "false" );
+
+ if ( rProps.GetOpt( ESCHER_Prop_fillColor, nValue ) )
+ impl_AddColor( m_pShapeAttrList.get(), XML_fillcolor, nValue );
+
+ if ( rProps.GetOpt( ESCHER_Prop_fillBackColor, nValue ) )
+ impl_AddColor( pAttrList.get(), XML_color2, nValue );
+
+
+ if (rProps.GetOpt(ESCHER_Prop_fillOpacity, nValue))
+ // Partly undo the transformation at the end of EscherPropertyContainer::CreateFillProperties(): VML opacity is 0..1.
+ pAttrList->add(XML_opacity, OString::number(double((nValue * 100) >> 16) / 100));
+ m_pSerializer->singleElementNS( XML_v, XML_fill, pAttrList );
+
+ }
+ }
+ bAlreadyWritten[ ESCHER_Prop_fillType ] = true;
+ bAlreadyWritten[ ESCHER_Prop_fillColor ] = true;
+ bAlreadyWritten[ ESCHER_Prop_fillBackColor ] = true;
+ bAlreadyWritten[ ESCHER_Prop_fillBlip ] = true;
+ bAlreadyWritten[ ESCHER_Prop_fNoFillHitTest ] = true;
+ bAlreadyWritten[ ESCHER_Prop_fillOpacity ] = true;
+ break;
+
+ case ESCHER_Prop_lineColor: // 448
+ case ESCHER_Prop_lineWidth: // 459
+ case ESCHER_Prop_lineDashing: // 462
+ case ESCHER_Prop_lineStartArrowhead: // 464
+ case ESCHER_Prop_lineEndArrowhead: // 465
+ case ESCHER_Prop_lineStartArrowWidth: // 466
+ case ESCHER_Prop_lineStartArrowLength: // 467
+ case ESCHER_Prop_lineEndArrowWidth: // 468
+ case ESCHER_Prop_lineEndArrowLength: // 469
+ case ESCHER_Prop_lineJoinStyle: // 470
+ case ESCHER_Prop_lineEndCapStyle: // 471
+ {
+ sal_uInt32 nValue;
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = FastSerializerHelper::createAttrList();
+
+ if ( rProps.GetOpt( ESCHER_Prop_lineColor, nValue ) )
+ impl_AddColor( pAttrList.get(), XML_color, nValue );
+
+ if ( rProps.GetOpt( ESCHER_Prop_lineWidth, nValue ) )
+ impl_AddInt( pAttrList.get(), XML_weight, nValue );
+
+ if ( rProps.GetOpt( ESCHER_Prop_lineDashing, nValue ) )
+ {
+ const char *pDashStyle = nullptr;
+ switch ( nValue )
+ {
+ case ESCHER_LineSolid: pDashStyle = "solid"; break;
+ case ESCHER_LineDashSys: pDashStyle = "shortdash"; break;
+ case ESCHER_LineDotSys: pDashStyle = "shortdot"; break;
+ case ESCHER_LineDashDotSys: pDashStyle = "shortdashdot"; break;
+ case ESCHER_LineDashDotDotSys: pDashStyle = "shortdashdotdot"; break;
+ case ESCHER_LineDotGEL: pDashStyle = "dot"; break;
+ case ESCHER_LineDashGEL: pDashStyle = "dash"; break;
+ case ESCHER_LineLongDashGEL: pDashStyle = "longdash"; break;
+ case ESCHER_LineDashDotGEL: pDashStyle = "dashdot"; break;
+ case ESCHER_LineLongDashDotGEL: pDashStyle = "longdashdot"; break;
+ case ESCHER_LineLongDashDotDotGEL: pDashStyle = "longdashdotdot"; break;
+ }
+ if ( pDashStyle )
+ pAttrList->add( XML_dashstyle, pDashStyle );
+ }
+
+ if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowhead, nValue ) )
+ impl_AddArrowHead( pAttrList.get(), XML_startarrow, nValue );
+
+ if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowhead, nValue ) )
+ impl_AddArrowHead( pAttrList.get(), XML_endarrow, nValue );
+
+ if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowWidth, nValue ) )
+ impl_AddArrowWidth( pAttrList.get(), XML_startarrowwidth, nValue );
+
+ if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowLength, nValue ) )
+ impl_AddArrowLength( pAttrList.get(), XML_startarrowlength, nValue );
+
+ if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowWidth, nValue ) )
+ impl_AddArrowWidth( pAttrList.get(), XML_endarrowwidth, nValue );
+
+ if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowLength, nValue ) )
+ impl_AddArrowLength( pAttrList.get(), XML_endarrowlength, nValue );
+
+ if ( rProps.GetOpt( ESCHER_Prop_lineJoinStyle, nValue ) )
+ {
+ const char *pJoinStyle = nullptr;
+ switch ( nValue )
+ {
+ case ESCHER_LineJoinBevel: pJoinStyle = "bevel"; break;
+ case ESCHER_LineJoinMiter: pJoinStyle = "miter"; break;
+ case ESCHER_LineJoinRound: pJoinStyle = "round"; break;
+ }
+ if ( pJoinStyle )
+ pAttrList->add( XML_joinstyle, pJoinStyle );
+ }
+
+ if ( rProps.GetOpt( ESCHER_Prop_lineEndCapStyle, nValue ) )
+ {
+ const char *pEndCap = nullptr;
+ switch ( nValue )
+ {
+ case ESCHER_LineEndCapRound: pEndCap = "round"; break;
+ case ESCHER_LineEndCapSquare: pEndCap = "square"; break;
+ case ESCHER_LineEndCapFlat: pEndCap = "flat"; break;
+ }
+ if ( pEndCap )
+ pAttrList->add( XML_endcap, pEndCap );
+ }
+
+ m_pSerializer->singleElementNS( XML_v, XML_stroke, pAttrList );
+ }
+ bAlreadyWritten[ ESCHER_Prop_lineColor ] = true;
+ bAlreadyWritten[ ESCHER_Prop_lineWidth ] = true;
+ bAlreadyWritten[ ESCHER_Prop_lineDashing ] = true;
+ bAlreadyWritten[ ESCHER_Prop_lineStartArrowhead ] = true;
+ bAlreadyWritten[ ESCHER_Prop_lineEndArrowhead ] = true;
+ bAlreadyWritten[ ESCHER_Prop_lineStartArrowWidth ] = true;
+ bAlreadyWritten[ ESCHER_Prop_lineStartArrowLength ] = true;
+ bAlreadyWritten[ ESCHER_Prop_lineEndArrowWidth ] = true;
+ bAlreadyWritten[ ESCHER_Prop_lineEndArrowLength ] = true;
+ bAlreadyWritten[ ESCHER_Prop_lineJoinStyle ] = true;
+ bAlreadyWritten[ ESCHER_Prop_lineEndCapStyle ] = true;
+ break;
+
+ case ESCHER_Prop_fHidden:
+ if ( !opt.nPropValue )
+ m_ShapeStyle.append( ";visibility:hidden" );
+ break;
+ case ESCHER_Prop_shadowColor:
+ case ESCHER_Prop_fshadowObscured:
+ {
+ sal_uInt32 nValue = 0;
+ bool bShadow = false;
+ bool bObscured = false;
+ if ( rProps.GetOpt( ESCHER_Prop_fshadowObscured, nValue ) )
+ {
+ bShadow = (( nValue & 0x20002 ) == 0x20002 );
+ bObscured = (( nValue & 0x10001 ) == 0x10001 );
+ }
+ if ( bShadow )
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = FastSerializerHelper::createAttrList();
+ impl_AddBool( pAttrList.get(), XML_on, bShadow );
+ impl_AddBool( pAttrList.get(), XML_obscured, bObscured );
+
+ if ( rProps.GetOpt( ESCHER_Prop_shadowColor, nValue ) )
+ impl_AddColor( pAttrList.get(), XML_color, nValue );
+
+ m_pSerializer->singleElementNS( XML_v, XML_shadow, pAttrList );
+ bAlreadyWritten[ ESCHER_Prop_fshadowObscured ] = true;
+ bAlreadyWritten[ ESCHER_Prop_shadowColor ] = true;
+ }
+ }
+ break;
+ case ESCHER_Prop_gtextUNICODE:
+ case ESCHER_Prop_gtextFont:
+ {
+ EscherPropSortStruct aUnicode;
+ if (rProps.GetOpt(ESCHER_Prop_gtextUNICODE, aUnicode))
+ {
+ SvMemoryStream aStream;
+
+ if(!opt.nProp.empty())
+ {
+ aStream.WriteBytes(opt.nProp.data(), opt.nProp.size());
+ }
+
+ aStream.Seek(0);
+ OUString aTextPathString = SvxMSDffManager::MSDFFReadZString(aStream, opt.nProp.size(), true);
+ aStream.Seek(0);
+
+ m_pSerializer->singleElementNS(XML_v, XML_path, XML_textpathok, "t");
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = FastSerializerHelper::createAttrList();
+ pAttrList->add(XML_on, "t");
+ pAttrList->add(XML_fitshape, "t");
+ pAttrList->add(XML_string, aTextPathString);
+ EscherPropSortStruct aFont;
+ OUString aStyle;
+ if (rProps.GetOpt(ESCHER_Prop_gtextFont, aFont))
+ {
+ aStream.WriteBytes(aFont.nProp.data(), aFont.nProp.size());
+ aStream.Seek(0);
+ OUString aTextPathFont = SvxMSDffManager::MSDFFReadZString(aStream, aFont.nProp.size(), true);
+ aStyle += "font-family:\"" + aTextPathFont + "\"";
+ }
+ sal_uInt32 nSize;
+ if (rProps.GetOpt(ESCHER_Prop_gtextSize, nSize))
+ {
+ float nSizeF = static_cast<sal_Int32>(nSize) / 65536.0;
+ OUString aSize = OUString::number(nSizeF);
+ aStyle += ";font-size:" + aSize + "pt";
+ }
+ if (IsWaterMarkShape(m_pSdrObject->GetName()))
+ pAttrList->add(XML_trim, "t");
+
+ if (!aStyle.isEmpty())
+ pAttrList->add(XML_style, aStyle);
+ m_pSerializer->singleElementNS(XML_v, XML_textpath, pAttrList);
+ }
+
+ bAlreadyWritten[ESCHER_Prop_gtextUNICODE] = true;
+ bAlreadyWritten[ESCHER_Prop_gtextFont] = true;
+ }
+ break;
+ case ESCHER_Prop_Rotation:
+ {
+ // The higher half of the variable contains the angle.
+ m_ShapeStyle.append(";rotation:" + OString::number(double(opt.nPropValue >> 16)));
+ bAlreadyWritten[ESCHER_Prop_Rotation] = true;
+ }
+ break;
+ case ESCHER_Prop_fNoLineDrawDash:
+ {
+ // See DffPropertyReader::ApplyLineAttributes().
+ impl_AddBool( m_pShapeAttrList.get(), XML_stroked, (opt.nPropValue & 8) != 0 );
+ bAlreadyWritten[ESCHER_Prop_fNoLineDrawDash] = true;
+ }
+ break;
+ case ESCHER_Prop_wzName:
+ {
+ SvMemoryStream aStream;
+
+ if(!opt.nProp.empty())
+ {
+ aStream.WriteBytes(opt.nProp.data(), opt.nProp.size());
+ }
+
+ aStream.Seek(0);
+ OUString idStr = SvxMSDffManager::MSDFFReadZString(aStream, opt.nProp.size(), true);
+ aStream.Seek(0);
+ if (!IsWaterMarkShape(m_pSdrObject->GetName()) && !m_bSkipwzName)
+ m_pShapeAttrList->add(XML_ID, idStr);
+
+ bAlreadyWritten[ESCHER_Prop_wzName] = true;
+ }
+ break;
+ default:
+#if OSL_DEBUG_LEVEL > 0
+ const size_t opt_nProp_size(opt.nProp.size());
+ const sal_uInt8 opt_nProp_empty(0);
+ fprintf( stderr, "TODO VMLExport::Commit(), unimplemented id: %d, value: %" SAL_PRIuUINT32 ", data: [%zu, %p]\n",
+ nId,
+ opt.nPropValue,
+ opt_nProp_size,
+ 0 == opt_nProp_size ? &opt_nProp_empty : opt.nProp.data());
+ if ( opt.nProp.size() )
+ {
+ const sal_uInt8 *pIt = opt.nProp.data();
+ fprintf( stderr, " ( " );
+ for ( int nCount = opt.nProp.size(); nCount; --nCount )
+ {
+ fprintf( stderr, "%02x ", *pIt );
+ ++pIt;
+ }
+ fprintf( stderr, ")\n" );
+ }
+#endif
+ break;
+ }
+ }
+
+ m_pSerializer->mergeTopMarks(Tag_Commit, sax_fastparser::MergeMarks::POSTPONE );
+}
+
+OString VMLExport::ShapeIdString( sal_uInt32 nId )
+{
+ if(m_bOverrideShapeIdGeneration)
+ return m_sShapeIDPrefix + OString::number( nId );
+ else
+ return "shape_" + OString::number( nId );
+}
+
+void VMLExport::AddFlipXY( )
+{
+ if (m_nShapeFlags & (ShapeFlag::FlipH | ShapeFlag::FlipV))
+ {
+ m_ShapeStyle.append( ";flip:" );
+
+ if (m_nShapeFlags & ShapeFlag::FlipH)
+ m_ShapeStyle.append( "x" );
+
+ if (m_nShapeFlags & ShapeFlag::FlipV)
+ m_ShapeStyle.append( "y" );
+ }
+}
+
+void VMLExport::AddLineDimensions( const tools::Rectangle& rRectangle )
+{
+ // style
+ if (!m_ShapeStyle.isEmpty())
+ m_ShapeStyle.append( ";" );
+
+ m_ShapeStyle.append( "position:absolute" );
+
+ AddFlipXY();
+
+ // the actual dimensions
+ OString aLeft, aTop, aRight, aBottom;
+
+ if ( mnGroupLevel == 1 )
+ {
+ const OString aPt( "pt" );
+ aLeft = OString::number( double( rRectangle.Left() ) / 20 ) + aPt;
+ aTop = OString::number( double( rRectangle.Top() ) / 20 ) + aPt;
+ aRight = OString::number( double( rRectangle.Right() ) / 20 ) + aPt;
+ aBottom = OString::number( double( rRectangle.Bottom() ) / 20 ) + aPt;
+ }
+ else
+ {
+ aLeft = OString::number( rRectangle.Left() );
+ aTop = OString::number( rRectangle.Top() );
+ aRight = OString::number( rRectangle.Right() );
+ aBottom = OString::number( rRectangle.Bottom() );
+ }
+
+ m_pShapeAttrList->add( XML_from, aLeft + "," + aTop );
+
+ m_pShapeAttrList->add( XML_to, aRight + "," + aBottom );
+}
+
+void VMLExport::AddRectangleDimensions( OStringBuffer& rBuffer, const tools::Rectangle& rRectangle, bool rbAbsolutePos)
+{
+ if ( !rBuffer.isEmpty() )
+ rBuffer.append( ";" );
+
+ if (rbAbsolutePos && !m_bInline)
+ {
+ rBuffer.append( "position:absolute;" );
+ }
+
+ if(m_bInline)
+ {
+ rBuffer.append( "width:" + OString::number( double( rRectangle.Right() - rRectangle.Left() ) / 20 ) +
+ "pt;height:" + OString::number( double( rRectangle.Bottom() - rRectangle.Top() ) / 20 ) +
+ "pt" );
+ }
+ else if ( mnGroupLevel == 1 )
+ {
+ rBuffer.append( "margin-left:" + OString::number( double( rRectangle.Left() ) / 20 ) +
+ "pt;margin-top:" + OString::number( double( rRectangle.Top() ) / 20 ) +
+ "pt;width:" + OString::number( double( rRectangle.Right() - rRectangle.Left() ) / 20 ) +
+ "pt;height:" + OString::number( double( rRectangle.Bottom() - rRectangle.Top() ) / 20 ) +
+ "pt" );
+ }
+ else
+ {
+ rBuffer.append( "left:" + OString::number( rRectangle.Left() ) +
+ ";top:" + OString::number( rRectangle.Top() ) +
+ ";width:" + OString::number( rRectangle.Right() - rRectangle.Left() ) +
+ ";height:" + OString::number( rRectangle.Bottom() - rRectangle.Top() ) );
+ }
+
+ AddFlipXY();
+}
+
+void VMLExport::AddShapeAttribute( sal_Int32 nAttribute, const OString& rValue )
+{
+ m_pShapeAttrList->add( nAttribute, rValue );
+}
+
+static std::vector<OString> lcl_getShapeTypes()
+{
+ std::vector<OString> aRet;
+
+ OUString aPath("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/filter/vml-shape-types");
+ rtl::Bootstrap::expandMacros(aPath);
+ SvFileStream aStream(aPath, StreamMode::READ);
+ if (aStream.GetError() != ERRCODE_NONE)
+ SAL_WARN("oox", "failed to open vml-shape-types");
+ OStringBuffer aLine;
+ bool bNotDone = aStream.ReadLine(aLine);
+ while (bNotDone)
+ {
+ // Filter out comments.
+ if (!o3tl::starts_with(aLine, "/"))
+ aRet.push_back(OString(aLine));
+ bNotDone = aStream.ReadLine(aLine);
+ }
+ return aRet;
+}
+
+static bool lcl_isTextBox(const SdrObject* pSdrObject)
+{
+ uno::Reference<beans::XPropertySet> xPropertySet(const_cast<SdrObject*>(pSdrObject)->getUnoShape(), uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ return false;
+ uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
+ if (!xPropertySetInfo->hasPropertyByName("TextBox"))
+ return false;
+ css::uno::Any aTextBox(xPropertySet->getPropertyValue("TextBox"));
+ if (!aTextBox.hasValue())
+ return false;
+ return aTextBox.get<bool>();
+}
+
+static OUString lcl_getAnchorIdFromGrabBag(const SdrObject* pSdrObject)
+{
+ OUString aResult;
+
+ uno::Reference<beans::XPropertySet> xShape(const_cast<SdrObject*>(pSdrObject)->getUnoShape(), uno::UNO_QUERY);
+ if (xShape->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"))
+ {
+ comphelper::SequenceAsHashMap aInteropGrabBag(xShape->getPropertyValue("InteropGrabBag"));
+ if (aInteropGrabBag.find("AnchorId") != aInteropGrabBag.end())
+ aInteropGrabBag["AnchorId"] >>= aResult;
+ }
+
+ return aResult;
+}
+
+sal_uInt32 VMLExport::GenerateShapeId()
+{
+ if(!m_bOverrideShapeIdGeneration)
+ return EscherEx::GenerateShapeId();
+ else
+ return m_nShapeIDCounter++;
+}
+
+OString VMLExport::GetVMLShapeTypeDefinition(
+ std::string_view sShapeID, const bool bIsPictureFrame )
+{
+ OString sShapeType;
+ if ( !bIsPictureFrame )
+ // We don't have a shape definition for host control in presetShapeDefinitions.xml
+ // So use a definition copied from DOCX file created with MSO
+ sShapeType = OString::Concat("<v:shapetype id=\"_x0000_t") + sShapeID +
+ "\" coordsize=\"21600,21600\" o:spt=\"" + sShapeID +
+ "\" path=\"m,l,21600l21600,21600l21600,xe\">\n"
+ "<v:stroke joinstyle=\"miter\"/>\n"
+ "<v:path shadowok=\"f\" o:extrusionok=\"f\" strokeok=\"f\" fillok=\"f\" o:connecttype=\"rect\"/>\n"
+ "<o:lock v:ext=\"edit\" shapetype=\"t\"/>\n"
+ "</v:shapetype>";
+ else
+ // We don't have a shape definition for picture frame in presetShapeDefinitions.xml
+ // So use a definition copied from DOCX file created with MSO
+ sShapeType = OString::Concat("<v:shapetype id=\"_x0000_t") + sShapeID +
+ "\" coordsize=\"21600,21600\" o:spt=\"" + sShapeID +
+ "\" o:preferrelative=\"t\" path=\"m@4@5l@4@11@9@11@9@5xe\" filled=\"f\" stroked=\"f\">\n"
+ "<v:stroke joinstyle=\"miter\"/>\n"
+ "<v:formulas>\n"
+ "<v:f eqn=\"if lineDrawn pixelLineWidth 0\"/>\n"
+ "<v:f eqn=\"sum @0 1 0\"/>\n"
+ "<v:f eqn=\"sum 0 0 @1\"/>\n"
+ "<v:f eqn=\"prod @2 1 2\"/>\n"
+ "<v:f eqn=\"prod @3 21600 pixelWidth\"/>\n"
+ "<v:f eqn=\"prod @3 21600 pixelHeight\"/>\n"
+ "<v:f eqn=\"sum @0 0 1\"/>\n"
+ "<v:f eqn=\"prod @6 1 2\"/>\n"
+ "<v:f eqn=\"prod @7 21600 pixelWidth\"/>\n"
+ "<v:f eqn=\"sum @8 21600 0\"/>\n"
+ "<v:f eqn=\"prod @7 21600 pixelHeight\"/>\n"
+ "<v:f eqn=\"sum @10 21600 0\"/>\n"
+ "</v:formulas>\n"
+ "<v:path o:extrusionok=\"f\" gradientshapeok=\"t\" o:connecttype=\"rect\"/>\n"
+ "<o:lock v:ext=\"edit\" aspectratio=\"t\"/>\n"
+ "</v:shapetype>";
+ return sShapeType;
+}
+
+sal_Int32 VMLExport::StartShape()
+{
+ if ( m_nShapeType == ESCHER_ShpInst_Nil )
+ return -1;
+
+ // some of the shapes have their own name ;-)
+ sal_Int32 nShapeElement = -1;
+ bool bReferToShapeType = false;
+ switch ( m_nShapeType )
+ {
+ case ESCHER_ShpInst_NotPrimitive: nShapeElement = XML_shape; break;
+ case ESCHER_ShpInst_Rectangle: nShapeElement = XML_rect; break;
+ case ESCHER_ShpInst_RoundRectangle: nShapeElement = XML_roundrect; break;
+ case ESCHER_ShpInst_Ellipse: nShapeElement = XML_oval; break;
+ case ESCHER_ShpInst_Arc: nShapeElement = XML_arc; break;
+ case ESCHER_ShpInst_Line: nShapeElement = XML_line; break;
+ case ESCHER_ShpInst_HostControl:
+ {
+ bReferToShapeType = true;
+ nShapeElement = XML_shape;
+ if ( !m_aShapeTypeWritten[ m_nShapeType ] )
+ {
+ m_pSerializer->write(GetVMLShapeTypeDefinition(OString::number(m_nShapeType), false));
+ m_aShapeTypeWritten[ m_nShapeType ] = true;
+ }
+ break;
+ }
+ case ESCHER_ShpInst_PictureFrame:
+ {
+ bReferToShapeType = true;
+ nShapeElement = XML_shape;
+ if ( !m_aShapeTypeWritten[ m_nShapeType ] )
+ {
+ m_pSerializer->write(GetVMLShapeTypeDefinition(OString::number(m_nShapeType), true));
+ m_aShapeTypeWritten[ m_nShapeType ] = true;
+ }
+ break;
+ }
+ default:
+ if ( m_nShapeType < ESCHER_ShpInst_COUNT )
+ {
+ nShapeElement = XML_shape;
+
+ // a predefined shape?
+ static std::vector<OString> aShapeTypes = lcl_getShapeTypes();
+ SAL_WARN_IF(m_nShapeType >= aShapeTypes.size(), "oox.vml", "Unknown shape type!");
+ if (m_nShapeType < aShapeTypes.size() && aShapeTypes[m_nShapeType] != "NULL")
+ {
+ bReferToShapeType = true;
+ if ( !m_aShapeTypeWritten[ m_nShapeType ] )
+ {
+ m_pSerializer->write(aShapeTypes[m_nShapeType]);
+ m_aShapeTypeWritten[ m_nShapeType ] = true;
+ }
+ }
+ else
+ {
+ // rectangle is probably the best fallback...
+ nShapeElement = XML_rect;
+ }
+ }
+ break;
+ }
+
+ // anchoring
+ switch (m_eHOri)
+ {
+ case text::HoriOrientation::LEFT:
+ m_ShapeStyle.append(";mso-position-horizontal:left");
+ break;
+ case text::HoriOrientation::CENTER:
+ m_ShapeStyle.append(";mso-position-horizontal:center");
+ break;
+ case text::HoriOrientation::RIGHT:
+ m_ShapeStyle.append(";mso-position-horizontal:right");
+ break;
+ case text::HoriOrientation::INSIDE:
+ m_ShapeStyle.append(";mso-position-horizontal:inside");
+ break;
+ case text::HoriOrientation::OUTSIDE:
+ m_ShapeStyle.append(";mso-position-horizontal:outside");
+ break;
+ default:
+ case text::HoriOrientation::NONE:
+ break;
+ }
+ switch (m_eHRel)
+ {
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ m_ShapeStyle.append(";mso-position-horizontal-relative:margin");
+ break;
+ case text::RelOrientation::PAGE_FRAME:
+ case text::RelOrientation::PAGE_LEFT:
+ case text::RelOrientation::PAGE_RIGHT:
+ m_ShapeStyle.append(";mso-position-horizontal-relative:page");
+ break;
+ case text::RelOrientation::CHAR:
+ m_ShapeStyle.append(";mso-position-horizontal-relative:char");
+ break;
+ default:
+ break;
+ }
+
+ switch (m_eVOri)
+ {
+ case text::VertOrientation::TOP:
+ case text::VertOrientation::LINE_TOP:
+ case text::VertOrientation::CHAR_TOP:
+ m_ShapeStyle.append(";mso-position-vertical:top");
+ break;
+ case text::VertOrientation::CENTER:
+ case text::VertOrientation::LINE_CENTER:
+ m_ShapeStyle.append(";mso-position-vertical:center");
+ break;
+ case text::VertOrientation::BOTTOM:
+ case text::VertOrientation::LINE_BOTTOM:
+ case text::VertOrientation::CHAR_BOTTOM:
+ m_ShapeStyle.append(";mso-position-vertical:bottom");
+ break;
+ default:
+ case text::VertOrientation::NONE:
+ break;
+ }
+ switch (m_eVRel)
+ {
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ m_ShapeStyle.append(";mso-position-vertical-relative:margin");
+ break;
+ case text::RelOrientation::PAGE_FRAME:
+ m_ShapeStyle.append(";mso-position-vertical-relative:page");
+ break;
+ default:
+ break;
+ }
+
+ if (!m_pSdrObject->getHyperlink().isEmpty())
+ m_pShapeAttrList->add(
+ XML_href, m_pSdrObject->getHyperlink());
+
+ m_pShapeAttrList->addNS(XML_o, XML_allowincell, m_IsFollowingTextFlow ? "t" : "f");
+
+ // add style
+ m_pShapeAttrList->add( XML_style, m_ShapeStyle.makeStringAndClear() );
+
+ OUString sAnchorId = lcl_getAnchorIdFromGrabBag(m_pSdrObject);
+ if (!sAnchorId.isEmpty())
+ m_pShapeAttrList->addNS(XML_wp14, XML_anchorId, sAnchorId);
+
+ if ( nShapeElement >= 0 && !m_pShapeAttrList->hasAttribute( XML_type ) && bReferToShapeType )
+ {
+ OString sType;
+ if (m_bUseHashMarkForType)
+ sType = "#";
+ m_pShapeAttrList->add( XML_type, sType +
+ "_x0000_t" + OString::number( m_nShapeType ) );
+ }
+
+ // start of the shape
+ m_pSerializer->startElementNS( XML_v, nShapeElement, m_pShapeAttrList );
+
+ OString const textboxStyle(m_TextboxStyle.makeStringAndClear());
+
+ // now check if we have some editeng text (not associated textbox) and we have a text exporter registered
+ const SdrTextObj* pTxtObj = dynamic_cast<const SdrTextObj*>( m_pSdrObject );
+ if (pTxtObj && m_pTextExport && msfilter::util::HasTextBoxContent(m_nShapeType) && !IsWaterMarkShape(m_pSdrObject->GetName()) && !lcl_isTextBox(m_pSdrObject))
+ {
+ std::optional<OutlinerParaObject> pParaObj;
+
+ /*
+ #i13885#
+ When the object is actively being edited, that text is not set into
+ the objects normal text object, but lives in a separate object.
+ */
+ if (pTxtObj->IsTextEditActive())
+ {
+ pParaObj = pTxtObj->CreateEditOutlinerParaObject();
+ }
+ else if (pTxtObj->GetOutlinerParaObject())
+ {
+ pParaObj = *pTxtObj->GetOutlinerParaObject();
+ }
+
+ if( pParaObj )
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> pTextboxAttrList = FastSerializerHelper::createAttrList();
+ if (!textboxStyle.isEmpty())
+ {
+ pTextboxAttrList->add(XML_style, textboxStyle);
+ }
+
+ // this is reached only in case some text is attached to the shape
+ m_pSerializer->startElementNS(XML_v, XML_textbox, pTextboxAttrList);
+ m_pTextExport->WriteOutliner(*pParaObj);
+ m_pSerializer->endElementNS(XML_v, XML_textbox);
+ }
+ }
+
+ return nShapeElement;
+}
+
+void VMLExport::EndShape( sal_Int32 nShapeElement )
+{
+ if ( nShapeElement < 0 )
+ return;
+
+ if (m_pTextExport && lcl_isTextBox(m_pSdrObject))
+ {
+ uno::Reference<drawing::XShape> xShape {const_cast<SdrObject*>(m_pSdrObject)->getUnoShape(), uno::UNO_QUERY};
+ uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
+ bool bBottomToTop = false;
+ if (xPropertySetInfo->hasPropertyByName("CustomShapeGeometry"))
+ {
+ // In this case a DrawingML DOCX was imported.
+ comphelper::SequenceAsHashMap aCustomShapeProperties(
+ xPropertySet->getPropertyValue("CustomShapeGeometry"));
+ if (aCustomShapeProperties.find("TextPreRotateAngle") != aCustomShapeProperties.end())
+ {
+ sal_Int32 nTextRotateAngle = aCustomShapeProperties["TextPreRotateAngle"].get<sal_Int32>();
+ if (nTextRotateAngle == -270)
+ bBottomToTop = true;
+ }
+ }
+ else
+ {
+ // In this case a pure VML DOCX was imported, so there is no CustomShapeGeometry.
+ auto pTextExport = m_pTextExport->GetDrawingML().GetTextExport();
+ // FIXME: somewhy pTextExport is always nullptr, we should find its reason
+ if (pTextExport)
+ {
+ auto xTextFrame = pTextExport->GetUnoTextFrame(xShape);
+ uno::Reference<beans::XPropertySet> xPropSet(xTextFrame, uno::UNO_QUERY);
+ auto aAny = xPropSet->getPropertyValue("WritingMode");
+ sal_Int16 nWritingMode;
+ if (aAny >>= nWritingMode)
+ {
+ switch (nWritingMode)
+ {
+ case text::WritingMode2::BT_LR:
+ bBottomToTop = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ rtl::Reference<sax_fastparser::FastAttributeList> pTextboxAttrList = FastSerializerHelper::createAttrList();
+ if (bBottomToTop)
+ pTextboxAttrList->add(XML_style, "mso-layout-flow-alt:bottom-to-top");
+ m_pSerializer->startElementNS(XML_v, XML_textbox, pTextboxAttrList);
+
+ m_pTextExport->WriteVMLTextBox(uno::Reference<drawing::XShape>(xPropertySet, uno::UNO_QUERY_THROW));
+
+ m_pSerializer->endElementNS(XML_v, XML_textbox);
+ }
+
+ if (m_pWrapAttrList)
+ {
+ m_pSerializer->singleElementNS(XML_w10, XML_wrap, m_pWrapAttrList);
+ }
+
+ // end of the shape
+ m_pSerializer->endElementNS( XML_v, nShapeElement );
+}
+
+OString const & VMLExport::AddSdrObject( const SdrObject& rObj,
+ bool const bIsFollowingTextFlow,
+ sal_Int16 eHOri, sal_Int16 eVOri, sal_Int16 eHRel, sal_Int16 eVRel,
+ FastAttributeList* pWrapAttrList,
+ const bool bOOxmlExport )
+{
+ m_pSdrObject = &rObj;
+ m_eHOri = eHOri;
+ m_eVOri = eVOri;
+ m_eHRel = eHRel;
+ m_eVRel = eVRel;
+ m_pWrapAttrList = pWrapAttrList;
+ m_bInline = false;
+ m_IsFollowingTextFlow = bIsFollowingTextFlow;
+ EscherEx::AddSdrObject(rObj, bOOxmlExport);
+ return m_sShapeId;
+}
+
+OString const & VMLExport::AddInlineSdrObject( const SdrObject& rObj, const bool bOOxmlExport )
+{
+ m_pSdrObject = &rObj;
+ m_eHOri = -1;
+ m_eVOri = -1;
+ m_eHRel = -1;
+ m_eVRel = -1;
+ m_pWrapAttrList.clear();
+ m_bInline = true;
+ m_IsFollowingTextFlow = true;
+ EscherEx::AddSdrObject(rObj, bOOxmlExport);
+ return m_sShapeId;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */