Files
odfstudio/oox/source/vml/vmlshape.cxx
T
Andrea Gelmini 850aab7852 Fix typos in code and comments + some reformatting.
Change-Id: I5b6d62104e46c6eb5d142e7e11666806383eed09
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210566
Tested-by: Jenkins
Reviewed-by: Julien Nabet <serval2412@yahoo.fr>
2026-09-10 15:15:58 +02:00

1622 lines
70 KiB
C++

/* -*- 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 )
, mbTextBox(false)
{
}
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;
sal_Int32 nTop;
if (o3tl::checked_add<sal_Int32>(ConversionHelper::decodeMeasureToHmm(rGraphicHelper, maTypeModel.maTop, 0, false, true),
ConversionHelper::decodeMeasureToHmm(rGraphicHelper, maTypeModel.maMarginTop, 0, false, true),
nTop))
{
SAL_WARN("oox", "overflow in addition");
nTop = 0;
}
return awt::Rectangle(nLeft, nTop, 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 ),
mbMoveWithCells( true ),
mbSizeWithCells( true )
{
}
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 = std::move(aMergedStrokeModel);
maTypeModel.maFillModel = std::move(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;
std::u16string_view tmp;
if (aType.startsWith(sShapeTypePrefix)) {
maTypeModel.moShapeType = o3tl::toInt32(aType.subView(sShapeTypePrefix.getLength()));
}
else if (aType.startsWith("_x0000_t", &tmp)) {
maTypeModel.moShapeType = o3tl::toInt32(tmp);
}
}
}
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(u"com.sun.star.text.TextFrame"_ustr))
{
uno::Reference<beans::XPropertySet> propertySet (xShape, uno::UNO_QUERY);
uno::Any aAny = propertySet->getPropertyValue(u"FrameInteropGrabBag"_ustr);
auto aGrabBag = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aAny.get< uno::Sequence<beans::PropertyValue> >());
aGrabBag.push_back(comphelper::makePropertyValue(u"VML-Z-ORDER"_ustr, maTypeModel.maZIndex.toInt32()));
if( !s_mso_next_textbox.isEmpty() )
aGrabBag.push_back(comphelper::makePropertyValue(u"mso-next-textbox"_ustr, s_mso_next_textbox));
if( !sLinkChainName.isEmpty() )
{
aGrabBag.push_back(comphelper::makePropertyValue(u"TxbxHasLink"_ustr, true));
aGrabBag.push_back(comphelper::makePropertyValue(u"Txbx-Id"_ustr, id));
aGrabBag.push_back(comphelper::makePropertyValue(u"Txbx-Seq"_ustr, seq));
aGrabBag.push_back(comphelper::makePropertyValue(u"LinkChainName"_ustr, sLinkChainName));
}
if(!maTypeModel.maRotation.isEmpty())
aGrabBag.push_back(comphelper::makePropertyValue(u"mso-rotation-angle"_ustr, ConversionHelper::decodeRotation(maTypeModel.maRotation).get()));
propertySet->setPropertyValue(u"FrameInteropGrabBag"_ustr, uno::Any(comphelper::containerToSequence(aGrabBag)));
sal_Int32 backColorTransparency = 0;
propertySet->getPropertyValue(u"BackColorTransparency"_ustr)
>>= backColorTransparency;
if (propertySet->getPropertyValue(u"FillStyle"_ustr) == FillStyle_NONE &&
backColorTransparency == 100)
{
// If there is no fill, the Word default is 100% transparency.
propertySet->setPropertyValue(u"FillTransparence"_ustr, 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(u"InteropGrabBag"_ustr) >>= 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( u"InteropGrabBag"_ustr, 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(u"com.sun.star.text.TextFrame"_ustr))
{
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(u"com.sun.star.drawing.CustomShape"_ustr))
{
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 the anchor is not inline, and is relative to left or right, then apply the margins
bool bHonorMargins = rTypeModel.maPosition == "relative" || rTypeModel.maPosition == "absolute";
if ( rTypeModel.maPositionHorizontal == "center" )
{
rPropSet.setAnyProperty(PROP_HoriOrient, Any(text::HoriOrientation::CENTER));
bHonorMargins = false;
}
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));
}
else
bHonorMargins = false;
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 // "text"
{
if (bHonorMargins)
rPropSet.setProperty(PROP_HoriOrientRelation, text::RelOrientation::PRINT_AREA);
else
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.
if (maTypeModel.mbHorizontalRule)
PropertySet(xShape).setAnyProperty(PROP_HorizontalRule, Any(true));
// 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
const bool bWrap = maTypeModel.maWrapStyle != "none";
PropertySet(xShape).setAnyProperty(PROP_TextWordWrap, Any(bWrap));
// 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);
}
if (mbTextBox)
{
// similar to drawingml::Shape::createAndInsert(...)
PropertySet(xShape).setAnyProperty(PROP_TextBox, Any(true));
}
}
}
// 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(u"com.sun.star.drawing.GroupShape"_ustr))
{
// If rotation is used, simple setPosition() is not enough.
aPropertySet.setAnyProperty(PROP_HoriOrientPosition, Any(aShapeRect.X));
aPropertySet.setAnyProperty(PROP_VertOrientPosition, Any(aShapeRect.Y));
}
}
if (!maTypeModel.mbVisible)
{
aPropertySet.setAnyProperty(PROP_Visible, uno::Any(false));
aPropertySet.setAnyProperty(PROP_Printable, uno::Any(false));
}
// 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(u"MirroredX"_ustr, true));
if (bFlipY)
aPropVec.push_back(comphelper::makePropertyValue(u"MirroredY"_ustr, 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( u"com.sun.star.drawing.GraphicObjectShape"_ustr, 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(u"com.sun.star.drawing.GroupShape"_ustr))
{
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 nWrapDistanceTop = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, maTypeModel.maWrapDistanceTop, 0, false, true);
const sal_Int32 nWrapDistanceBottom = ConversionHelper::decodeMeasureToHmm(rGraphicHelper, maTypeModel.maWrapDistanceBottom, 0, false, true);
aPropSet.setProperty(PROP_TopMargin, uno::Any(nWrapDistanceTop));
aPropSet.setProperty(PROP_BottomMargin, uno::Any(nWrapDistanceBottom));
// 9pt default left and right wrap distances for Word files
const bool bIsMSO = mrDrawing.getFilter().isMSODocument();
const OUString aWordWrapDefault = OUString::number(0x0001BE7C);
auto decodeOrDefault = [&](const OUString& rVal, bool bPixelX) -> sal_Int32 {
if (!rVal.isEmpty())
return ConversionHelper::decodeMeasureToHmm(rGraphicHelper, rVal, 0, bPixelX, true);
if (bIsMSO)
return ConversionHelper::decodeMeasureToHmm(rGraphicHelper, aWordWrapDefault, 0, bPixelX, false);
return 0;
};
aPropSet.setProperty(PROP_LeftMargin, uno::Any(decodeOrDefault(maTypeModel.maWrapDistanceLeft, true)));
aPropSet.setProperty(PROP_RightMargin, uno::Any(decodeOrDefault(maTypeModel.maWrapDistanceRight, true)));
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, u"com.sun.star.drawing.RectangleShape"_ustr )
{
}
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, u"com.sun.star.drawing.EllipseShape"_ustr )
{
}
PolyLineShape::PolyLineShape( Drawing& rDrawing ) :
SimpleShape( rDrawing, u"com.sun.star.drawing.PolyLineShape"_ustr )
{
}
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(u"com.sun.star.drawing.PolyPolygonShape"_ustr);
}
}
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, u"com.sun.star.drawing.LineShape"_ustr)
{
}
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, u"com.sun.star.drawing.OpenBezierShape"_ustr)
{
}
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( u"com.sun.star.drawing.ClosedBezierShape"_ustr );
}
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( u"com.sun.star.drawing.ClosedBezierShape"_ustr );
}
}
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, u"com.sun.star.drawing.CustomShape"_ustr )
{
}
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( u"com.sun.star.drawing.OLE2Shape"_ustr, 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(u"IsSignatureLine"_ustr, uno::Any(true));
xPropertySet->setPropertyValue(u"SignatureLineId"_ustr,
uno::Any(getShapeModel().maSignatureId));
xPropertySet->setPropertyValue(
u"SignatureLineSuggestedSignerName"_ustr,
uno::Any(getShapeModel().maSignatureLineSuggestedSignerName));
xPropertySet->setPropertyValue(
u"SignatureLineSuggestedSignerTitle"_ustr,
uno::Any(getShapeModel().maSignatureLineSuggestedSignerTitle));
xPropertySet->setPropertyValue(
u"SignatureLineSuggestedSignerEmail"_ustr,
uno::Any(getShapeModel().maSignatureLineSuggestedSignerEmail));
xPropertySet->setPropertyValue(
u"SignatureLineSigningInstructions"_ustr,
uno::Any(getShapeModel().maSignatureLineSigningInstructions));
xPropertySet->setPropertyValue(
u"SignatureLineShowSignDate"_ustr,
uno::Any(getShapeModel().mbSignatureLineShowSignDate));
xPropertySet->setPropertyValue(
u"SignatureLineCanAddComment"_ustr,
uno::Any(getShapeModel().mbSignatureLineCanAddComment));
xPropertySet->setPropertyValue(u"SignatureLineIsSigned"_ustr, uno::Any(bIsSigned));
if (!aGraphicPath.isEmpty())
{
xGraphic = rFilter.getGraphicHelper().importEmbeddedGraphic(aGraphicPath);
xPropertySet->setPropertyValue(u"SignatureLineUnsignedImage"_ustr, 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( u"com.sun.star.drawing.GroupShape"_ustr, 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(u"InteropGrabBag"_ustr) >>= aGrabBag;
sal_Int32 nLength = aGrabBag.getLength();
aGrabBag.realloc(nLength + 1);
aGrabBag.getArray()[nLength] = comphelper::makePropertyValue(u"mso-edit-as"_ustr, maTypeModel.maEditAs);
xPropertySet->setPropertyValue(u"InteropGrabBag"_ustr, 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::vml
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */