diff options
Diffstat (limited to 'oox/source/vml/vmlshape.cxx')
-rw-r--r-- | oox/source/vml/vmlshape.cxx | 1577 |
1 files changed, 1577 insertions, 0 deletions
diff --git a/oox/source/vml/vmlshape.cxx b/oox/source/vml/vmlshape.cxx new file mode 100644 index 0000000000..5284de1768 --- /dev/null +++ b/oox/source/vml/vmlshape.cxx @@ -0,0 +1,1577 @@ +/* -*- 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 <algorithm> + +#include <optional> + +#include <o3tl/safeint.hxx> +#include <oox/vml/vmlshape.hxx> +#include <utility> +#include <vcl/wmfexternal.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/PointSequenceSequence.hpp> +#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp> +#include <com/sun/star/drawing/TextVerticalAdjust.hpp> +#include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/drawing/XControlShape.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/table/BorderLine2.hpp> +#include <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/text/RelOrientation.hpp> +#include <com/sun/star/text/SizeType.hpp> +#include <com/sun/star/text/VertOrientation.hpp> +#include <com/sun/star/text/WrapTextMode.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/text/TextContentAnchorType.hpp> +#include <com/sun/star/text/GraphicCrop.hpp> +#include <com/sun/star/security/DocumentDigitalSignatures.hpp> +#include <com/sun/star/security/XDocumentDigitalSignatures.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include <com/sun/star/drawing/ColorMode.hpp> +#include <sal/log.hxx> +#include <oox/drawingml/shapepropertymap.hxx> +#include <oox/helper/graphichelper.hxx> +#include <oox/helper/propertyset.hxx> +#include <oox/ole/axcontrol.hxx> +#include <oox/ole/axcontrolfragment.hxx> +#include <oox/ole/oleobjecthelper.hxx> +#include <oox/token/properties.hxx> +#include <oox/token/tokens.hxx> +#include <oox/vml/vmldrawing.hxx> +#include <oox/vml/vmlshapecontainer.hxx> +#include <oox/vml/vmltextbox.hxx> +#include <oox/core/xmlfilterbase.hxx> +#include <oox/helper/containerhelper.hxx> +#include <svx/msdffdef.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/svdobj.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/storagehelper.hxx> +#include <o3tl/string_view.hxx> + +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::uno::Any; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; + +namespace oox::vml { + +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::graphic; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; + +using ::oox::core::XmlFilterBase; + +namespace { + +const sal_Int32 VML_SHAPETYPE_PICTUREFRAME = 75; +const sal_Int32 VML_SHAPETYPE_HOSTCONTROL = 201; + +awt::Point lclGetAbsPoint( const awt::Point& rRelPoint, const awt::Rectangle& rShapeRect, const awt::Rectangle& rCoordSys ) +{ + double fWidthRatio = static_cast< double >( rShapeRect.Width ) / rCoordSys.Width; + double fHeightRatio = static_cast< double >( rShapeRect.Height ) / rCoordSys.Height; + awt::Point aAbsPoint; + aAbsPoint.X = static_cast< sal_Int32 >( rShapeRect.X + fWidthRatio * (rRelPoint.X - rCoordSys.X) + 0.5 ); + aAbsPoint.Y = static_cast< sal_Int32 >( rShapeRect.Y + fHeightRatio * (rRelPoint.Y - rCoordSys.Y) + 0.5 ); + return aAbsPoint; +} + +awt::Rectangle lclGetAbsRect( const awt::Rectangle& rRelRect, const awt::Rectangle& rShapeRect, const awt::Rectangle& rCoordSys ) +{ + double fWidthRatio = static_cast< double >( rShapeRect.Width ) / rCoordSys.Width; + double fHeightRatio = static_cast< double >( rShapeRect.Height ) / rCoordSys.Height; + awt::Rectangle aAbsRect; + aAbsRect.X = static_cast< sal_Int32 >( rShapeRect.X + fWidthRatio * (rRelRect.X - rCoordSys.X) + 0.5 ); + aAbsRect.Y = static_cast< sal_Int32 >( rShapeRect.Y + fHeightRatio * (rRelRect.Y - rCoordSys.Y) + 0.5 ); + aAbsRect.Width = static_cast< sal_Int32 >( fWidthRatio * rRelRect.Width + 0.5 ); + aAbsRect.Height = static_cast< sal_Int32 >( fHeightRatio * rRelRect.Height + 0.5 ); + return aAbsRect; +} + +/// Count the crop value based on a crop fraction and a reference size. +sal_Int32 lclConvertCrop(std::u16string_view rCrop, sal_uInt32 nSize) +{ + if (o3tl::ends_with(rCrop, u"f")) + { + // Numeric value is specified in 1/65536-ths. + sal_uInt32 nCrop = o3tl::toUInt32(rCrop.substr(0, rCrop.size() - 1)); + return (nCrop * nSize) / 65536; + } + + return 0; +} + +} // namespace + +ShapeTypeModel::ShapeTypeModel(): + mbAutoHeight( false ), + mbVisible( true ) +{ +} + +void ShapeTypeModel::assignUsed( const ShapeTypeModel& rSource ) +{ + assignIfUsed( moShapeType, rSource.moShapeType ); + assignIfUsed( moCoordPos, rSource.moCoordPos ); + assignIfUsed( moCoordSize, rSource.moCoordSize ); + /* The style properties position, left, top, width, height, margin-left, + margin-top are not derived from shape template to shape. */ + maStrokeModel.assignUsed( rSource.maStrokeModel ); + maFillModel.assignUsed( rSource.maFillModel ); + assignIfUsed( moGraphicPath, rSource.moGraphicPath ); + assignIfUsed( moGraphicTitle, rSource.moGraphicTitle ); +} + +ShapeType::ShapeType( Drawing& rDrawing ) : + mrDrawing( rDrawing ) +{ +} + +ShapeType::~ShapeType() +{ +} + +sal_Int32 ShapeType::getShapeType() const +{ + return maTypeModel.moShapeType.value_or( 0 ); +} + +OUString ShapeType::getGraphicPath() const +{ + return maTypeModel.moGraphicPath.value_or( OUString() ); +} + +awt::Rectangle ShapeType::getCoordSystem() const +{ + Int32Pair aCoordPos = maTypeModel.moCoordPos.value_or( Int32Pair( 0, 0 ) ); + Int32Pair aCoordSize = maTypeModel.moCoordSize.value_or( Int32Pair( 1000, 1000 ) ); + if( aCoordSize.first == 0 ) + aCoordSize.first = 1; + if( aCoordSize.second == 0 ) + aCoordSize.second = 1; + return awt::Rectangle( aCoordPos.first, aCoordPos.second, aCoordSize.first, aCoordSize.second ); +} + +awt::Rectangle ShapeType::getRectangle( const ShapeParentAnchor* pParentAnchor ) const +{ + return pParentAnchor ? + lclGetAbsRect( getRelRectangle(), pParentAnchor->maShapeRect, pParentAnchor->maCoordSys ) : + getAbsRectangle(); +} + +awt::Rectangle ShapeType::getAbsRectangle() const +{ + const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper(); + + sal_Int32 nWidth = ConversionHelper::decodeMeasureToHmm( rGraphicHelper, maTypeModel.maWidth, 0, true, true ); + if ( nWidth == 0 ) + nWidth = 1; + + sal_Int32 nHeight = ConversionHelper::decodeMeasureToHmm( rGraphicHelper, maTypeModel.maHeight, 0, false, true ); + if ( nHeight == 0 ) + nHeight = 1; + + sal_Int32 nLeft; + if (o3tl::checked_add<sal_Int32>(ConversionHelper::decodeMeasureToHmm(rGraphicHelper, maTypeModel.maLeft, 0, true, true), + ConversionHelper::decodeMeasureToHmm(rGraphicHelper, maTypeModel.maMarginLeft, 0, true, true), + nLeft)) + { + SAL_WARN("oox", "overflow in addition"); + nLeft = 0; + } + if (nLeft == 0 && maTypeModel.maPosition == "absolute") + nLeft = 1; + + return awt::Rectangle( + nLeft, + ConversionHelper::decodeMeasureToHmm( rGraphicHelper, maTypeModel.maTop, 0, false, true ) + ConversionHelper::decodeMeasureToHmm( rGraphicHelper, maTypeModel.maMarginTop, 0, false, true ), + nWidth, nHeight ); +} + +awt::Rectangle ShapeType::getRelRectangle() const +{ + sal_Int32 nWidth = maTypeModel.maWidth.toInt32(); + if ( nWidth == 0 ) + nWidth = 1; + + sal_Int32 nHeight = maTypeModel.maHeight.toInt32(); + if ( nHeight == 0 ) + nHeight = 1; + + return awt::Rectangle( + maTypeModel.maLeft.toInt32(), + maTypeModel.maTop.toInt32(), + nWidth, nHeight ); +} + +ClientData::ClientData() : + mnObjType( XML_TOKEN_INVALID ), + mnTextHAlign( XML_Left ), + mnTextVAlign( XML_Top ), + mnCol( -1 ), + mnRow( -1 ), + mnChecked( VML_CLIENTDATA_UNCHECKED ), + mnDropStyle( XML_Combo ), + mnDropLines( 1 ), + mnVal( 0 ), + mnMin( 0 ), + mnMax( 0 ), + mnInc( 0 ), + mnPage( 0 ), + mnSelType( XML_Single ), + mnVTEdit( VML_CLIENTDATA_TEXT ), + mbPrintObject( true ), + mbVisible( false ), + mbDde( false ), + mbNo3D( false ), + mbNo3D2( false ), + mbMultiLine( false ), + mbVScroll( false ), + mbSecretEdit( false ) +{ +} + +ShapeModel::ShapeModel() + : mbIsSignatureLine(false) + , mbSignatureLineShowSignDate(true) + , mbSignatureLineCanAddComment(false) + , mbInGroup(false) +{ +} + +ShapeModel::~ShapeModel() +{ +} + +TextBox& ShapeModel::createTextBox(ShapeTypeModel& rModel) +{ + mxTextBox.reset( new TextBox(rModel) ); + return *mxTextBox; +} + +ClientData& ShapeModel::createClientData() +{ + mxClientData.reset( new ClientData ); + return *mxClientData; +} + +ShapeBase::ShapeBase( Drawing& rDrawing ) : + ShapeType( rDrawing ) +{ +} + +void ShapeBase::finalizeFragmentImport() +{ + if( maShapeModel.maType.getLength() <= 1 ) + return; + + OUString aType = maShapeModel.maType; + if (aType[ 0 ] == '#') + aType = aType.copy(1); + if( const ShapeType* pShapeType = mrDrawing.getShapes().getShapeTypeById( aType ) ) + { + // Make sure that the props from maTypeModel have priority over the props from + // the shape type. + StrokeModel aMergedStrokeModel; + aMergedStrokeModel.assignUsed(pShapeType->getTypeModel().maStrokeModel); + aMergedStrokeModel.assignUsed(maTypeModel.maStrokeModel); + FillModel aMergedFillModel; + aMergedFillModel.assignUsed(pShapeType->getTypeModel().maFillModel); + aMergedFillModel.assignUsed(maTypeModel.maFillModel); + + maTypeModel.assignUsed( pShapeType->getTypeModel() ); + maTypeModel.maStrokeModel = aMergedStrokeModel; + maTypeModel.maFillModel = aMergedFillModel; + } + else { + // Temporary fix, shapetype not found if referenced from different substream + // FIXME: extend scope of ShapeContainer to store all shapetypes from the document + static constexpr OUString sShapeTypePrefix = u"shapetype_"_ustr; + OUString tmp; + if (aType.startsWith(sShapeTypePrefix)) { + maTypeModel.moShapeType = o3tl::toInt32(aType.subView(sShapeTypePrefix.getLength())); + } + else if (aType.startsWith("_x0000_t", &tmp)) { + maTypeModel.moShapeType = tmp.toInt32(); + } + } +} + +OUString ShapeBase::getShapeName() const +{ + if( !maTypeModel.maShapeName.isEmpty() ) + return maTypeModel.maShapeName; + + OUString aBaseName = mrDrawing.getShapeBaseName( *this ); + if( !aBaseName.isEmpty() ) + { + sal_Int32 nShapeIdx = mrDrawing.getLocalShapeIndex( getShapeId() ); + if( nShapeIdx > 0 ) + return aBaseName + OUStringChar(' ') + OUString::number( nShapeIdx ); + } + + return OUString(); +} + +const ShapeType* ShapeBase::getChildTypeById( const OUString& ) const +{ + return nullptr; +} + +const ShapeBase* ShapeBase::getChildById( const OUString& ) const +{ + return nullptr; +} + +Reference< XShape > ShapeBase::convertAndInsert( const Reference< XShapes >& rxShapes, const ShapeParentAnchor* pParentAnchor ) const +{ + Reference< XShape > xShape; + if( mrDrawing.isShapeSupported( *this ) ) + { + /* Calculate shape rectangle. Applications may do something special + according to some imported shape client data (e.g. Excel cell anchor). */ + awt::Rectangle aShapeRect = calcShapeRectangle( pParentAnchor ); + + if( ((aShapeRect.Width > 0) || (aShapeRect.Height > 0)) && rxShapes.is() ) + { + xShape = implConvertAndInsert( rxShapes, aShapeRect ); + if( xShape.is() ) + { + // set imported or generated shape name (not supported by form controls) + PropertySet aShapeProp( xShape ); + if( aShapeProp.hasProperty( PROP_Name ) ) + aShapeProp.setProperty( PROP_Name, getShapeName() ); + uno::Reference< lang::XServiceInfo > xSInfo( xShape, uno::UNO_QUERY_THROW ); + + OUString sLinkChainName = getTypeModel().maLegacyId; + sal_Int32 id = 0; + sal_Int32 idPos = sLinkChainName.indexOf("_x"); + sal_Int32 seq = 0; + if (idPos >= 0) + { + sal_Int32 seqPos = sLinkChainName.indexOf("_s",idPos); + if (idPos < seqPos) + { + auto idPosEnd = idPos+2; + id = o3tl::toInt32(sLinkChainName.subView(idPosEnd, seqPos - idPosEnd)); + seq = o3tl::toInt32(sLinkChainName.subView(seqPos+2)); + } + } + + OUString s_mso_next_textbox; + if( getTextBox() ) + s_mso_next_textbox = getTextBox()->msNextTextbox; + if( s_mso_next_textbox.startsWith("#") ) + s_mso_next_textbox = s_mso_next_textbox.copy(1); + + if (xSInfo->supportsService("com.sun.star.text.TextFrame")) + { + uno::Reference<beans::XPropertySet> propertySet (xShape, uno::UNO_QUERY); + uno::Any aAny = propertySet->getPropertyValue("FrameInteropGrabBag"); + auto aGrabBag = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aAny.get< uno::Sequence<beans::PropertyValue> >()); + + aGrabBag.push_back(comphelper::makePropertyValue("VML-Z-ORDER", maTypeModel.maZIndex.toInt32())); + + if( !s_mso_next_textbox.isEmpty() ) + aGrabBag.push_back(comphelper::makePropertyValue("mso-next-textbox", s_mso_next_textbox)); + + if( !sLinkChainName.isEmpty() ) + { + aGrabBag.push_back(comphelper::makePropertyValue("TxbxHasLink", true)); + aGrabBag.push_back(comphelper::makePropertyValue("Txbx-Id", id)); + aGrabBag.push_back(comphelper::makePropertyValue("Txbx-Seq", seq)); + aGrabBag.push_back(comphelper::makePropertyValue("LinkChainName", sLinkChainName)); + } + + if(!maTypeModel.maRotation.isEmpty()) + aGrabBag.push_back(comphelper::makePropertyValue("mso-rotation-angle", ConversionHelper::decodeRotation(maTypeModel.maRotation).get())); + propertySet->setPropertyValue("FrameInteropGrabBag", uno::Any(comphelper::containerToSequence(aGrabBag))); + sal_Int32 backColorTransparency = 0; + propertySet->getPropertyValue("BackColorTransparency") + >>= backColorTransparency; + if (propertySet->getPropertyValue("FillStyle") == FillStyle_NONE && + backColorTransparency == 100) + { + // If there is no fill, the Word default is 100% transparency. + propertySet->setPropertyValue("FillTransparence", Any(sal_Int16(100))); + } + } + else + { + if( maTypeModel.maZIndex.toInt32() ) + { + uno::Sequence<beans::PropertyValue> aGrabBag; + uno::Reference<beans::XPropertySet> propertySet (xShape, uno::UNO_QUERY); + propertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag; + sal_Int32 length; + + length = aGrabBag.getLength(); + aGrabBag.realloc( length+1 ); + auto pGrabBag = aGrabBag.getArray(); + pGrabBag[length].Name = "VML-Z-ORDER"; + pGrabBag[length].Value <<= maTypeModel.maZIndex.toInt32(); + + if( !s_mso_next_textbox.isEmpty() ) + { + length = aGrabBag.getLength(); + aGrabBag.realloc( length+1 ); + pGrabBag = aGrabBag.getArray(); + pGrabBag[length].Name = "mso-next-textbox"; + pGrabBag[length].Value <<= s_mso_next_textbox; + } + + if( !sLinkChainName.isEmpty() ) + { + length = aGrabBag.getLength(); + aGrabBag.realloc( length+4 ); + pGrabBag = aGrabBag.getArray(); + pGrabBag[length].Name = "TxbxHasLink"; + pGrabBag[length].Value <<= true; + pGrabBag[length+1].Name = "Txbx-Id"; + pGrabBag[length+1].Value <<= id; + pGrabBag[length+2].Name = "Txbx-Seq"; + pGrabBag[length+2].Value <<= seq; + pGrabBag[length+3].Name = "LinkChainName"; + pGrabBag[length+3].Value <<= sLinkChainName; + } + propertySet->setPropertyValue( "InteropGrabBag", uno::Any(aGrabBag) ); + } + } + Reference< XControlShape > xControlShape( xShape, uno::UNO_QUERY ); + if ( xControlShape.is() && !getTypeModel().mbVisible ) + { + PropertySet aControlShapeProp( xControlShape->getControl() ); + aControlShapeProp.setProperty( PROP_EnableVisible, uno::Any( false ) ); + } + + xShape = finalImplConvertAndInsert(xShape); + /* Notify the drawing that a new shape has been inserted. For + convenience, pass the rectangle that contains position and + size of the shape. */ + bool bGroupChild = pParentAnchor != nullptr; + mrDrawing.notifyXShapeInserted( xShape, aShapeRect, *this, bGroupChild ); + } + } + else + SAL_WARN("oox", "not converting shape, as calculated rectangle is empty"); + } + return xShape; +} + +awt::Rectangle ShapeBase::getShapeRectangle() const +{ + /* Calculate shape rectangle. Applications may do something special + according to some imported shape client data (e.g. Excel cell anchor). */ + return calcShapeRectangle(nullptr); +} + +void ShapeBase::setContainer(ShapeContainer* pContainer) { mpContainer = pContainer; } + +ShapeContainer* ShapeBase::getContainer() const { return mpContainer; } + +// protected ------------------------------------------------------------------ + +awt::Rectangle ShapeBase::calcShapeRectangle( const ShapeParentAnchor* pParentAnchor ) const +{ + /* Calculate shape rectangle. Applications may do something special + according to some imported shape client data (e.g. Excel cell anchor). */ + awt::Rectangle aShapeRect; + const ClientData* pClientData = getClientData(); + if( !pClientData || !mrDrawing.convertClientAnchor( aShapeRect, pClientData->maAnchor ) ) + aShapeRect = getRectangle( pParentAnchor ); + return aShapeRect; +} + +::oox::drawingml::ShapePropertyMap ShapeBase::makeShapePropertyMap() const +{ + ::oox::drawingml::ShapePropertyMap aPropMap( mrDrawing.getFilter().getModelObjectHelper() ); + const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper(); + maTypeModel.maStrokeModel.pushToPropMap( aPropMap, rGraphicHelper ); + maTypeModel.maFillModel.pushToPropMap( aPropMap, rGraphicHelper ); + return aPropMap; +} + +void ShapeBase::convertShapeProperties( const Reference< XShape >& rxShape ) const +{ + ::oox::drawingml::ShapePropertyMap aPropMap(makeShapePropertyMap()); + + uno::Reference<lang::XServiceInfo> xSInfo(rxShape, uno::UNO_QUERY_THROW); + if (xSInfo->supportsService("com.sun.star.text.TextFrame")) + { + const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper(); + // Any other service supporting the ShadowFormat property? + maTypeModel.maShadowModel.pushToPropMap(aPropMap, rGraphicHelper); + // TextFrames have BackColor, not FillColor + if (aPropMap.hasProperty(PROP_FillColor)) + { + aPropMap.setAnyProperty(PROP_BackColor, aPropMap.getProperty(PROP_FillColor)); + aPropMap.erase(PROP_FillColor); + } + // TextFrames have BackColorTransparency, not FillTransparence + if (aPropMap.hasProperty(PROP_FillTransparence)) + { + aPropMap.setAnyProperty(PROP_BackColorTransparency, aPropMap.getProperty(PROP_FillTransparence)); + aPropMap.erase(PROP_FillTransparence); + } + // And no LineColor property; individual borders can have colors and widths + std::optional<sal_Int32> oLineWidth; + if (maTypeModel.maStrokeModel.moWeight.has_value()) + oLineWidth = ConversionHelper::decodeMeasureToHmm( + rGraphicHelper, maTypeModel.maStrokeModel.moWeight.value(), 0, false, false); + if (aPropMap.hasProperty(PROP_LineColor)) + { + uno::Reference<beans::XPropertySet> xPropertySet(rxShape, uno::UNO_QUERY); + static const sal_Int32 aBorders[] = { + PROP_TopBorder, PROP_LeftBorder, PROP_BottomBorder, PROP_RightBorder + }; + for (sal_Int32 nBorder : aBorders) + { + table::BorderLine2 aBorderLine = xPropertySet->getPropertyValue(PropertyMap::getPropertyName(nBorder)).get<table::BorderLine2>(); + aBorderLine.Color = aPropMap.getProperty(PROP_LineColor).get<sal_Int32>(); + if (oLineWidth) + aBorderLine.LineWidth = *oLineWidth; + aPropMap.setProperty(nBorder, aBorderLine); + } + aPropMap.erase(PROP_LineColor); + } + } + else if (xSInfo->supportsService("com.sun.star.drawing.CustomShape")) + { + const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper(); + maTypeModel.maTextpathModel.pushToPropMap(aPropMap, rxShape, rGraphicHelper); + } + + PropertySet( rxShape ).setProperties( aPropMap ); +} + +SimpleShape::SimpleShape( Drawing& rDrawing, OUString aService ) : + ShapeBase( rDrawing ), + maService(std::move( aService )) +{ +} + +static void lcl_setSurround(PropertySet& rPropSet, const ShapeTypeModel& rTypeModel, const GraphicHelper& rGraphicHelper) +{ + OUString aWrapType = rTypeModel.moWrapType.value_or(""); + + // Extreme negative top margin? Then the shape will end up at the top of the page, it's pointless to perform any kind of wrapping. + sal_Int32 nMarginTop = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, rTypeModel.maMarginTop, 0, false, true); + if (nMarginTop < -35277) // Less than 1000 points. + aWrapType.clear(); + + css::text::WrapTextMode nSurround = css::text::WrapTextMode_THROUGH; + if ( aWrapType == "square" || aWrapType == "tight" || + aWrapType == "through" ) + { + nSurround = css::text::WrapTextMode_PARALLEL; + if ( !rTypeModel.moWrapSide.has_value() ) + ; // leave as PARALLEL + else if ( rTypeModel.moWrapSide.value() == "left" ) + nSurround = css::text::WrapTextMode_LEFT; + else if ( rTypeModel.moWrapSide.value() == "right" ) + nSurround = css::text::WrapTextMode_RIGHT; + } + else if ( aWrapType == "topAndBottom" ) + nSurround = css::text::WrapTextMode_NONE; + + rPropSet.setProperty(PROP_Surround, static_cast<sal_Int32>(nSurround)); + rPropSet.setProperty(PROP_SurroundContour, aWrapType == "tight"); +} + +static void lcl_SetAnchorType(PropertySet& rPropSet, const ShapeTypeModel& rTypeModel, const GraphicHelper& rGraphicHelper) +{ + if ( rTypeModel.maPosition == "absolute" ) + { + // Word supports as-character (inline) and at-character only, absolute can't be inline. + rPropSet.setProperty(PROP_AnchorType, text::TextContentAnchorType_AT_CHARACTER); + // anchor is set after insertion, so reset to NONE + rPropSet.setAnyProperty(PROP_VertOrient, Any(text::VertOrientation::NONE)); + + if ( rTypeModel.maPositionVerticalRelative == "page" ) + { + rPropSet.setProperty(PROP_VertOrientRelation, text::RelOrientation::PAGE_FRAME); + } + else if ( rTypeModel.maPositionVerticalRelative == "margin" ) + { + rPropSet.setProperty(PROP_VertOrientRelation, text::RelOrientation::PAGE_PRINT_AREA); + } + else if (rTypeModel.maPositionVerticalRelative == "top-margin-area") + { + rPropSet.setProperty(PROP_VertOrientRelation, text::RelOrientation::PAGE_PRINT_AREA_TOP); + } + else if (rTypeModel.maPositionVerticalRelative == "bottom-margin-area") + { + rPropSet.setProperty(PROP_VertOrientRelation, text::RelOrientation::PAGE_PRINT_AREA_BOTTOM); + } + else + { + rPropSet.setProperty(PROP_VertOrientRelation, text::RelOrientation::FRAME); + } + } + else if( rTypeModel.maPosition == "relative" ) + { // I'm not very sure this is correct either. + rPropSet.setProperty(PROP_AnchorType, text::TextContentAnchorType_AT_PARAGRAPH); + // anchor is set after insertion, so reset to NONE + rPropSet.setAnyProperty(PROP_VertOrient, Any(text::VertOrientation::NONE)); + } + else // static (is the default) means anchored inline + { + rPropSet.setProperty(PROP_AnchorType, text::TextContentAnchorType_AS_CHARACTER); + // Use top orientation, this one seems similar to what MSO uses as inline + rPropSet.setAnyProperty(PROP_VertOrient, Any(text::VertOrientation::TOP)); + } + + if ( rTypeModel.maPositionHorizontal == "center" ) + rPropSet.setAnyProperty(PROP_HoriOrient, Any(text::HoriOrientation::CENTER)); + else if ( rTypeModel.maPositionHorizontal == "left" ) + rPropSet.setAnyProperty(PROP_HoriOrient, Any(text::HoriOrientation::LEFT)); + else if ( rTypeModel.maPositionHorizontal == "right" ) + rPropSet.setAnyProperty(PROP_HoriOrient, Any(text::HoriOrientation::RIGHT)); + else if ( rTypeModel.maPositionHorizontal == "inside" ) + { + rPropSet.setAnyProperty(PROP_HoriOrient, Any(text::HoriOrientation::LEFT)); + rPropSet.setAnyProperty(PROP_PageToggle, Any(true)); + } + else if ( rTypeModel.maPositionHorizontal == "outside" ) + { + rPropSet.setAnyProperty(PROP_HoriOrient, Any(text::HoriOrientation::RIGHT)); + rPropSet.setAnyProperty(PROP_PageToggle, Any(true)); + } + + if ( rTypeModel.maPositionHorizontalRelative == "page" ) + rPropSet.setAnyProperty(PROP_HoriOrientRelation, Any(text::RelOrientation::PAGE_FRAME)); + else if ( rTypeModel.maPositionHorizontalRelative == "margin" ) + rPropSet.setProperty(PROP_HoriOrientRelation, text::RelOrientation::PAGE_PRINT_AREA); + else if (rTypeModel.maPositionHorizontalRelative == "right-margin-area" || + rTypeModel.maPositionHorizontalRelative == "inner-margin-area") + rPropSet.setProperty(PROP_HoriOrientRelation, text::RelOrientation::PAGE_RIGHT); + else if (rTypeModel.maPositionHorizontalRelative == "left-margin-area" || + rTypeModel.maPositionHorizontalRelative == "outer-margin-area") + rPropSet.setProperty(PROP_HoriOrientRelation, text::RelOrientation::PAGE_LEFT); + else if ( rTypeModel.maPositionHorizontalRelative == "text" ) + rPropSet.setProperty(PROP_HoriOrientRelation, text::RelOrientation::FRAME); + + if ( rTypeModel.maPositionVertical == "center" ) + rPropSet.setAnyProperty(PROP_VertOrient, Any(text::VertOrientation::CENTER)); + else if ( rTypeModel.maPositionVertical == "top" ) + rPropSet.setAnyProperty(PROP_VertOrient, Any(text::VertOrientation::TOP)); + else if ( rTypeModel.maPositionVertical == "bottom" ) + rPropSet.setAnyProperty(PROP_VertOrient, Any(text::VertOrientation::BOTTOM)); + else if ( rTypeModel.maPositionVertical == "inside" ) + rPropSet.setAnyProperty(PROP_VertOrient, Any(text::VertOrientation::TOP)); + else if ( rTypeModel.maPositionVertical == "outside" ) + rPropSet.setAnyProperty(PROP_VertOrient, Any(text::VertOrientation::BOTTOM)); + + lcl_setSurround( rPropSet, rTypeModel, rGraphicHelper ); +} + +Reference< XShape > SimpleShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const awt::Rectangle& rShapeRect ) const +{ + awt::Rectangle aShapeRect(rShapeRect); + std::optional<Degree100> oRotation; + bool bFlipX = false, bFlipY = false; + // tdf#137765: skip this rotation for line shapes + if (!maTypeModel.maRotation.isEmpty() && maService != "com.sun.star.drawing.LineShape") + oRotation = ConversionHelper::decodeRotation(maTypeModel.maRotation); + if (!maTypeModel.maFlip.isEmpty()) + { + if (maTypeModel.maFlip.startsWith("x")) + { + bFlipX = true; + } + if (maTypeModel.maFlip.endsWith("y")) + { + bFlipY = true; + } + } + + Reference< XShape > xShape = mrDrawing.createAndInsertXShape( maService, rxShapes, aShapeRect ); + SdrObject* pShape = SdrObject::getSdrObjectFromXShape(xShape); + if( pShape && getShapeType() >= 0 ) + { + //The resize autoshape to fit text attr of FontWork/Word-Art should always be false + //for the fallback geometry. + sal_Int32 nType = getShapeType(); + if((mso_sptTextSimple <= nType && nType <= mso_sptTextOnRing) + || (mso_sptTextPlainText <= nType && nType <= mso_sptTextCanDown)) + { + pShape->SetMergedItem(makeSdrTextAutoGrowHeightItem(false)); + pShape->SetMergedItem(makeSdrTextAutoGrowWidthItem(false)); + } + } + convertShapeProperties( xShape ); + + // Handle left/right/top/bottom wrap distance. + // Default value of mso-wrap-distance-left/right is supposed to be 0 (see + // 19.1.2.19 of the VML spec), but Word implements a non-zero value. + // [MS-ODRAW] says the below default value in 2.3.4.9. + const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper(); + OUString aWrapDistanceLeft = OUString::number(0x0001BE7C); + if (!maTypeModel.maWrapDistanceLeft.isEmpty()) + aWrapDistanceLeft = maTypeModel.maWrapDistanceLeft; + sal_Int32 nWrapDistanceLeft = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, aWrapDistanceLeft, 0, true, false); + PropertySet(xShape).setAnyProperty(PROP_LeftMargin, uno::Any(nWrapDistanceLeft)); + OUString aWrapDistanceRight = OUString::number(0x0001BE7C); + if (!maTypeModel.maWrapDistanceRight.isEmpty()) + aWrapDistanceRight = maTypeModel.maWrapDistanceRight; + sal_Int32 nWrapDistanceRight = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, aWrapDistanceRight, 0, true, false); + PropertySet(xShape).setAnyProperty(PROP_RightMargin, uno::Any(nWrapDistanceRight)); + sal_Int32 nWrapDistanceTop = 0; + if (!maTypeModel.maWrapDistanceTop.isEmpty()) + nWrapDistanceTop = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, maTypeModel.maWrapDistanceTop, 0, false, true); + PropertySet(xShape).setAnyProperty(PROP_TopMargin, uno::Any(nWrapDistanceTop)); + sal_Int32 nWrapDistanceBottom = 0; + if (!maTypeModel.maWrapDistanceBottom.isEmpty()) + nWrapDistanceBottom = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, maTypeModel.maWrapDistanceBottom, 0, false, true); + PropertySet(xShape).setAnyProperty(PROP_BottomMargin, uno::Any(nWrapDistanceBottom)); + + if ( maService == "com.sun.star.text.TextFrame" ) + { + PropertySet( xShape ).setAnyProperty( PROP_FrameIsAutomaticHeight, Any( maTypeModel.mbAutoHeight ) ); + PropertySet( xShape ).setAnyProperty( PROP_SizeType, Any( maTypeModel.mbAutoHeight ? SizeType::MIN : SizeType::FIX ) ); + if( getTextBox()->borderDistanceSet ) + { + PropertySet( xShape ).setAnyProperty( PROP_LeftBorderDistance, Any( sal_Int32( getTextBox()->borderDistanceLeft ))); + PropertySet( xShape ).setAnyProperty( PROP_TopBorderDistance, Any( sal_Int32( getTextBox()->borderDistanceTop ))); + PropertySet( xShape ).setAnyProperty( PROP_RightBorderDistance, Any( sal_Int32( getTextBox()->borderDistanceRight ))); + PropertySet( xShape ).setAnyProperty( PROP_BottomBorderDistance, Any( sal_Int32( getTextBox()->borderDistanceBottom ))); + } + + sal_Int16 nWritingMode = text::WritingMode2::LR_TB; + if (getTextBox()->maLayoutFlow == "vertical" && maTypeModel.maLayoutFlowAlt.isEmpty()) + { + nWritingMode = text::WritingMode2::TB_RL; + } + else if (maTypeModel.maLayoutFlowAlt == "bottom-to-top") + { + nWritingMode = text::WritingMode2::BT_LR; + } + if (nWritingMode != text::WritingMode2::LR_TB) + { + PropertySet(xShape).setAnyProperty(PROP_WritingMode, uno::Any(nWritingMode)); + } + // tdf#123626 + if (!maShapeModel.maHyperlink.isEmpty()) + PropertySet(xShape).setAnyProperty(PROP_HyperLinkURL, Any(maShapeModel.maHyperlink)); + } + else + { + // FIXME Setting the relative width/height only for everything but text frames as + // TextFrames already have relative width/height feature... but currently not working + // in the way we need. + + // Set the relative width / height if any + if ( !maTypeModel.maWidthPercent.isEmpty( ) ) + { + // Only page-relative width is supported ATM + if ( maTypeModel.maWidthRelative.isEmpty() || maTypeModel.maWidthRelative == "page" ) + { + sal_Int16 nWidth = maTypeModel.maWidthPercent.toInt32() / 10; + // Only apply if nWidth != 0 + if ( nWidth ) + PropertySet( xShape ).setAnyProperty(PROP_RelativeWidth, Any( nWidth ) ); + } + } + if ( !maTypeModel.maHeightPercent.isEmpty( ) ) + { + // Only page-relative height is supported ATM + if ( maTypeModel.maHeightRelative.isEmpty() || maTypeModel.maHeightRelative == "page" ) + { + sal_Int16 nHeight = maTypeModel.maHeightPercent.toInt32() / 10; + // Only apply if nHeight != 0 + if ( nHeight ) + PropertySet( xShape ).setAnyProperty(PROP_RelativeHeight, Any( nHeight ) ); + } + } + + // drawinglayer default is center, MSO default is top. + drawing::TextVerticalAdjust eTextVerticalAdjust = drawing::TextVerticalAdjust_TOP; + if (maTypeModel.maVTextAnchor == "middle") + eTextVerticalAdjust = drawing::TextVerticalAdjust_CENTER; + else if (maTypeModel.maVTextAnchor == "bottom") + eTextVerticalAdjust = drawing::TextVerticalAdjust_BOTTOM; + PropertySet(xShape).setAnyProperty(PROP_TextVerticalAdjust, Any(eTextVerticalAdjust)); + + // tdf#97618 + if(!maTypeModel.maWrapStyle.isEmpty()) + PropertySet(xShape).setAnyProperty(PROP_TextWordWrap, Any(maTypeModel.maWrapStyle == "square")); + + // tdf#123626 + if (!maShapeModel.maHyperlink.isEmpty()) + PropertySet(xShape).setAnyProperty(PROP_Hyperlink, Any(maShapeModel.maHyperlink)); + + PropertySet(xShape).setAnyProperty(PROP_TextAutoGrowHeight, + Any(maTypeModel.mbAutoHeight)); + + if (getTextBox()) + { + getTextBox()->convert(xShape); + if (getTextBox()->borderDistanceSet) + { + awt::Size aSize = xShape->getSize(); + PropertySet(xShape).setAnyProperty(PROP_TextLeftDistance, Any(sal_Int32(getTextBox()->borderDistanceLeft))); + PropertySet(xShape).setAnyProperty(PROP_TextUpperDistance, Any(sal_Int32(getTextBox()->borderDistanceTop))); + PropertySet(xShape).setAnyProperty(PROP_TextRightDistance, Any(sal_Int32(getTextBox()->borderDistanceRight))); + PropertySet(xShape).setAnyProperty(PROP_TextLowerDistance, Any(sal_Int32(getTextBox()->borderDistanceBottom))); + xShape->setSize(aSize); + } + } + } + + // Import Legacy Fragments (if any) + if( xShape.is() && !maShapeModel.maLegacyDiagramPath.isEmpty() ) + { + Reference< XInputStream > xInStrm( mrDrawing.getFilter().openInputStream( maShapeModel.maLegacyDiagramPath ), UNO_SET_THROW ); + if( xInStrm.is() ) + PropertySet( xShape ).setProperty( PROP_LegacyFragment, xInStrm ); + } + + PropertySet aPropertySet(xShape); + if (xShape.is()) + { + if (oRotation) + { + aPropertySet.setAnyProperty(PROP_RotateAngle, Any((*oRotation).get())); + uno::Reference<lang::XServiceInfo> xServiceInfo(rxShapes, uno::UNO_QUERY); + if (!xServiceInfo->supportsService("com.sun.star.drawing.GroupShape")) + { + // If rotation is used, simple setPosition() is not enough. + aPropertySet.setAnyProperty(PROP_HoriOrientPosition, Any(aShapeRect.X)); + aPropertySet.setAnyProperty(PROP_VertOrientPosition, Any(aShapeRect.Y)); + } + } + + // custom shape geometry attributes + std::vector<css::beans::PropertyValue> aPropVec; + + // When flip has 'x' or 'y', the associated ShapeRect will be changed but direction change doesn't occur. + // It might occur internally in SdrObject of "sw" module, not here. + // The associated properties "PROP_MirroredX" and "PROP_MirroredY" have to be set here so that direction change will occur internally. + if (bFlipX) + aPropVec.push_back(comphelper::makePropertyValue("MirroredX", true)); + if (bFlipY) + aPropVec.push_back(comphelper::makePropertyValue("MirroredY", true)); + + if (!maTypeModel.maAdjustments.isEmpty()) + { + std::vector<drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentValues; + sal_Int32 nIndex = 0; + do + { + std::u16string_view aToken = o3tl::getToken(maTypeModel.maAdjustments, 0, ',', nIndex); + drawing::EnhancedCustomShapeAdjustmentValue aAdjustmentValue; + if (aToken.empty()) + aAdjustmentValue.State = css::beans::PropertyState::PropertyState_DEFAULT_VALUE; + else + aAdjustmentValue.Value <<= o3tl::toInt32(aToken); + aAdjustmentValues.push_back(aAdjustmentValue); + } while (nIndex >= 0); + + css::beans::PropertyValue aProp; + aProp.Name = "AdjustmentValues"; + aProp.Value <<= comphelper::containerToSequence(aAdjustmentValues); + aPropVec.push_back(aProp); + } + + if (!aPropVec.empty()) + aPropertySet.setAnyProperty(PROP_CustomShapeGeometry, Any(comphelper::containerToSequence(aPropVec))); + } + + lcl_SetAnchorType(aPropertySet, maTypeModel, rGraphicHelper ); + + return xShape; +} + +Reference<XShape> SimpleShape::finalImplConvertAndInsert(const css::uno::Reference<css::drawing::XShape>& rxShape) const +{ + // tdf#41466 This setting must be done here, because the position of textbox will be set as an + // effect of the PROP_TextBox property setting, and if we do this setting earlier (setting of + // properties of position and size) then the position of textbox will be set with wrong data. + // TODO: TextShape is set if we have rect shape in group; we should use the shape-with-textbox + // mechanism to handle this situation + if (getTextBox() && maService != "com.sun.star.text.TextFrame" && maService != "com.sun.star.drawing.TextShape" + && !maShapeModel.mbInGroup) + { + const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper(); + const auto& nLeft = ConversionHelper::decodeMeasureToHmm( + rGraphicHelper, maTypeModel.maMarginLeft, 0, true, true); + PropertySet aPropertySet(rxShape); + aPropertySet.setProperty(PROP_HoriOrientPosition, nLeft); + const auto& nTop = ConversionHelper::decodeMeasureToHmm( + rGraphicHelper, maTypeModel.maMarginTop, 0, true, true); + aPropertySet.setProperty(PROP_VertOrientPosition, nTop); + aPropertySet.setProperty(PROP_TextBox, true); + + // And these properties must be set after textbox creation (set PROP_Textbox property). + // Note: if you set a new property then you have to handle it in the proper + // SwTextBoxHelper::syncProperty function. + if (maTypeModel.maLayoutFlowAlt == "bottom-to-top") + aPropertySet.setAnyProperty(PROP_TextWritingMode, uno::Any(text::WritingMode2::BT_LR)); + } + return ShapeBase::finalImplConvertAndInsert(rxShape); +} +Reference< XShape > SimpleShape::createEmbeddedPictureObject( const Reference< XShapes >& rxShapes, const awt::Rectangle& rShapeRect, OUString const & rGraphicPath ) const +{ + Reference<XGraphic> xGraphic = mrDrawing.getFilter().getGraphicHelper().importEmbeddedGraphic(rGraphicPath); + return SimpleShape::createPictureObject(rxShapes, rShapeRect, xGraphic); +} + +Reference< XShape > SimpleShape::createPictureObject(const Reference< XShapes >& rxShapes, + const awt::Rectangle& rShapeRect, + uno::Reference<graphic::XGraphic> const & rxGraphic) const +{ + Reference< XShape > xShape = mrDrawing.createAndInsertXShape( "com.sun.star.drawing.GraphicObjectShape", rxShapes, rShapeRect ); + if( xShape.is() ) + { + PropertySet aPropSet(xShape); + if (rxGraphic.is()) + { + aPropSet.setProperty(PROP_Graphic, rxGraphic); + } + uno::Reference< lang::XServiceInfo > xServiceInfo(rxShapes, uno::UNO_QUERY); + // If the shape has an absolute position, set the properties accordingly, unless we're inside a group shape. + if ( maTypeModel.maPosition == "absolute" && !xServiceInfo->supportsService("com.sun.star.drawing.GroupShape")) + { + aPropSet.setProperty(PROP_HoriOrientPosition, rShapeRect.X); + aPropSet.setProperty(PROP_VertOrientPosition, rShapeRect.Y); + aPropSet.setProperty(PROP_Opaque, false); + } + // fdo#70457: preserve rotation information + if ( !maTypeModel.maRotation.isEmpty() ) + aPropSet.setAnyProperty(PROP_RotateAngle, Any(ConversionHelper::decodeRotation(maTypeModel.maRotation).get())); + + const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper(); + lcl_SetAnchorType(aPropSet, maTypeModel, rGraphicHelper); + + const sal_Int32 nWrapDistanceLeft = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, maTypeModel.maWrapDistanceLeft, 0, true, true); + const sal_Int32 nWrapDistanceRight = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, maTypeModel.maWrapDistanceRight, 0, true, true); + const sal_Int32 nWrapDistanceTop = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, maTypeModel.maWrapDistanceTop, 0, false, true); + const sal_Int32 nWrapDistanceBottom = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, maTypeModel.maWrapDistanceBottom, 0, false, true); + aPropSet.setProperty(PROP_LeftMargin, uno::Any(nWrapDistanceLeft)); + aPropSet.setProperty(PROP_RightMargin, uno::Any(nWrapDistanceRight)); + aPropSet.setProperty(PROP_TopMargin, uno::Any(nWrapDistanceTop)); + aPropSet.setProperty(PROP_BottomMargin, uno::Any(nWrapDistanceBottom)); + + if (maTypeModel.moCropBottom.has_value() || maTypeModel.moCropLeft.has_value() || maTypeModel.moCropRight.has_value() || maTypeModel.moCropTop.has_value()) + { + text::GraphicCrop aGraphicCrop; + awt::Size aOriginalSize = rGraphicHelper.getOriginalSize(rxGraphic); + + if (maTypeModel.moCropBottom.has_value()) + aGraphicCrop.Bottom = lclConvertCrop(maTypeModel.moCropBottom.value(), aOriginalSize.Height); + if (maTypeModel.moCropLeft.has_value()) + aGraphicCrop.Left = lclConvertCrop(maTypeModel.moCropLeft.value(), aOriginalSize.Width); + if (maTypeModel.moCropRight.has_value()) + aGraphicCrop.Right = lclConvertCrop(maTypeModel.moCropRight.value(), aOriginalSize.Width); + if (maTypeModel.moCropTop.has_value()) + aGraphicCrop.Top = lclConvertCrop(maTypeModel.moCropTop.value(), aOriginalSize.Height); + + aPropSet.setProperty(PROP_GraphicCrop, aGraphicCrop); + } + + if (maTypeModel.mnGain == -70 && maTypeModel.mnBlacklevel == 70) + { + // Map MSO 'washout' to our watermark colormode. + aPropSet.setProperty(PROP_GraphicColorMode, uno::Any(drawing::ColorMode_WATERMARK)); + } + } + return xShape; +} + +RectangleShape::RectangleShape( Drawing& rDrawing ) : + SimpleShape( rDrawing, "com.sun.star.drawing.RectangleShape" ) +{ +} + +Reference<XShape> RectangleShape::implConvertAndInsert(const Reference<XShapes>& rxShapes, const awt::Rectangle& rShapeRect) const +{ + OUString aGraphicPath = getGraphicPath(); + + // try to create a picture object + if(!aGraphicPath.isEmpty()) + return SimpleShape::createEmbeddedPictureObject(rxShapes, rShapeRect, aGraphicPath); + + // default: try to create a rectangle shape + Reference<XShape> xShape = SimpleShape::implConvertAndInsert(rxShapes, rShapeRect); + OUString sArcsize = maTypeModel.maArcsize; + if ( !sArcsize.isEmpty( ) ) + { + sal_Unicode cLastChar = sArcsize[sArcsize.getLength() - 1]; + sal_Int32 nValue = o3tl::toInt32(sArcsize.subView( 0, sArcsize.getLength() - 1 )); + // Get the smallest half-side + double size = std::min( rShapeRect.Height, rShapeRect.Width ) / 2.0; + sal_Int32 nRadius = 0; + if ( cLastChar == 'f' ) + nRadius = size * nValue / 65536; + else if ( cLastChar == '%' ) + nRadius = size * nValue / 100; + PropertySet( xShape ).setAnyProperty( PROP_CornerRadius, Any( nRadius ) ); + } + return xShape; +} + +EllipseShape::EllipseShape( Drawing& rDrawing ) : + SimpleShape( rDrawing, "com.sun.star.drawing.EllipseShape" ) +{ +} + +PolyLineShape::PolyLineShape( Drawing& rDrawing ) : + SimpleShape( rDrawing, "com.sun.star.drawing.PolyLineShape" ) +{ +} + +Reference< XShape > PolyLineShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const awt::Rectangle& rShapeRect ) const +{ + ::std::vector<awt::Point> aAbsPoints; + awt::Rectangle aCoordSys = getCoordSystem(); + if (!maShapeModel.maPoints.empty() && (aCoordSys.Width > 0) && (aCoordSys.Height > 0)) + { + for (auto const& point : maShapeModel.maPoints) + aAbsPoints.push_back(lclGetAbsPoint(point, rShapeRect, aCoordSys)); + // A polyline cannot be filled but only a polygon. We treat first point == last point as + // indicator for being closed. In that case we force to type PolyPolygonShape. + if (aAbsPoints.size() > 2 && aAbsPoints.front().X == aAbsPoints.back().X + && aAbsPoints.front().Y == aAbsPoints.back().Y) + { + const_cast<PolyLineShape*>(this)->setService("com.sun.star.drawing.PolyPolygonShape"); + } + } + + Reference<XShape> xShape = SimpleShape::implConvertAndInsert(rxShapes, rShapeRect); + + // polygon path + + if (!aAbsPoints.empty()) + { + PointSequenceSequence aPointSeq{ comphelper::containerToSequence( aAbsPoints ) }; + PropertySet aPropSet( xShape ); + aPropSet.setProperty( PROP_PolyPolygon, aPointSeq ); + } + return xShape; +} + +namespace +{ + void doMirrorX(SdrObject* pShape) + { + Point aCenter(pShape->GetSnapRect().Center()); + Point aPoint2(aCenter); + aPoint2.setY(aPoint2.getY() + 1); + pShape->NbcMirror(aCenter, aPoint2); + } + + void doMirrorY(SdrObject* pShape) + { + Point aCenter(pShape->GetSnapRect().Center()); + Point aPoint2(aCenter); + aPoint2.setX(aPoint2.getX() + 1); + pShape->NbcMirror(aCenter, aPoint2); + } + + void handleMirroring(const ShapeTypeModel& rTypeModel, const Reference<XShape>& rxShape) + { + if (!rTypeModel.maFlip.isEmpty()) + { + if (SdrObject* pShape = SdrObject::getSdrObjectFromXShape(rxShape)) + { + if (rTypeModel.maFlip.startsWith("x")) + doMirrorX(pShape); + if (rTypeModel.maFlip.endsWith("y")) + doMirrorY(pShape); + } + } + } + + void handleRotation(const ShapeTypeModel& rTypeModel, const Reference<XShape>& rxShape) + { + if (!rTypeModel.maRotation.isEmpty()) + { + if (SdrObject* pShape = SdrObject::getSdrObjectFromXShape(rxShape)) + { + // The needed factor -1 for opposite direction and factor 100 for Degree100 is + // contained in method decodeRotation(). + Degree100 nAngle(ConversionHelper::decodeRotation(rTypeModel.maRotation)); + pShape->NbcRotate(pShape->GetSnapRect().Center(), nAngle); + } + } + } +} + +LineShape::LineShape(Drawing& rDrawing) + : SimpleShape(rDrawing, "com.sun.star.drawing.LineShape") +{ +} + +Reference<XShape> LineShape::implConvertAndInsert(const Reference<XShapes>& rxShapes, const awt::Rectangle& rShapeRect) const +{ + Reference<XShape> xShape = SimpleShape::implConvertAndInsert(rxShapes, rShapeRect); + // tdf#137765 + handleRotation(maTypeModel, xShape); + // tdf#97517 tdf#137678 + // The MirroredX and MirroredY properties (in the CustomShapeGeometry property) are not + // supported for the LineShape by UNO, so we have to make the mirroring here. + handleMirroring(maTypeModel, xShape); + return xShape; +} + +awt::Rectangle LineShape::getAbsRectangle() const +{ + const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper(); + awt::Rectangle aShapeRect; + sal_Int32 nIndex = 0; + + aShapeRect.X = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, o3tl::getToken(maShapeModel.maFrom, 0, ',', nIndex), 0, true, true); + aShapeRect.Y = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, o3tl::getToken(maShapeModel.maFrom, 0, ',', nIndex), 0, false, true); + nIndex = 0; + aShapeRect.Width = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, o3tl::getToken(maShapeModel.maTo, 0, ',', nIndex), 0, true, true) - aShapeRect.X; + aShapeRect.Height = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, o3tl::getToken(maShapeModel.maTo, 0, ',', nIndex), 0, false, true) - aShapeRect.Y; + return aShapeRect; +} + +awt::Rectangle LineShape::getRelRectangle() const +{ + awt::Rectangle aShapeRect; + sal_Int32 nIndex = 0; + + aShapeRect.X = o3tl::toInt32(o3tl::getToken(maShapeModel.maFrom, 0, ',', nIndex)); + aShapeRect.Y = o3tl::toInt32(o3tl::getToken(maShapeModel.maFrom, 0, ',', nIndex)); + nIndex = 0; + aShapeRect.Width = o3tl::toInt32(o3tl::getToken(maShapeModel.maTo, 0, ',', nIndex)) - aShapeRect.X; + aShapeRect.Height = o3tl::toInt32(o3tl::getToken(maShapeModel.maTo, 0, ',', nIndex)) - aShapeRect.Y; + return aShapeRect; +} + +BezierShape::BezierShape(Drawing& rDrawing) + : SimpleShape(rDrawing, "com.sun.star.drawing.OpenBezierShape") +{ +} + +Reference< XShape > BezierShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const awt::Rectangle& rShapeRect ) const +{ + // If we have an 'x' in the last part of the path it means it is closed... + sal_Int32 nPos = maShapeModel.maVmlPath.lastIndexOf(','); + if ( nPos != -1 && maShapeModel.maVmlPath.indexOf('x', nPos) != -1 ) + { + const_cast<BezierShape*>( this )->setService( "com.sun.star.drawing.ClosedBezierShape" ); + } + + awt::Rectangle aCoordSys = getCoordSystem(); + PolyPolygonBezierCoords aBezierCoords; + + if( (aCoordSys.Width > 0) && (aCoordSys.Height > 0) ) + { + const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper(); + + // Bezier paths may consist of one or more sub-paths + typedef ::std::vector< ::std::vector< PolygonFlags > > FlagsList; + std::vector< ::std::vector< awt::Point > > aCoordLists; + FlagsList aFlagLists; + + // Curve defined by to, from, control1 and control2 attributes + if ( maShapeModel.maVmlPath.isEmpty() ) + { + aCoordLists.emplace_back( ); + aFlagLists.emplace_back( ); + sal_Int32 nIndex = 0; + + // Start point + aCoordLists[ 0 ].emplace_back( + ConversionHelper::decodeMeasureToHmm( rGraphicHelper, o3tl::getToken(maShapeModel.maFrom, 0, ',', nIndex ), 0, true, true ), + ConversionHelper::decodeMeasureToHmm( rGraphicHelper, o3tl::getToken(maShapeModel.maFrom, 0, ',', nIndex ), 0, false, true ) ); + // Control point 1 + aCoordLists[ 0 ].emplace_back( + ConversionHelper::decodeMeasureToHmm( rGraphicHelper, o3tl::getToken(maShapeModel.maControl1, 0, ',', nIndex ), 0, true, true ), + ConversionHelper::decodeMeasureToHmm( rGraphicHelper, o3tl::getToken(maShapeModel.maControl1, 0, ',', nIndex ), 0, false, true ) ); + // Control point 2 + aCoordLists[ 0 ].emplace_back( + ConversionHelper::decodeMeasureToHmm( rGraphicHelper, o3tl::getToken(maShapeModel.maControl2, 0, ',', nIndex ), 0, true, true ), + ConversionHelper::decodeMeasureToHmm( rGraphicHelper, o3tl::getToken(maShapeModel.maControl2, 0, ',', nIndex ), 0, false, true ) ); + // End point + aCoordLists[ 0 ].emplace_back( + ConversionHelper::decodeMeasureToHmm( rGraphicHelper, o3tl::getToken(maShapeModel.maTo, 0, ',', nIndex ), 0, true, true ), + ConversionHelper::decodeMeasureToHmm( rGraphicHelper, o3tl::getToken(maShapeModel.maTo, 0, ',', nIndex ), 0, false, true ) ); + + // First and last points are normals, points 2 and 4 are controls + aFlagLists[ 0 ].resize( aCoordLists[ 0 ].size(), PolygonFlags_CONTROL ); + aFlagLists[ 0 ][ 0 ] = PolygonFlags_NORMAL; + aFlagLists[ 0 ].back() = PolygonFlags_NORMAL; + } + // Curve defined by path attribute + else + { + // Parse VML path string and convert to absolute coordinates + ConversionHelper::decodeVmlPath( aCoordLists, aFlagLists, maShapeModel.maVmlPath ); + + for (auto & coordList : aCoordLists) + for (auto & point : coordList) + { + point = lclGetAbsPoint( point, rShapeRect, aCoordSys ); + } + } + + aBezierCoords.Coordinates.realloc( aCoordLists.size() ); + auto pCoordinates = aBezierCoords.Coordinates.getArray(); + for ( size_t i = 0; i < aCoordLists.size(); i++ ) + pCoordinates[i] = comphelper::containerToSequence( aCoordLists[i] ); + + aBezierCoords.Flags.realloc( aFlagLists.size() ); + auto pFlags = aBezierCoords.Flags.getArray(); + for ( size_t i = 0; i < aFlagLists.size(); i++ ) + pFlags[i] = comphelper::containerToSequence( aFlagLists[i] ); + + if( !aCoordLists.front().empty() && !aCoordLists.back().empty() + && aCoordLists.front().front().X == aCoordLists.back().back().X + && aCoordLists.front().front().Y == aCoordLists.back().back().Y ) + { // HACK: If the shape is in fact closed, which can be found out only when the path is known, + // force to closed bezier shape (otherwise e.g. fill won't work). + const_cast< BezierShape* >( this )->setService( "com.sun.star.drawing.ClosedBezierShape" ); + } + } + + Reference< XShape > xShape = SimpleShape::implConvertAndInsert( rxShapes, rShapeRect ); + + if( aBezierCoords.Coordinates.hasElements()) + { + PropertySet aPropSet( xShape ); + aPropSet.setProperty( PROP_PolyPolygonBezier, aBezierCoords ); + } + + // tdf#105875 handle rotation + // Note: must rotate before flip! + handleRotation(maTypeModel, xShape); + + // Handle horizontal and vertical flip. + handleMirroring(maTypeModel, xShape); + + return xShape; +} + +CustomShape::CustomShape( Drawing& rDrawing ) : + SimpleShape( rDrawing, "com.sun.star.drawing.CustomShape" ) +{ +} + +Reference< XShape > CustomShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const awt::Rectangle& rShapeRect ) const +{ + // try to create a custom shape + Reference< XShape > xShape = SimpleShape::implConvertAndInsert( rxShapes, rShapeRect ); + if( xShape.is() ) try + { + // create the custom shape geometry + Reference< XEnhancedCustomShapeDefaulter > xDefaulter( xShape, UNO_QUERY_THROW ); + xDefaulter->createCustomShapeDefaults( OUString::number( getShapeType() ) ); + // convert common properties + convertShapeProperties( xShape ); + } + catch( Exception& ) + { + } + return xShape; +} + +ComplexShape::ComplexShape( Drawing& rDrawing ) : + CustomShape( rDrawing ) +{ +} + +Reference< XShape > ComplexShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const awt::Rectangle& rShapeRect ) const +{ + XmlFilterBase& rFilter = mrDrawing.getFilter(); + sal_Int32 nShapeType = getShapeType(); + OUString aGraphicPath = getGraphicPath(); + + // try to find registered OLE object info + if( const OleObjectInfo* pOleObjectInfo = mrDrawing.getOleObjectInfo( maTypeModel.maShapeId ) ) + { + SAL_WARN_IF( + nShapeType != VML_SHAPETYPE_PICTUREFRAME, "oox", + "ComplexShape::implConvertAndInsert - unexpected shape type"); + + // if OLE object is embedded into a DrawingML shape (PPTX), do not create it here + if( pOleObjectInfo->mbDmlShape ) + return Reference< XShape >(); + + PropertyMap aOleProps; + awt::Size aOleSize( rShapeRect.Width, rShapeRect.Height ); + if( rFilter.getOleObjectHelper().importOleObject( aOleProps, *pOleObjectInfo, aOleSize ) ) + { + Reference< XShape > xShape = mrDrawing.createAndInsertXShape( "com.sun.star.drawing.OLE2Shape", rxShapes, rShapeRect ); + if( xShape.is() ) + { + // set the replacement graphic + if( !aGraphicPath.isEmpty() ) + { + WmfExternal aExtHeader; + aExtHeader.mapMode = 8; + aExtHeader.xExt = rShapeRect.Width; + aExtHeader.yExt = rShapeRect.Height; + + Reference< XGraphic > xGraphic = rFilter.getGraphicHelper().importEmbeddedGraphic(aGraphicPath, &aExtHeader); + if (xGraphic.is()) + aOleProps.setProperty( PROP_Graphic, xGraphic); + } + + PropertySet aPropSet( xShape ); + aPropSet.setProperties( aOleProps ); + + return xShape; + } + } + } + + // try to find registered form control info + const ControlInfo* pControlInfo = mrDrawing.getControlInfo( maTypeModel.maShapeId ); + if( pControlInfo && !pControlInfo->maFragmentPath.isEmpty() ) + { + if( !pControlInfo->maName.isEmpty() ) + { + // load the control properties from fragment + ::oox::ole::EmbeddedControl aControl(pControlInfo->maName); + if( rFilter.importFragment( new ::oox::ole::AxControlFragment( rFilter, pControlInfo->maFragmentPath, aControl ) ) ) + { + // create and return the control shape (including control model) + sal_Int32 nCtrlIndex = -1; + Reference< XShape > xShape = mrDrawing.createAndInsertXControlShape( aControl, rxShapes, rShapeRect, nCtrlIndex ); + + if (pControlInfo->mbTextContentShape) + { + PropertySet aPropertySet(xShape); + lcl_SetAnchorType(aPropertySet, maTypeModel, mrDrawing.getFilter().getGraphicHelper()); + } + // on error, proceed and try to create picture from replacement image + if( xShape.is() ) + return xShape; + } + } + } + + // host application wants to create the shape (do not try failed OLE controls again) + if( (nShapeType == VML_SHAPETYPE_HOSTCONTROL) && !pControlInfo ) + { + OSL_ENSURE( getClientData(), "ComplexShape::implConvertAndInsert - missing client data" ); + Reference< XShape > xShape = mrDrawing.createAndInsertClientXShape( *this, rxShapes, rShapeRect ); + if( xShape.is() ) + return xShape; + } + + + if( getShapeModel().mbIsSignatureLine ) + { + uno::Reference<graphic::XGraphic> xGraphic; + bool bIsSigned(false); + try + { + // Get the document signatures + Reference<security::XDocumentDigitalSignatures> xSignatures( + security::DocumentDigitalSignatures::createDefault( + comphelper::getProcessComponentContext())); + + uno::Reference<embed::XStorage> xStorage + = comphelper::OStorageHelper::GetStorageOfFormatFromURL( + ZIP_STORAGE_FORMAT_STRING, mrDrawing.getFilter().getFileUrl(), + embed::ElementModes::READ); + SAL_WARN_IF(!xStorage.is(), "oox.vml", "No xStorage!"); + + const uno::Sequence<security::DocumentSignatureInformation> xSignatureInfo + = xSignatures->verifyScriptingContentSignatures(xStorage, + uno::Reference<io::XInputStream>()); + + // Try to find matching signature line image - if none exists that is fine, + // then the signature line is not digitally signed. + auto pSignInfo = std::find_if(xSignatureInfo.begin(), xSignatureInfo.end(), + [this](const security::DocumentSignatureInformation& rSigInfo) { + return rSigInfo.SignatureLineId == getShapeModel().maSignatureId; }); + if (pSignInfo != xSignatureInfo.end()) + { + bIsSigned = true; + if (pSignInfo->SignatureIsValid) + { + // Signature is valid, use the 'valid' image + SAL_WARN_IF(!pSignInfo->ValidSignatureLineImage.is(), "oox.vml", + "No ValidSignatureLineImage!"); + xGraphic = pSignInfo->ValidSignatureLineImage; + } + else + { + // Signature is invalid, use the 'invalid' image + SAL_WARN_IF(!pSignInfo->InvalidSignatureLineImage.is(), "oox.vml", + "No InvalidSignatureLineImage!"); + xGraphic = pSignInfo->InvalidSignatureLineImage; + } + } + } + catch (css::uno::Exception&) + { + // DocumentDigitalSignatures service not available. + // We continue by rendering the "unsigned" shape instead. + } + + Reference< XShape > xShape; + if (xGraphic.is()) + { + // If available, use the signed image from the signature + xShape = SimpleShape::createPictureObject(rxShapes, rShapeRect, xGraphic); + } + else + { + // Create shape with the fallback "unsigned" image + xShape = SimpleShape::createEmbeddedPictureObject(rxShapes, rShapeRect, aGraphicPath); + } + + // Store signature line properties + uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY); + xPropertySet->setPropertyValue("IsSignatureLine", uno::Any(true)); + xPropertySet->setPropertyValue("SignatureLineId", + uno::Any(getShapeModel().maSignatureId)); + xPropertySet->setPropertyValue( + "SignatureLineSuggestedSignerName", + uno::Any(getShapeModel().maSignatureLineSuggestedSignerName)); + xPropertySet->setPropertyValue( + "SignatureLineSuggestedSignerTitle", + uno::Any(getShapeModel().maSignatureLineSuggestedSignerTitle)); + xPropertySet->setPropertyValue( + "SignatureLineSuggestedSignerEmail", + uno::Any(getShapeModel().maSignatureLineSuggestedSignerEmail)); + xPropertySet->setPropertyValue( + "SignatureLineSigningInstructions", + uno::Any(getShapeModel().maSignatureLineSigningInstructions)); + xPropertySet->setPropertyValue( + "SignatureLineShowSignDate", + uno::Any(getShapeModel().mbSignatureLineShowSignDate)); + xPropertySet->setPropertyValue( + "SignatureLineCanAddComment", + uno::Any(getShapeModel().mbSignatureLineCanAddComment)); + xPropertySet->setPropertyValue("SignatureLineIsSigned", uno::Any(bIsSigned)); + + if (!aGraphicPath.isEmpty()) + { + xGraphic = rFilter.getGraphicHelper().importEmbeddedGraphic(aGraphicPath); + xPropertySet->setPropertyValue("SignatureLineUnsignedImage", uno::Any(xGraphic)); + } + return xShape; + } + + // try to create a picture object + if( !aGraphicPath.isEmpty() ) + { + Reference<XShape> xShape = SimpleShape::createEmbeddedPictureObject(rxShapes, rShapeRect, aGraphicPath); + // AS_CHARACTER shape: vertical orientation default is bottom, MSO default is top. + if ( maTypeModel.maPosition != "absolute" && maTypeModel.maPosition != "relative" ) + PropertySet( xShape ).setAnyProperty( PROP_VertOrient, Any(text::VertOrientation::TOP)); + + // Apply stroke props from the type model. + oox::drawingml::ShapePropertyMap aPropMap(mrDrawing.getFilter().getModelObjectHelper()); + const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper(); + maTypeModel.maStrokeModel.pushToPropMap(aPropMap, rGraphicHelper); + // And, fill-color properties as well... + maTypeModel.maFillModel.pushToPropMap(aPropMap, rGraphicHelper); + PropertySet(xShape).setProperties(aPropMap); + + return xShape; + } + + // default: try to create a custom shape + return CustomShape::implConvertAndInsert( rxShapes, rShapeRect ); +} + +GroupShape::GroupShape( Drawing& rDrawing ) : + ShapeBase( rDrawing ), + mxChildren( new ShapeContainer( rDrawing ) ) +{ +} + +GroupShape::~GroupShape() +{ +} + +void GroupShape::finalizeFragmentImport() +{ + // basic shape processing + ShapeBase::finalizeFragmentImport(); + // finalize all child shapes + mxChildren->finalizeFragmentImport(); +} + +const ShapeType* GroupShape::getChildTypeById( const OUString& rShapeId ) const +{ + return mxChildren->getShapeTypeById( rShapeId ); +} + +const ShapeBase* GroupShape::getChildById( const OUString& rShapeId ) const +{ + return mxChildren->getShapeById( rShapeId ); +} + +Reference< XShape > GroupShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const awt::Rectangle& rShapeRect ) const +{ + Reference< XShape > xGroupShape; + // check that this shape contains children and a valid coordinate system + ShapeParentAnchor aParentAnchor; + aParentAnchor.maShapeRect = rShapeRect; + aParentAnchor.maCoordSys = getCoordSystem(); + if( !mxChildren->empty() && (aParentAnchor.maCoordSys.Width > 0) && (aParentAnchor.maCoordSys.Height > 0) ) try + { + xGroupShape = mrDrawing.createAndInsertXShape( "com.sun.star.drawing.GroupShape", rxShapes, rShapeRect ); + Reference< XShapes > xChildShapes( xGroupShape, UNO_QUERY_THROW ); + mxChildren->convertAndInsert( xChildShapes, &aParentAnchor ); + if( !xChildShapes->hasElements() ) + { + SAL_WARN("oox", "no child shape has been created - deleting the group shape"); + rxShapes->remove( xGroupShape ); + xGroupShape.clear(); + } + } + catch( Exception& ) + { + } + + uno::Reference<beans::XPropertySet> xPropertySet; + if (!maTypeModel.maEditAs.isEmpty()) + xPropertySet = uno::Reference<beans::XPropertySet>(xGroupShape, uno::UNO_QUERY); + if (xPropertySet.is()) + { + uno::Sequence<beans::PropertyValue> aGrabBag; + xPropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag; + sal_Int32 nLength = aGrabBag.getLength(); + aGrabBag.realloc(nLength + 1); + aGrabBag.getArray()[nLength] = comphelper::makePropertyValue("mso-edit-as", maTypeModel.maEditAs); + xPropertySet->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag)); + } + // Make sure group shapes are inline as well, unless there is an explicit different style. + PropertySet aPropertySet(xGroupShape); + const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper(); + lcl_SetAnchorType(aPropertySet, maTypeModel, rGraphicHelper); + if (!maTypeModel.maRotation.isEmpty()) + aPropertySet.setAnyProperty(PROP_RotateAngle, Any(ConversionHelper::decodeRotation(maTypeModel.maRotation).get())); + return xGroupShape; +} + +} // namespace oox + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |