diff options
Diffstat (limited to 'oox/source/drawingml/shape.cxx')
-rw-r--r-- | oox/source/drawingml/shape.cxx | 2486 |
1 files changed, 2486 insertions, 0 deletions
diff --git a/oox/source/drawingml/shape.cxx b/oox/source/drawingml/shape.cxx new file mode 100644 index 0000000000..c42a9f7820 --- /dev/null +++ b/oox/source/drawingml/shape.cxx @@ -0,0 +1,2486 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_wasm_strip.h> + +#include <oox/drawingml/shape.hxx> +#include <drawingml/customshapeproperties.hxx> +#include <oox/drawingml/theme.hxx> +#include <drawingml/fillproperties.hxx> +#include <drawingml/fontworkhelpers.hxx> +#include <drawingml/graphicproperties.hxx> +#include <drawingml/lineproperties.hxx> +#include <drawingml/presetgeometrynames.hxx> +#include <drawingml/shape3dproperties.hxx> +#include "effectproperties.hxx" +#include <oox/drawingml/shapepropertymap.hxx> +#include <drawingml/textbody.hxx> +#include <drawingml/textparagraph.hxx> +#include <drawingml/ThemeOverrideFragmentHandler.hxx> +#include <drawingml/table/tableproperties.hxx> +#include <oox/drawingml/chart/chartconverter.hxx> +#include <drawingml/chart/chartspacefragment.hxx> +#include <drawingml/chart/chartspacemodel.hxx> +#include <o3tl/safeint.hxx> +#include <o3tl/unit_conversion.hxx> +#include <oox/ppt/pptimport.hxx> +#include <oox/vml/vmldrawing.hxx> +#include <oox/vml/vmlshape.hxx> +#include <oox/vml/vmlshapecontainer.hxx> +#include <oox/core/xmlfilterbase.hxx> +#include <oox/helper/graphichelper.hxx> +#include <oox/helper/propertyset.hxx> +#include <oox/helper/modelobjecthelper.hxx> +#include <oox/mathml/imexport.hxx> +#include <oox/mathml/importutils.hxx> +#include <oox/token/properties.hxx> +#include "diagram/datamodel.hxx" +#include "diagram/diagramhelper.hxx" + +#include <comphelper/classids.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/gen.hxx> +#include <tools/globname.hxx> +#include <tools/mapunit.hxx> +#include <editeng/unoprnms.hxx> +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/awt/XBitmap.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/xml/dom/XDocument.hpp> +#include <com/sun/star/xml/sax/XFastSAXSerializable.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/HomogenMatrix3.hpp> +#include <com/sun/star/drawing/TextVerticalAdjust.hpp> +#include <com/sun/star/drawing/GraphicExportFilter.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp> +#include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeTextPathMode.hpp> +#include <com/sun/star/drawing/ConnectorType.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/table/BorderLine2.hpp> +#include <com/sun/star/table/ShadowFormat.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/style/ParagraphAdjust.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> + +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <com/sun/star/document/XActionLockable.hpp> +#include <com/sun/star/chart2/data/XDataReceiver.hpp> +#include <com/sun/star/text/GraphicCrop.hpp> +#include <svx/svdobj.hxx> +#include <svx/svdotable.hxx> +#include <svx/svdtrans.hxx> +#include <tools/stream.hxx> +#include <unotools/streamwrap.hxx> +#include <unotools/fltrcfg.hxx> +#include <unotools/mediadescriptor.hxx> +#include <vcl/graph.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/svapp.hxx> +#include <vcl/wmfexternal.hxx> +#include <sal/log.hxx> +#include <svx/sdtaitm.hxx> +#include <oox/drawingml/diagram/diagram.hxx> +#include <docmodel/theme/Theme.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <i18nlangtag/mslangid.hxx> + +using namespace ::oox::core; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::style; + +namespace oox::drawingml { + +Shape::Shape( const char* pServiceName, bool bDefaultHeight ) +: mpLinePropertiesPtr( std::make_shared<LineProperties>() ) +, mpShapeRefLinePropPtr( std::make_shared<LineProperties>() ) +, mpFillPropertiesPtr( std::make_shared<FillProperties>() ) +, mpShapeRefFillPropPtr( std::make_shared<FillProperties>() ) +, mpGraphicPropertiesPtr( std::make_shared<GraphicProperties>() ) +, mpCustomShapePropertiesPtr( std::make_shared<CustomShapeProperties>() ) +, mp3DPropertiesPtr( std::make_shared<Shape3DProperties>() ) +, mpEffectPropertiesPtr( std::make_shared<EffectProperties>() ) +, mpShapeRefEffectPropPtr( std::make_shared<EffectProperties>() ) +, mpMasterTextListStyle( std::make_shared<TextListStyle>() ) +, mnSubType( 0 ) +, meFrameType( FRAMETYPE_GENERIC ) +, mnRotation( 0 ) +, mnDiagramRotation( 0 ) +, mbFlipH( false ) +, mbFlipV( false ) +, mbHidden( false ) +, mbHiddenMasterShape( false ) +, mbLocked( false ) +, mbWPGChild(false) +, mbLockedCanvas( false ) +, mbWordprocessingCanvas(false) +, mbWps( false ) +, mbTextBox( false ) +, mbHasLinkedTxbx( false ) +, maDiagramDoms( 0 ) +, mpDiagramHelper( nullptr ) +{ + if ( pServiceName ) + msServiceName = OUString::createFromAscii( pServiceName ); + setDefaults(bDefaultHeight); +} + +Shape::Shape( const ShapePtr& pSourceShape ) +: mpTextBody(pSourceShape->mpTextBody) +, mpLinePropertiesPtr( pSourceShape->mpLinePropertiesPtr ) +, mpShapeRefLinePropPtr( pSourceShape->mpShapeRefLinePropPtr ) +, mpFillPropertiesPtr( pSourceShape->mpFillPropertiesPtr ) +, mpShapeRefFillPropPtr( pSourceShape->mpShapeRefFillPropPtr ) +, mpGraphicPropertiesPtr( pSourceShape->mpGraphicPropertiesPtr ) +, mpCustomShapePropertiesPtr( pSourceShape->mpCustomShapePropertiesPtr ) +, mpTablePropertiesPtr( pSourceShape->mpTablePropertiesPtr ) +, mp3DPropertiesPtr( pSourceShape->mp3DPropertiesPtr ) +, mpEffectPropertiesPtr (pSourceShape->mpEffectPropertiesPtr) +, mpShapeRefEffectPropPtr(pSourceShape->mpShapeRefEffectPropPtr) +, maShapeProperties( pSourceShape->maShapeProperties ) +, mpMasterTextListStyle( pSourceShape->mpMasterTextListStyle ) +, msServiceName( pSourceShape->msServiceName ) +, msName( pSourceShape->msName ) +, msInternalName( pSourceShape->msInternalName ) +, msId( pSourceShape->msId ) +, mnSubType( pSourceShape->mnSubType ) +, moSubTypeIndex( pSourceShape->moSubTypeIndex ) +, maShapeStyleRefs( pSourceShape->maShapeStyleRefs ) +, maSize( pSourceShape->maSize ) +, maPosition( pSourceShape->maPosition ) +, meFrameType( pSourceShape->meFrameType ) +, mnRotation( pSourceShape->mnRotation ) +, mnDiagramRotation( pSourceShape->mnDiagramRotation ) +, mbFlipH( pSourceShape->mbFlipH ) +, mbFlipV( pSourceShape->mbFlipV ) +, mbHidden( pSourceShape->mbHidden ) +, mbHiddenMasterShape( pSourceShape->mbHiddenMasterShape ) +, mbLocked( pSourceShape->mbLocked ) +, mbWPGChild( pSourceShape->mbWPGChild ) +, mbLockedCanvas( pSourceShape->mbLockedCanvas ) +, mbWordprocessingCanvas(pSourceShape->mbWordprocessingCanvas) +, mbWps( pSourceShape->mbWps ) +, mbTextBox( pSourceShape->mbTextBox ) +, mbHasLinkedTxbx(false) +, maDiagramDoms( pSourceShape->maDiagramDoms ) +, mnZOrder(pSourceShape->mnZOrder) +, mnZOrderOff(pSourceShape->mnZOrderOff) +, mnDataNodeType(pSourceShape->mnDataNodeType) +, mfAspectRatio(pSourceShape->mfAspectRatio) +, mpDiagramHelper( nullptr ) +, msDiagramDataModelID(pSourceShape->msDiagramDataModelID) +{} + +Shape::~Shape() +{ + // DiagramHelper should not be set here anymore, see + // propagateDiagramHelper below (maybe assert..?) + delete mpDiagramHelper; +} + +void Shape::prepareDiagramHelper( + const std::shared_ptr< Diagram >& rDiagramPtr, + const std::shared_ptr<::oox::drawingml::Theme>& rTheme) +{ + // Prepare Diagram data collecting for this Shape + if( nullptr == mpDiagramHelper && FRAMETYPE_DIAGRAM == meFrameType ) + { + mpDiagramHelper = new AdvancedDiagramHelper( + rDiagramPtr, + rTheme, + getSize()); + } +} + +void Shape::propagateDiagramHelper() +{ + // Propagate collected Diagram data to data holder + if (FRAMETYPE_DIAGRAM == meFrameType && nullptr != mpDiagramHelper) + { + SdrObjGroup* pAnchorObj = dynamic_cast<SdrObjGroup*>(SdrObject::getSdrObjectFromXShape(mxShape)); + + if(pAnchorObj) + { + mpDiagramHelper->doAnchor(*pAnchorObj, *this); + mpDiagramHelper = nullptr; + } + } + + // If propagation failed, delete/cleanup here. Since the DiagramHelper + // holds a Diagram and that this Shape it is necessary - the destructor + // will not be called and will be too late + if (nullptr != mpDiagramHelper) + { + delete mpDiagramHelper; + mpDiagramHelper = nullptr; + } +} + +void Shape::migrateDiagramHelperToNewShape(const ShapePtr& pTarget) +{ + if(!mpDiagramHelper) + { + return; + } + + if(!pTarget) + { + // no migrate target, but cleanup helper + delete mpDiagramHelper; + mpDiagramHelper = nullptr; + return; + } + + if(pTarget->mpDiagramHelper) + { + // this should no happen, but if there is already a helper, clean it up + delete pTarget->mpDiagramHelper; + pTarget->mpDiagramHelper = nullptr; + } + + // exchange and reset to nullptr + pTarget->mpDiagramHelper = mpDiagramHelper; + mpDiagramHelper = nullptr; +} + +table::TablePropertiesPtr const & Shape::getTableProperties() +{ + if ( !mpTablePropertiesPtr ) + mpTablePropertiesPtr = std::make_shared<table::TableProperties>(); + return mpTablePropertiesPtr; +} + +void Shape::setDefaults(bool bHeight) +{ + maDefaultShapeProperties.setProperty(PROP_TextAutoGrowHeight, false); + maDefaultShapeProperties.setProperty(PROP_TextWordWrap, true); + maDefaultShapeProperties.setProperty(PROP_TextLeftDistance, static_cast< sal_Int32 >( 250 )); + maDefaultShapeProperties.setProperty(PROP_TextUpperDistance, static_cast< sal_Int32 >( 125 )); + maDefaultShapeProperties.setProperty(PROP_TextRightDistance, static_cast< sal_Int32 >( 250 )); + maDefaultShapeProperties.setProperty(PROP_TextLowerDistance, static_cast< sal_Int32 >( 125 )); + if (bHeight) + maDefaultShapeProperties.setProperty(PROP_CharHeight, static_cast< float >( 18.0 )); + maDefaultShapeProperties.setProperty(PROP_TextVerticalAdjust, TextVerticalAdjust_TOP); + maDefaultShapeProperties.setProperty(PROP_ParaAdjust, + static_cast<sal_Int16>(ParagraphAdjust_LEFT)); +} + +::oox::vml::OleObjectInfo& Shape::setOleObjectType() +{ + OSL_ENSURE( meFrameType == FRAMETYPE_GENERIC, "Shape::setOleObjectType - multiple frame types" ); + meFrameType = FRAMETYPE_OLEOBJECT; + mxOleObjectInfo = std::make_shared<::oox::vml::OleObjectInfo>( true ); + return *mxOleObjectInfo; +} + +ChartShapeInfo& Shape::setChartType( bool bEmbedShapes ) +{ + OSL_ENSURE( meFrameType == FRAMETYPE_GENERIC, "Shape::setChartType - multiple frame types" ); + meFrameType = FRAMETYPE_CHART; + if (mbWps) + msServiceName = "com.sun.star.drawing.temporaryForXMLImportOLE2Shape"; + else + msServiceName = "com.sun.star.drawing.OLE2Shape"; + mxChartShapeInfo = std::make_shared<ChartShapeInfo>( bEmbedShapes ); + return *mxChartShapeInfo; +} + +void Shape::setDiagramType() +{ + OSL_ENSURE( meFrameType == FRAMETYPE_GENERIC, "Shape::setDiagramType - multiple frame types" ); + meFrameType = FRAMETYPE_DIAGRAM; + msServiceName = "com.sun.star.drawing.GroupShape"; + mnSubType = 0; +} + +void Shape::setTableType() +{ + OSL_ENSURE( meFrameType == FRAMETYPE_GENERIC, "Shape::setTableType - multiple frame types" ); + meFrameType = FRAMETYPE_TABLE; + msServiceName = "com.sun.star.drawing.TableShape"; + mnSubType = 0; +} + +void Shape::setServiceName( const char* pServiceName ) +{ + if ( pServiceName ) + msServiceName = OUString::createFromAscii( pServiceName ); +} + +const ShapeStyleRef* Shape::getShapeStyleRef( sal_Int32 nRefType ) const +{ + ShapeStyleRefMap::const_iterator aIt = maShapeStyleRefs.find( nRefType ); + return (aIt == maShapeStyleRefs.end()) ? nullptr : &aIt->second; +} + +void Shape::addShape( + ::oox::core::XmlFilterBase& rFilterBase, + const Theme* pTheme, + const Reference< XShapes >& rxShapes, + const basegfx::B2DHomMatrix& aTransformation, + const FillProperties& rShapeOrParentShapeFillProps, + ShapeIdMap* pShapeMap, + oox::drawingml::ShapePtr pParentGroupShape) +{ + SAL_INFO("oox.drawingml", "Shape::addShape: id='" << msId << "'"); + + try + { + OUString sServiceName( msServiceName ); + if( !sServiceName.isEmpty() ) + { + basegfx::B2DHomMatrix aMatrix( aTransformation ); + Reference< XShape > xShape( createAndInsert( rFilterBase, sServiceName, pTheme, rxShapes, false, false, aMatrix, rShapeOrParentShapeFillProps, pParentGroupShape) ); + + if( pShapeMap && !msId.isEmpty() ) + { + (*pShapeMap)[ msId ] = shared_from_this(); + } + + // if this is a group shape, we have to add also each child shape + Reference< XShapes > xShapes( xShape, UNO_QUERY ); + if ( xShapes.is() ) + addChildren( rFilterBase, *this, pTheme, xShapes, pShapeMap, aMatrix ); + + if (mbWordprocessingCanvas && !mbWPGChild) + { + // This is a drawing canvas. In case the canvas has no fill and no stroke, Word does + // not render shadow or glow, even if it is set for the canvas. Thus we disable shadow + // and glow in this case for the ersatz background shape of the drawing canvas. + try + { + oox::drawingml::ShapePtr pBgShape = getChildren().front(); + const Reference<css::drawing::XShape>& xBgShape = pBgShape->getXShape(); + Reference<XPropertySet> xBgProps(xBgShape, uno::UNO_QUERY); + drawing::FillStyle eFillStyle = drawing::FillStyle_NONE; + xBgProps->getPropertyValue("FillStyle") >>= eFillStyle; + drawing::LineStyle eLineStyle = drawing::LineStyle_NONE; + xBgProps->getPropertyValue("LineStyle") >>= eLineStyle; + if (eFillStyle == drawing::FillStyle_NONE + && eLineStyle == drawing::LineStyle_NONE) + { + xBgProps->setPropertyValue(UNO_NAME_SHADOW, uno::Any(false)); + xBgProps->setPropertyValue(u"GlowEffectRadius"_ustr, uno::Any(sal_Int32(0))); + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("oox.drawingml", "Shape::addShape mbWordprocessingCanvas"); + } + } + + if (isWPGChild() && xShape) + { + // This is a wps shape and it is the child of the WPG, now copy the + // the text body properties to the xshape. + Reference<XPropertySet> xChildWPSProperties(xShape, uno::UNO_QUERY); + + if (getTextBody() && xChildWPSProperties) + { + xChildWPSProperties->setPropertyValue( + UNO_NAME_TEXT_VERTADJUST, + uno::Any(getTextBody()->getTextProperties().meVA)); + + xChildWPSProperties->setPropertyValue( + UNO_NAME_TEXT_LEFTDIST, + uno::Any(getTextBody()->getTextProperties().moInsets[0].has_value() + ? *getTextBody()->getTextProperties().moInsets[0] + : 0)); + xChildWPSProperties->setPropertyValue( + UNO_NAME_TEXT_UPPERDIST, + uno::Any(getTextBody()->getTextProperties().moInsets[1].has_value() + ? *getTextBody()->getTextProperties().moInsets[1] + : 0)); + xChildWPSProperties->setPropertyValue( + UNO_NAME_TEXT_RIGHTDIST, + uno::Any(getTextBody()->getTextProperties().moInsets[2].has_value() + ? *getTextBody()->getTextProperties().moInsets[2] + : 0)); + xChildWPSProperties->setPropertyValue( + UNO_NAME_TEXT_LOWERDIST, + uno::Any(getTextBody()->getTextProperties().moInsets[3].has_value() + ? *getTextBody()->getTextProperties().moInsets[3] + : 0)); + } + + // tdf#145147 Set the Hyperlink property to the child wps shape. + if (getShapeProperties().hasProperty(PROP_URL)) try + { + uno::Any aAny = getShapeProperties().getProperty(PROP_URL); + OUString sUrl = aAny.get<OUString>(); + if (!sUrl.isEmpty()) + xChildWPSProperties->setPropertyValue(UNO_NAME_HYPERLINK, aAny); + } + catch (const Exception&) + { + } + } + + if( meFrameType == FRAMETYPE_DIAGRAM ) + { + keepDiagramCompatibilityInfo(); + + // set DiagramHelper at SdrObjGroup + propagateDiagramHelper(); + + // Check if this is the PPTX import, so far converting SmartArt to a non-editable + // metafile is only implemented for DOCX. + bool bPowerPoint = dynamic_cast<oox::ppt::PowerPointImport*>(&rFilterBase) != nullptr; + + if (!SvtFilterOptions::Get().IsSmartArt2Shape() && !bPowerPoint) + convertSmartArtToMetafile( rFilterBase ); + } + + NamedShapePairs* pNamedShapePairs = rFilterBase.getDiagramFontHeights(); + if (xShape.is() && pNamedShapePairs) + { + auto itPairs = pNamedShapePairs->find(getInternalName()); + if (itPairs != pNamedShapePairs->end()) + { + auto it = itPairs->second.find(shared_from_this()); + if (it != itPairs->second.end()) + { + // Our drawingml::Shape is in the list of an internal name, remember the now + // inserted XShape. + it->second = xShape; + } + } + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "oox.drawingml", "Shape::addShape" ); + } +} + +void Shape::setLockedCanvas(bool bLockedCanvas) +{ + mbLockedCanvas = bLockedCanvas; +} + +void Shape::setWordprocessingCanvas(bool bWordprocessingCanvas) +{ + mbWordprocessingCanvas = bWordprocessingCanvas; +} + +void Shape::setWPGChild(bool bWPG) +{ + mbWPGChild = bWPG; +} + +void Shape::setWps(bool bWps) +{ + mbWps = bWps; +} + +void Shape::setTextBox(bool bTextBox) +{ + mbTextBox = bTextBox; +} + +void Shape::applyShapeReference( const Shape& rReferencedShape, bool bUseText ) +{ + SAL_INFO("oox.drawingml", "Shape::applyShapeReference: apply '" << rReferencedShape.msId << "' to '" << msId << "'"); + + if ( rReferencedShape.mpTextBody && bUseText ) + mpTextBody = std::make_shared<TextBody>( *rReferencedShape.mpTextBody ); + else + mpTextBody.reset(); + maShapeProperties = rReferencedShape.maShapeProperties; + mpShapeRefLinePropPtr = std::make_shared<LineProperties>( rReferencedShape.getActualLineProperties(nullptr) ); + mpShapeRefFillPropPtr = std::make_shared<FillProperties>( rReferencedShape.getActualFillProperties(nullptr, nullptr) ); + mpCustomShapePropertiesPtr = std::make_shared<CustomShapeProperties>( *rReferencedShape.mpCustomShapePropertiesPtr ); + mpTablePropertiesPtr = rReferencedShape.mpTablePropertiesPtr ? std::make_shared<table::TableProperties>( *rReferencedShape.mpTablePropertiesPtr ) : nullptr; + mpShapeRefEffectPropPtr = std::make_shared<EffectProperties>( rReferencedShape.getActualEffectProperties(nullptr) ); + mpMasterTextListStyle = std::make_shared<TextListStyle>( *rReferencedShape.mpMasterTextListStyle ); + maSize = rReferencedShape.maSize; + maPosition = rReferencedShape.maPosition; + mnRotation = rReferencedShape.mnRotation; + mbFlipH = rReferencedShape.mbFlipH; + mbFlipV = rReferencedShape.mbFlipV; + mbHidden = rReferencedShape.mbHidden; + mbLocked = rReferencedShape.mbLocked; +} + +namespace { + +struct ActionLockGuard +{ + explicit ActionLockGuard(Reference<drawing::XShape> const& xShape) + : m_xLockable(xShape, UNO_QUERY) + { + if (m_xLockable.is()) { + m_xLockable->addActionLock(); + } + } + ~ActionLockGuard() + { + if (m_xLockable.is()) { + m_xLockable->removeActionLock(); + } + } +private: + Reference<document::XActionLockable> m_xLockable; +}; + +} + +// for group shapes, the following method is also adding each child +void Shape::addChildren( + XmlFilterBase& rFilterBase, + Shape& rMaster, + const Theme* pTheme, + const Reference< XShapes >& rxShapes, + ShapeIdMap* pShapeMap, + const basegfx::B2DHomMatrix& aTransformation ) +{ + for (auto const& child : rMaster.maChildren) + { + child->setMasterTextListStyle( mpMasterTextListStyle ); + child->addShape( rFilterBase, pTheme, rxShapes, aTransformation, getFillProperties(), pShapeMap, rMaster.shared_from_this()); + } +} + +static SdrTextHorzAdjust lcl_convertAdjust( ParagraphAdjust eAdjust ) +{ + if (eAdjust == ParagraphAdjust_LEFT) + return SDRTEXTHORZADJUST_LEFT; + else if (eAdjust == ParagraphAdjust_RIGHT) + return SDRTEXTHORZADJUST_RIGHT; + else if (eAdjust == ParagraphAdjust_CENTER) + return SDRTEXTHORZADJUST_CENTER; + return SDRTEXTHORZADJUST_LEFT; +} + +// LO does not interpret properties in styles belonging to the text content of a FontWork shape, +// but only those in the shape style. This method copies properties from the text content styles to +// the shape style. +static void lcl_copyCharPropsToShape(const uno::Reference<drawing::XShape>& xShape, + const TextBodyPtr& pTextBody, + const ::oox::core::XmlFilterBase& rFilter) +{ + if (!xShape.is() || !pTextBody) + return; + + Reference<XPropertySet> xSet(xShape, UNO_QUERY); + if (!xSet.is()) + return; + + // Content stretches or scales to given width and height, thus disable autogrow. + xSet->setPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT, uno::Any(false)); + xSet->setPropertyValue(UNO_NAME_TEXT_AUTOGROWWIDTH, uno::Any(false)); + + // LibreOffice is not able (as of Nov 2022) to use different styles for the paragraphs or + // characters in FontWork, since that was not allowed in old binary WordArt. We use the + // properties of the first non empty paragraph for now. + const TextParagraphVector& rParagraphs = pTextBody->getParagraphs(); + auto aParaIt = std::find_if_not(rParagraphs.cbegin(), rParagraphs.cend(), + [](const std::shared_ptr<TextParagraph> pParagraph) { + return pParagraph->getRuns().empty(); + }); + if (aParaIt != rParagraphs.cend()) + { + std::shared_ptr<TextParagraph> pParagraph = *aParaIt; + const TextRunVector& rRuns = pParagraph->getRuns(); + auto aRunIt = std::find_if_not( + rRuns.cbegin(), rRuns.cend(), + [](const std::shared_ptr<TextRun> pRun) { return pRun->getText().isEmpty(); }); + if (aRunIt != rRuns.cend()) + { + std::shared_ptr<TextRun> pRun = *aRunIt; + TextCharacterProperties& rCharProps = pRun->getTextCharacterProperties(); + + // set language + if (rCharProps.moLang.has_value() && !rCharProps.moLang.value().isEmpty()) + { + LanguageTag aTag(rCharProps.moLang.value()); + css::lang::Locale aLocale(aTag.getLocale(false)); + switch (MsLangId::getScriptType(aTag.getLanguageType())) + { + case css::i18n::ScriptType::LATIN: + xSet->setPropertyValue(u"CharLocale"_ustr, uno::Any(aLocale)); + break; + case css::i18n::ScriptType::ASIAN: + xSet->setPropertyValue(u"CharLocaleAsian"_ustr, uno::Any(aLocale)); + break; + case css::i18n::ScriptType::COMPLEX: + xSet->setPropertyValue(u"CharLocaleComplex"_ustr, uno::Any(aLocale)); + break; + default:; + } + } + + // Font Weight, Posture, Height + if (rCharProps.moBold.has_value() && rCharProps.moBold.value()) + { + xSet->setPropertyValue(UNO_NAME_CHAR_WEIGHT, uno::Any(css::awt::FontWeight::BOLD)); + } + if (rCharProps.moItalic.has_value() && rCharProps.moItalic.value()) + { + xSet->setPropertyValue(UNO_NAME_CHAR_POSTURE, + uno::Any(css::awt::FontSlant::FontSlant_ITALIC)); + } + if (rCharProps.moHeight.has_value()) + { + sal_Int32 nHeight = rCharProps.moHeight.value() / 100; + xSet->setPropertyValue(UNO_NAME_CHAR_HEIGHT, uno::Any(nHeight)); + } + + // Put theme fonts into shape properties + OUString sFontName; + sal_Int16 nFontPitch = 0; + sal_Int16 nFontFamily = 0; + bool bRet(false); + if (const Theme* pTheme = rFilter.getCurrentTheme()) + { + // minor Latin + if (const TextFont* pFont = pTheme->resolveFont(u"+mn-lt")) + { + bRet = pFont->getFontData(sFontName, nFontPitch, nFontFamily, nullptr, rFilter); + if (bRet) + { + xSet->setPropertyValue(u"CharFontName"_ustr, uno::Any(sFontName)); + xSet->setPropertyValue(u"CharFontPitch"_ustr, uno::Any(nFontPitch)); + xSet->setPropertyValue(u"CharFontFamily"_ustr, uno::Any(nFontFamily)); + } + } + // minor Asian + if (const TextFont* pFont = pTheme->resolveFont(u"+mn-ea")) + { + bRet = pFont->getFontData(sFontName, nFontPitch, nFontFamily, nullptr, rFilter); + if (bRet) + { + xSet->setPropertyValue(u"CharFontNameAsian"_ustr, uno::Any(sFontName)); + xSet->setPropertyValue(u"CharFontPitchAsian"_ustr, uno::Any(nFontPitch)); + xSet->setPropertyValue(u"CharFontFamilyAsian"_ustr, uno::Any(nFontFamily)); + } + } + // minor Complex + if (const TextFont* pFont = pTheme->resolveFont(u"+mn-cs")) + { + bRet = pFont->getFontData(sFontName, nFontPitch, nFontFamily, nullptr, rFilter); + if (bRet) + { + xSet->setPropertyValue(u"CharFontNameComplex"_ustr, uno::Any(sFontName)); + xSet->setPropertyValue(u"CharFontPitchComplex"_ustr, uno::Any(nFontPitch)); + xSet->setPropertyValue(u"CharFontFamilyComplex"_ustr, uno::Any(nFontFamily)); + } + } + } + + // Replace theme fonts with formatting at run if any. ToDo: Inspect paragraph too? + // Latin + bRet = rCharProps.maLatinFont.getFontData(sFontName, nFontPitch, nFontFamily, nullptr, rFilter); + if (!bRet) + // In case there is no direct font, try to look it up as a theme reference. + bRet = rCharProps.maLatinThemeFont.getFontData(sFontName, nFontPitch, nFontFamily, nullptr, + rFilter); + + if (bRet) + { + xSet->setPropertyValue(u"CharFontName"_ustr, uno::Any(sFontName)); + xSet->setPropertyValue(u"CharFontPitch"_ustr, uno::Any(nFontPitch)); + xSet->setPropertyValue(u"CharFontFamily"_ustr, uno::Any(nFontFamily)); + } + // Asian + bRet = rCharProps.maAsianFont.getFontData(sFontName, nFontPitch, nFontFamily, nullptr, rFilter); + if (!bRet) + // In case there is no direct font, try to look it up as a theme reference. + bRet = rCharProps.maAsianThemeFont.getFontData(sFontName, nFontPitch, nFontFamily, nullptr, + rFilter); + if (bRet) + { + xSet->setPropertyValue(u"CharFontNameAsian"_ustr, uno::Any(sFontName)); + xSet->setPropertyValue(u"CharFontPitchAsian"_ustr, uno::Any(nFontPitch)); + xSet->setPropertyValue(u"CharFontFamilyAsian"_ustr, uno::Any(nFontFamily)); + } + // Complex + bRet + = rCharProps.maComplexFont.getFontData(sFontName, nFontPitch, nFontFamily, nullptr, rFilter); + if (!bRet) + // In case there is no direct font, try to look it up as a theme reference. + bRet = rCharProps.maComplexThemeFont.getFontData(sFontName, nFontPitch, nFontFamily, nullptr, + rFilter); + if (bRet) + { + xSet->setPropertyValue(u"CharFontNameComplex"_ustr, uno::Any(sFontName)); + xSet->setPropertyValue(u"CharFontPitchComplex"_ustr, uno::Any(nFontPitch)); + xSet->setPropertyValue(u"CharFontFamilyComplex"_ustr, uno::Any(nFontFamily)); + } + + // LO uses shape properties, MS Office character properties. Copy them from char to shape. + // Outline + if (rCharProps.moTextOutlineProperties.has_value()) + { + oox::drawingml::ShapePropertyMap aStrokeShapeProps(rFilter.getModelObjectHelper()); + rCharProps.moTextOutlineProperties.value().pushToPropMap( + aStrokeShapeProps, rFilter.getGraphicHelper()); + for (const auto& rProp : aStrokeShapeProps.makePropertyValueSequence()) + { + xSet->setPropertyValue(rProp.Name, rProp.Value); + } + } + else + { + xSet->setPropertyValue(UNO_NAME_LINESTYLE, uno::Any(drawing::LineStyle_NONE)); + } + + // Fill + // ToDo: Replace flip and rotate constants in parameters with actual values. + // tdf#155327 If color is not explicitly set, MS Office uses scheme color 'tx1'. + oox::drawingml::ShapePropertyMap aFillShapeProps(rFilter.getModelObjectHelper()); + if (!rCharProps.maFillProperties.moFillType.has_value()) + rCharProps.maFillProperties.moFillType = XML_solidFill; + if (!rCharProps.maFillProperties.maFillColor.isUsed()) + rCharProps.maFillProperties.maFillColor.setSchemeClr(XML_tx1); + rCharProps.maFillProperties.pushToPropMap(aFillShapeProps, rFilter.getGraphicHelper(), + /*nShapeRotation*/ 0, + /*nPhClr*/ API_RGB_TRANSPARENT, + /*aShapeSize*/ css::awt::Size(0, 0), + /*nPhClrTheme*/ -1, + /*bFlipH*/ false, /*bFlipV*/ false, + /*bIsCustomShape*/ true); + for (const auto& rProp : aFillShapeProps.makePropertyValueSequence()) + { + xSet->setPropertyValue(rProp.Name, rProp.Value); + } + + // ToDo: Import WordArt glow and simple shadow effects. They are available in LO. + } + + // LO does not evaluate paragraph alignment in text path mode. Use text area anchor instead. + { + ParagraphAdjust eAdjust = ParagraphAdjust_LEFT; + if (pParagraph->getProperties().getParaAdjust()) + eAdjust = *pParagraph->getProperties().getParaAdjust(); + xSet->setPropertyValue("ParaAdjust", uno::Any(eAdjust)); + SdrObject* pShape = SdrObject::getSdrObjectFromXShape(xShape); + assert(pShape); + SdrTextHorzAdjust eHorzAdjust = lcl_convertAdjust(eAdjust); + pShape->SetMergedItem(SdrTextHorzAdjustItem(eHorzAdjust)); + } + } + + // Vertical adjustment is only meaningful for OOXML WordArt shapes of 'Follow Path' kinds. We set + // it so, that text position is approximately same as in MS Office. + const OUString sMSPresetType = pTextBody->getTextProperties().msPrst; + const OUString sFontworkType = PresetGeometryTypeNames::GetFontworkType(sMSPresetType); + SdrObject* pShape = SdrObject::getSdrObjectFromXShape(xShape); + assert(pShape); + if (sFontworkType == "fontwork-arch-up-curve" || sFontworkType == "fontwork-circle-curve") + pShape->SetMergedItem(SdrTextVertAdjustItem(SdrTextVertAdjust::SDRTEXTVERTADJUST_BOTTOM)); + else if (sFontworkType == "fontwork-arch-down-curve") + pShape->SetMergedItem(SdrTextVertAdjustItem(SdrTextVertAdjust::SDRTEXTVERTADJUST_TOP)); + else + pShape->SetMergedItem(SdrTextVertAdjustItem(SdrTextVertAdjust::SDRTEXTVERTADJUST_CENTER)); +} + +// Some helper methods for createAndInsert +namespace +{ +// mirrors aTransformation at its center axis +// only valid if neither rotation or shear included +void lcl_mirrorAtCenter(basegfx::B2DHomMatrix& aTransformation, bool bFlipH, bool bFlipV) +{ + if (!bFlipH && !bFlipV) + return; + basegfx::B2DPoint aCenter(0.5, 0.5); + aCenter *= aTransformation; + aTransformation.translate(-aCenter); + aTransformation.scale(bFlipH ? -1.0 : 1.0, bFlipV ? -1.0 : 1.0); + aTransformation.translate(aCenter); + return; +} + +// only valid if neither rotation or shear included +void lcl_doSpecialMSOWidthHeightToggle(basegfx::B2DHomMatrix& aTransformation) +{ + // The values are directly set at the matrix without any matrix multiplication. + // That way it is valid for lines too. Those have zero width or height. + const double fSx(aTransformation.get(0, 0)); + const double fSy(aTransformation.get(1, 1)); + const double fTx(aTransformation.get(0, 2)); + const double fTy(aTransformation.get(1, 2)); + aTransformation.set(0, 0, fSy); + aTransformation.set(1, 1, fSx); + aTransformation.set(0, 2, fTx + 0.5 * (fSx - fSy)); + aTransformation.set(1, 2, fTy + 0.5 * (fSy - fSx)); + return; +} + +void lcl_RotateAtCenter(basegfx::B2DHomMatrix& aTransformation,sal_Int32 nMSORotationAngle) +{ + if (nMSORotationAngle == 0) + return; + double fRad = basegfx::deg2rad<60000>(nMSORotationAngle); + basegfx::B2DPoint aCenter(0.5, 0.5); + aCenter *= aTransformation; + aTransformation.translate(-aCenter); + aTransformation.rotate(fRad); + aTransformation.translate(aCenter); + return; +} +} + +Reference< XShape > const & Shape::createAndInsert( + ::oox::core::XmlFilterBase& rFilterBase, + const OUString& rServiceName, + const Theme* pTheme, + const css::uno::Reference< css::drawing::XShapes >& rxShapes, + bool bClearText, + bool bDoNotInsertEmptyTextBody, + basegfx::B2DHomMatrix& aParentTransformation, + const FillProperties& rShapeOrParentShapeFillProps, + oox::drawingml::ShapePtr pParentGroupShape) +{ + bool bIsEmbMedia = false; + SAL_INFO("oox.drawingml", "Shape::createAndInsert: id='" << msId << "' service='" << rServiceName << "'"); + + formulaimport::XmlStreamBuilder * pMathXml(nullptr); + if (mpTextBody) + { + for (auto const& it : mpTextBody->getParagraphs()) + { + if (it->HasMathXml()) + { + if (!mpTextBody->isEmpty() || pMathXml != nullptr) + { + SAL_WARN("oox.drawingml", "losing a Math object..."); + } + else + { + pMathXml = &it->GetMathXml(); + } + } + } + } + + // tdf#90403 PowerPoint ignores a:ext cx and cy values of p:xfrm, and uses real table width and height + if ( mpTablePropertiesPtr && rServiceName == "com.sun.star.drawing.TableShape" ) + { + maSize.Width = 0; + for (auto const& elem : mpTablePropertiesPtr->getTableGrid()) + { + maSize.Width = o3tl::saturating_add(maSize.Width, static_cast<sal_Int32>(elem)); + } + maSize.Height = 0; + for (auto const& elem : mpTablePropertiesPtr->getTableRows()) + { + // WARN: If some rows can't fit the content, this is not the final height + maSize.Height = o3tl::saturating_add(maSize.Height, elem.getHeight()); + } + } + + awt::Rectangle aShapeRectHmm( + o3tl::convert(maPosition.X, o3tl::Length::emu, o3tl::Length::mm100), + o3tl::convert(maPosition.Y, o3tl::Length::emu, o3tl::Length::mm100), + o3tl::convert(maSize.Width, o3tl::Length::emu, o3tl::Length::mm100), + o3tl::convert(maSize.Height, o3tl::Length::emu, o3tl::Length::mm100)); + + OUString aServiceName; + if (pMathXml) + { + // convert this shape to OLE + aServiceName = "com.sun.star.drawing.OLE2Shape"; + msServiceName = aServiceName; + meFrameType = FRAMETYPE_GENERIC; // not OLEOBJECT, no stream in package + mnSubType = 0; + } + else if (rServiceName == "com.sun.star.drawing.GraphicObjectShape" && + mpGraphicPropertiesPtr && !mpGraphicPropertiesPtr->m_sMediaPackageURL.isEmpty()) + { + aServiceName = finalizeServiceName( rFilterBase, "com.sun.star.presentation.MediaShape", aShapeRectHmm ); + bIsEmbMedia = true; + } + else + { + aServiceName = finalizeServiceName( rFilterBase, rServiceName, aShapeRectHmm ); + } + // Use custom shape instead of GraphicObjectShape if the image is cropped to + // shape. Except rectangle, which does not require further cropping + bool bIsCroppedGraphic = (aServiceName == "com.sun.star.drawing.GraphicObjectShape" && + !mpCustomShapePropertiesPtr->representsDefaultShape()); + + bool bIsCustomShape = (aServiceName == "com.sun.star.drawing.CustomShape" || bIsCroppedGraphic); + bool bIsConnectorShape = (aServiceName == "com.sun.star.drawing.ConnectorShape"); + if(bIsCroppedGraphic) + { + aServiceName = "com.sun.star.drawing.CustomShape"; + mpGraphicPropertiesPtr->mbIsCustomShape = true; + } + bool bUseRotationTransform = ( !mbWps || + aServiceName == "com.sun.star.drawing.LineShape" || + aServiceName == "com.sun.star.drawing.GroupShape" || + mbFlipH || + mbFlipV ); + + basegfx::B2DHomMatrix aTransformation; // will be cumulative transformation of this object + + // Special for SmartArt import. Rotate diagram's shape around object's center before sizing. + if (bUseRotationTransform && mnDiagramRotation != 0) + { + aTransformation.translate(-0.5, -0.5); + aTransformation.rotate(basegfx::deg2rad<60000>(mnDiagramRotation)); + aTransformation.translate(0.5, 0.5); + } + + // Build object matrix from shape size and position; corresponds to MSO ext and off + // Only LineShape and ConnectorShape may have zero width or height. + if (aServiceName == "com.sun.star.drawing.LineShape" + || aServiceName == "com.sun.star.drawing.ConnectorShape") + aTransformation.scale(maSize.Width, maSize.Height); + else + { + aTransformation.scale(maSize.Width ? maSize.Width : 1.0, + maSize.Height ? maSize.Height : 1.0); + } + + // Evaluate object flip. Other shapes than custom shapes have no attribute for flip but use + // negative scale. Flip in MSO is at object center. + if (!bIsCustomShape && (mbFlipH || mbFlipV)) + lcl_mirrorAtCenter(aTransformation, mbFlipH, mbFlipV); + + // Evaluate parent flip. + // A CustomShape has mirror not as negative scale, but as attributes. + basegfx::B2DVector aParentScale(1.0, 1.0); + basegfx::B2DVector aParentTranslate(0.0, 0.0); + double fParentRotate(0.0); + double fParentShearX(0.0); + if (pParentGroupShape) + { + aParentTransformation.decompose(aParentScale, aParentTranslate, fParentRotate, fParentShearX); + if (bIsCustomShape) + { + lcl_mirrorAtCenter(aTransformation, aParentScale.getX() < 0, aParentScale.getY() < 0); + if(aParentScale.getX() < 0) + mbFlipH = !mbFlipH; + if(aParentScale.getY() < 0) + mbFlipV = !mbFlipV; + } + } + + if (maPosition.X != 0 || maPosition.Y != 0) + { + // if global position is used, add it to transformation + if (mbWps && pParentGroupShape == nullptr) + aTransformation.translate( + o3tl::convert(maPosition.X, o3tl::Length::mm100, o3tl::Length::emu), + o3tl::convert(maPosition.Y, o3tl::Length::mm100, o3tl::Length::emu)); + else + aTransformation.translate(maPosition.X, maPosition.Y); + } + + // Apply further parent transformations. First scale object then rotate. Other way round would + // introduce shearing. + + // The attributes chExt and chOff of the group in oox file contain the values on which the size + // and position of the child is based on. If they differ from the actual size of the group as + // given in its ext and off attributes, the child has to be transformed according the new values. + if (pParentGroupShape) + { + // ToDo: A diagram in a group might need special handling because it cannot flip and only + // resize uniformly. But currently it is imported with zero size, see tdf#139575. That needs + // to be fixed beforehand. + + // Scaling is done from left/top edges of the group. So these need to become coordinate axes. + aTransformation.translate(-pParentGroupShape->maChPosition.X, + -pParentGroupShape->maChPosition.Y); + + // oox allows zero or missing attribute chExt. In that case the scaling factor is 1. + // Transform2DContext::onCreateContext has set maChSize to maSize for groups in oox file in + // such cases. For own made groups (e.g. diagrams) that is missing. + // The factors cumulate on the way through the parent groups, so we do not use maSize of the + // direct parent group but the cumulated value from aParentScale. + double fFactorX = 1.0; + double fFactorY = 1.0; + if (pParentGroupShape->maChSize.Width != 0) + fFactorX = aParentScale.getX() / pParentGroupShape->maChSize.Width; + if (pParentGroupShape->maChSize.Height != 0) + fFactorY = aParentScale.getY() / pParentGroupShape->maChSize.Height; + if (fFactorX != 1 || fFactorY != 1) + { + // It depends on the object rotation angle whether scaling is applied to switched + // width and height. MSO acts strange in that case (as of May 2021). + const sal_Int32 nDeg(mnRotation / 60000); + const bool bNeedsMSOWidthHeightToggle + = (nDeg >= 45 && nDeg < 135) || (nDeg >= 225 && nDeg < 315); + if (bNeedsMSOWidthHeightToggle) + lcl_doSpecialMSOWidthHeightToggle(aTransformation); + + aTransformation.scale(fFactorX, fFactorY); + + if (bNeedsMSOWidthHeightToggle) + { + lcl_doSpecialMSOWidthHeightToggle(aTransformation); + // In case of flip the special case needs an additional 180deg rotation. + if ((aParentScale.getX() < 0) != (aParentScale.getY() < 0)) + lcl_RotateAtCenter(aTransformation, 10800000); + } + } + } + + // Apply object rotation at current object center + // The flip contained in aParentScale will affect orientation of object rotation angle. + sal_Int16 nOrientation = ((aParentScale.getX() < 0) != (aParentScale.getY() < 0)) ? -1 : 1; + // ToDo: Not sure about the restrictions given by bUseRotationTransform. + // Since LibreOffice doesn't have 3D camera options for 2D shapes, rotate the shape opposite of + // the camera Z axis rotation, in order to produce the same visual result from MSO + const sal_Int32 nCameraRotation = get3DProperties().maCameraRotation.mnRevolution.value_or(0); + if (bUseRotationTransform && (mnRotation != 0 || nCameraRotation != 0)) + lcl_RotateAtCenter(aTransformation, nOrientation * (mnRotation - nCameraRotation)); + + if (fParentRotate != 0.0) + aTransformation.rotate(fParentRotate); + if (!aParentTranslate.equalZero()) + aTransformation.translate(aParentTranslate); + + aParentTransformation = aTransformation; + + constexpr double fEmuToMm100 = o3tl::convert(1.0, o3tl::Length::emu, o3tl::Length::mm100); + aTransformation.scale(fEmuToMm100, fEmuToMm100); + + // OOXML flips shapes before rotating them, so the rotation needs to be inverted + if( bIsCustomShape && mbFlipH != mbFlipV ) + { + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + aTransformation.decompose(aScale, aTranslate, fRotate, fShearX); + + if(fRotate != 0) + { + basegfx::B2DPoint aCenter(0.5, 0.5); + aCenter *= aTransformation; + aTransformation.translate( -aCenter.getX(), -aCenter.getY() ); + aTransformation.rotate( fRotate * -2.0 ); + aTransformation.translate( aCenter.getX(), aCenter.getY() ); + } + } + + // special for lineshape + if ( aServiceName == "com.sun.star.drawing.LineShape" ) + { + ::basegfx::B2DPolygon aPoly; + aPoly.insert( 0, ::basegfx::B2DPoint( 0, 0 ) ); + aPoly.insert( 1, ::basegfx::B2DPoint( maSize.Width ? 1 : 0, maSize.Height ? 1 : 0 ) ); + aPoly.transform( aTransformation ); + + // now creating the corresponding PolyPolygon + sal_Int32 i, nNumPoints = aPoly.count(); + uno::Sequence< awt::Point > aPointSequence( nNumPoints ); + awt::Point* pPoints = aPointSequence.getArray(); + for( i = 0; i < nNumPoints; ++i ) + { + basegfx::B2DPoint aPoint( aPoly.getB2DPoint( i ) ); + + // Guard against zero width or height. + if (i) + { + const basegfx::B2DPoint& rPreviousPoint = aPoly.getB2DPoint(i - 1); + if (aPoint.getX() - rPreviousPoint.getX() == 0) + aPoint.setX(aPoint.getX() + 1); + if (aPoint.getY() - rPreviousPoint.getY() == 0) + aPoint.setY(aPoint.getY() + 1); + } + + pPoints[i] = awt::Point(static_cast<sal_Int32>(aPoint.getX()), static_cast<sal_Int32>(aPoint.getY())); + } + uno::Sequence< uno::Sequence< awt::Point > > aPolyPolySequence( 1 ); + aPolyPolySequence.getArray()[ 0 ] = aPointSequence; + + maShapeProperties.setProperty(PROP_PolyPolygon, aPolyPolySequence); + } + else if ( aServiceName == "com.sun.star.drawing.ConnectorShape" ) + { + ::basegfx::B2DPolygon aPoly; + aPoly.insert( 0, ::basegfx::B2DPoint( 0, 0 ) ); + aPoly.insert( 1, ::basegfx::B2DPoint( maSize.Width ? 1 : 0, maSize.Height ? 1 : 0 ) ); + aPoly.transform( aTransformation ); + + basegfx::B2DPoint aStartPosition( aPoly.getB2DPoint( 0 ) ); + basegfx::B2DPoint aEndPosition( aPoly.getB2DPoint( 1 ) ); + awt::Point aAWTStartPosition( static_cast< sal_Int32 >( aStartPosition.getX() ), static_cast< sal_Int32 >( aStartPosition.getY() ) ); + awt::Point aAWTEndPosition( static_cast< sal_Int32 >( aEndPosition.getX() ), static_cast< sal_Int32 >( aEndPosition.getY() ) ); + + maShapeProperties.setProperty(PROP_StartPosition, aAWTStartPosition); + maShapeProperties.setProperty(PROP_EndPosition, aAWTEndPosition); + } + else + { + // now set transformation for this object + HomogenMatrix3 aMatrix; + + aMatrix.Line1.Column1 = aTransformation.get(0,0); + aMatrix.Line1.Column2 = aTransformation.get(0,1); + aMatrix.Line1.Column3 = aTransformation.get(0,2); + + aMatrix.Line2.Column1 = aTransformation.get(1,0); + aMatrix.Line2.Column2 = aTransformation.get(1,1); + aMatrix.Line2.Column3 = aTransformation.get(1,2); + + aMatrix.Line3.Column1 = 0; + aMatrix.Line3.Column2 = 0; + aMatrix.Line3.Column3 = 1; + + maShapeProperties.setProperty(PROP_Transformation, aMatrix); + } + + Reference< lang::XMultiServiceFactory > xServiceFact( rFilterBase.getModel(), UNO_QUERY_THROW ); + if ( !mxShape.is() ) + { + mxShape.set( xServiceFact->createInstance( aServiceName ), UNO_QUERY_THROW ); + } + + Reference< XPropertySet > xSet( mxShape, UNO_QUERY ); + if (xSet.is()) + { + if( !msName.isEmpty() ) + { + Reference< container::XNamed > xNamed( mxShape, UNO_QUERY ); + if( xNamed.is() ) + xNamed->setName( msName ); + } + if( !msDescription.isEmpty() ) + { + xSet->setPropertyValue( "Description", Any( msDescription ) ); + } + if (m_isDecorative) + { + xSet->setPropertyValue("Decorative", Any(m_isDecorative)); + } + if (aServiceName != "com.sun.star.text.TextFrame") + rxShapes->add( mxShape ); + + if ( mbHidden || mbHiddenMasterShape ) + { + SAL_INFO("oox.drawingml", "Shape::createAndInsert: invisible shape with id='" << msId << "'"); + xSet->setPropertyValue( "Visible", Any( false ) ); + // In Excel hidden means not printed, let's use visibility for now until that's handled separately + xSet->setPropertyValue( "Printable", Any( false ) ); + } + + if (mbLocked) + { + xSet->setPropertyValue("MoveProtect", Any(true)); + xSet->setPropertyValue("SizeProtect", Any(true)); + } + + ActionLockGuard const alg(mxShape); + + // sj: removing default text of placeholder objects such as SlideNumberShape or HeaderShape + if ( bClearText ) + { + uno::Reference< text::XText > xText( mxShape, uno::UNO_QUERY ); + if ( xText.is() ) + { + xText->setString( "" ); + } + } + + if (pMathXml) + { + // the "EmbeddedObject" property is read-only, so we have to create + // the shape first, and it can be read only after the shape is + // inserted into the document, so delay the actual import until here + SvGlobalName name(SO3_SM_CLASSID); + xSet->setPropertyValue("CLSID", uno::Any(name.GetHexName())); + uno::Reference<embed::XEmbeddedObject> const xObj( + xSet->getPropertyValue("EmbeddedObject"), uno::UNO_QUERY); + if (xObj.is()) + { + uno::Reference<uno::XInterface> const xMathModel(xObj->getComponent()); + oox::FormulaImExportBase *const pMagic( + dynamic_cast<oox::FormulaImExportBase*>(xMathModel.get())); + assert(pMagic); + pMagic->readFormulaOoxml(*pMathXml); + } + } + + const GraphicHelper& rGraphicHelper = rFilterBase.getGraphicHelper(); + + ::Color nLinePhClr(ColorTransparency, 0xffffffff); + ::Color nFillPhClr(ColorTransparency, 0xffffffff); + sal_Int16 nFillPhClrTheme = -1; + sal_Int16 nLinePhClrTheme = -1; + // TODO: use ph color when applying effect properties + //sal_Int32 nEffectPhClr = -1; + + // dmapper needs the original rotation angle for calculating square wrap. This angle is not + // available as property there, so store it in InteropGrabBag. + putPropertyToGrabBag("mso-rotation-angle", Any(mnRotation)); + + if( pTheme ) + { + if( const ShapeStyleRef* pLineRef = getShapeStyleRef( XML_lnRef ) ) + { + LineProperties aLineProperties; + aLineProperties.maLineFill.moFillType = XML_noFill; + if( const LineProperties* pLineProps = pTheme->getLineStyle( pLineRef->mnThemedIdx ) ) + aLineProperties.assignUsed( *pLineProps ); + nLinePhClr = pLineRef->maPhClr.getColor( rGraphicHelper ); + nLinePhClrTheme = pLineRef->maPhClr.getSchemeColorIndex(); + + // Store style-related properties to InteropGrabBag to be able to export them back + uno::Sequence<beans::PropertyValue> aProperties = comphelper::InitPropertySequence( + { + {"SchemeClr", uno::Any(pLineRef->maPhClr.getSchemeColorName())}, + {"Idx", uno::Any(pLineRef->mnThemedIdx)}, + {"Color", uno::Any(nLinePhClr)}, + {"LineStyle", uno::Any(aLineProperties.getLineStyle())}, + {"LineCap", uno::Any(aLineProperties.getLineCap())}, + {"LineJoint", uno::Any(aLineProperties.getLineJoint())}, + {"LineWidth", uno::Any(aLineProperties.getLineWidth())}, + {"Transformations", uno::Any(pLineRef->maPhClr.getTransformations())} + }); + putPropertyToGrabBag( "StyleLnRef", Any( aProperties ) ); + } + if( const ShapeStyleRef* pFillRef = getShapeStyleRef( XML_fillRef ) ) + { + if (!getFillProperties().moUseBgFill.value_or(false)) + { + nFillPhClr = pFillRef->maPhClr.getColor(rGraphicHelper); + nFillPhClrTheme = pFillRef->maPhClr.getSchemeColorIndex(); + } + + OUString sColorScheme = pFillRef->maPhClr.getSchemeColorName(); + if( !sColorScheme.isEmpty() ) + { + uno::Sequence<beans::PropertyValue> aProperties = comphelper::InitPropertySequence( + { + {"SchemeClr", uno::Any(sColorScheme)}, + {"Idx", uno::Any(pFillRef->mnThemedIdx)}, + {"Color", uno::Any(nFillPhClr)}, + {"Transformations", uno::Any(pFillRef->maPhClr.getTransformations())} + }); + + putPropertyToGrabBag( "StyleFillRef", Any( aProperties ) ); + } + } + if( const ShapeStyleRef* pEffectRef = getShapeStyleRef( XML_effectRef ) ) + { + // TODO: use ph color when applying effect properties + // nEffectPhClr = pEffectRef->maPhClr.getColor( rGraphicHelper ); + + // Store style-related properties to InteropGrabBag to be able to export them back + uno::Sequence<beans::PropertyValue> aProperties = comphelper::InitPropertySequence( + { + {"SchemeClr", uno::Any(pEffectRef->maPhClr.getSchemeColorName())}, + {"Idx", uno::Any(pEffectRef->mnThemedIdx)}, + {"Transformations", uno::Any(pEffectRef->maPhClr.getTransformations())} + }); + putPropertyToGrabBag( "StyleEffectRef", Any( aProperties ) ); + } + } + ShapePropertyMap aShapeProps( rFilterBase.getModelObjectHelper() ); + + // add properties from textbody to shape properties + if( mpTextBody ) + { + mpTextBody->getTextProperties().pushTextDistances(Size(aShapeRectHmm.Width, aShapeRectHmm.Height)); + aShapeProps.assignUsed( mpTextBody->getTextProperties().maPropertyMap ); + // Push char properties as well - specifically useful when this is a placeholder + if( mpMasterTextListStyle && mpMasterTextListStyle->getListStyle()[0].getTextCharacterProperties().moHeight.has_value() ) + aShapeProps.setProperty(PROP_CharHeight, GetFontHeight( mpMasterTextListStyle->getListStyle()[0].getTextCharacterProperties().moHeight.value() )); + } + + // applying properties + aShapeProps.assignUsed( getShapeProperties() ); + aShapeProps.assignUsed( maDefaultShapeProperties ); + if(mnRotation != 0 && bIsCustomShape) + aShapeProps.setProperty( PROP_RotateAngle, sal_Int32( NormAngle36000( Degree100(mnRotation / -600) ) )); + if( bIsEmbMedia || + bIsCustomShape || + aServiceName == "com.sun.star.drawing.GraphicObjectShape" || + aServiceName == "com.sun.star.drawing.OLE2Shape") + { + mpGraphicPropertiesPtr->pushToPropMap( aShapeProps, rGraphicHelper, mbFlipH, mbFlipV ); + } + if ( mpTablePropertiesPtr && aServiceName == "com.sun.star.drawing.TableShape" ) + { + mpTablePropertiesPtr->pushToPropSet( rFilterBase, xSet, mpMasterTextListStyle ); + if ( auto* pTableShape = dynamic_cast<sdr::table::SdrTableObj*>(SdrObject::getSdrObjectFromXShape(mxShape)) ) + { + // Disable layouting until table height is expanded to fit the content + pTableShape->SetSkipChangeLayout(true); + } + } + + FillProperties aFillProperties = getActualFillProperties(pTheme, &rShapeOrParentShapeFillProps); + if (getFillProperties().moFillType.has_value() && getFillProperties().moFillType.value() == XML_grpFill) + getFillProperties().assignUsed(aFillProperties); + if(!bIsCroppedGraphic) + aFillProperties.pushToPropMap(aShapeProps, rGraphicHelper, mnRotation, nFillPhClr, + css::awt::Size(aShapeRectHmm.Width, aShapeRectHmm.Height), + nFillPhClrTheme, mbFlipH, mbFlipV, bIsCustomShape); + + LineProperties aLineProperties = getActualLineProperties(pTheme); + aLineProperties.pushToPropMap( aShapeProps, rGraphicHelper, nLinePhClr, nLinePhClrTheme); + EffectProperties aEffectProperties = getActualEffectProperties(pTheme); + // TODO: use ph color when applying effect properties + aEffectProperties.pushToPropMap( aShapeProps, rGraphicHelper ); + + // applying autogrowheight property before setting shape size, because + // the shape size might be changed if currently autogrowheight is true + // we must also check that the PropertySet supports the property. + Reference< XPropertySetInfo > xSetInfo( xSet->getPropertySetInfo() ); + const OUString& rPropName = PropertyMap::getPropertyName( PROP_TextAutoGrowHeight ); + if( xSetInfo.is() && xSetInfo->hasPropertyByName( rPropName ) ) + if( aShapeProps.hasProperty( PROP_TextAutoGrowHeight ) ) + xSet->setPropertyValue( rPropName, Any( false ) ); + + // do not set properties at a group shape (this causes + // assertions from svx) ... + if( aServiceName != "com.sun.star.drawing.GroupShape" ) + { + if (aServiceName == "com.sun.star.text.TextFrame") + { + if (mpCustomShapePropertiesPtr && mpCustomShapePropertiesPtr->getShapeTypeOverride()) + { + uno::Reference<beans::XPropertySet> propertySet (mxShape, uno::UNO_QUERY); + uno::Sequence<beans::PropertyValue> aGrabBag; + propertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag; + sal_Int32 length = aGrabBag.getLength(); + aGrabBag.realloc( length+1); + auto pGrabBag = aGrabBag.getArray(); + pGrabBag[length].Name = "mso-orig-shape-type"; + pGrabBag[length].Value <<= mpCustomShapePropertiesPtr->getShapePresetTypeName(); + propertySet->setPropertyValue("FrameInteropGrabBag",uno::Any(aGrabBag)); + } + //If the text box has links then save the link information so that + //it can be accessed in DomainMapper_Impl.cxx while chaining the text frames. + if (isLinkedTxbx()) + { + uno::Reference<beans::XPropertySet> propertySet (mxShape, uno::UNO_QUERY); + uno::Sequence<beans::PropertyValue> aGrabBag; + propertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag; + sal_Int32 length = aGrabBag.getLength(); + aGrabBag.realloc( length + 3 ); + auto pGrabBag = aGrabBag.getArray(); + pGrabBag[length].Name = "TxbxHasLink"; + pGrabBag[length].Value <<= isLinkedTxbx(); + pGrabBag[length + 1 ].Name = "Txbx-Id"; + pGrabBag[length + 1 ].Value <<= getLinkedTxbxAttributes().id; + pGrabBag[length + 2 ].Name = "Txbx-Seq"; + pGrabBag[length + 2 ].Value <<= getLinkedTxbxAttributes().seq; + propertySet->setPropertyValue("FrameInteropGrabBag",uno::Any(aGrabBag)); + } + + // TextFrames have BackColor, not FillColor + if (aShapeProps.hasProperty(PROP_FillColor)) + { + aShapeProps.setAnyProperty(PROP_BackColor, aShapeProps.getProperty(PROP_FillColor)); + aShapeProps.erase(PROP_FillColor); + } + // TextFrames have BackColorTransparency, not FillTransparence + if (aShapeProps.hasProperty(PROP_FillTransparence)) + { + aShapeProps.setAnyProperty(PROP_BackColorTransparency, aShapeProps.getProperty(PROP_FillTransparence)); + aShapeProps.erase(PROP_FillTransparence); + } + // TextFrames have BackGraphic, not FillBitmap + if (aShapeProps.hasProperty(PROP_FillBitmap)) + { + aShapeProps.setAnyProperty(PROP_BackGraphic, aShapeProps.getProperty(PROP_FillBitmap)); + aShapeProps.erase(PROP_FillBitmap); + } + if (aShapeProps.hasProperty(PROP_FillBitmapName)) + { + uno::Any aAny = aShapeProps.getProperty(PROP_FillBitmapName); + OUString aFillBitmapName = aAny.get<OUString>(); + uno::Reference<awt::XBitmap> xBitmap = rFilterBase.getModelObjectHelper().getFillBitmap(aFillBitmapName); + uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY); + aShapeProps.setProperty(PROP_BackGraphic, xGraphic); + // aShapeProps.erase(PROP_FillBitmapName); // Maybe, leave the name as well + } + // And no LineColor property; individual borders can have colors + if (aShapeProps.hasProperty(PROP_LineColor)) + { + uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY); + static const sal_Int32 aBorders[] = + { + PROP_TopBorder, PROP_LeftBorder, PROP_BottomBorder, PROP_RightBorder + }; + for (sal_Int32 nBorder : aBorders) + { + css::table::BorderLine2 aBorderLine = xPropertySet->getPropertyValue(PropertyMap::getPropertyName(nBorder)).get<css::table::BorderLine2>(); + aBorderLine.Color = aShapeProps.getProperty(PROP_LineColor).get<sal_Int32>(); + if (aLineProperties.moLineWidth.has_value()) + aBorderLine.LineWidth = convertEmuToHmm(aLineProperties.moLineWidth.value()); + aShapeProps.setProperty(nBorder, aBorderLine); + } + aShapeProps.erase(PROP_LineColor); + } + if(mnRotation) + { + uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY); + static constexpr OUString aGrabBagPropName = u"FrameInteropGrabBag"_ustr; + uno::Sequence<beans::PropertyValue> aGrabBag; + xPropertySet->getPropertyValue(aGrabBagPropName) >>= aGrabBag; + beans::PropertyValue aPair(comphelper::makePropertyValue("mso-rotation-angle", + mnRotation)); + if (aGrabBag.hasElements()) + { + sal_Int32 nLength = aGrabBag.getLength(); + aGrabBag.realloc(nLength + 1); + aGrabBag.getArray()[nLength] = aPair; + } + else + { + aGrabBag = { aPair }; + } + xPropertySet->setPropertyValue(aGrabBagPropName, uno::Any(aGrabBag)); + } + // TextFrames have ShadowFormat, not individual shadow properties. + std::optional<sal_Int32> oShadowDistance; + if (aShapeProps.hasProperty(PROP_ShadowXDistance)) + { + oShadowDistance = aShapeProps.getProperty(PROP_ShadowXDistance).get<sal_Int32>(); + aShapeProps.erase(PROP_ShadowXDistance); + } + if (aShapeProps.hasProperty(PROP_ShadowYDistance)) + { + // There is a single 'dist' attribute, so no need to count the avg of x and y. + aShapeProps.erase(PROP_ShadowYDistance); + } + std::optional<sal_Int32> oShadowColor; + if (aShapeProps.hasProperty(PROP_ShadowColor)) + { + oShadowColor = aShapeProps.getProperty(PROP_ShadowColor).get<sal_Int32>(); + aShapeProps.erase(PROP_ShadowColor); + } + if (aShapeProps.hasProperty(PROP_Shadow)) + aShapeProps.erase(PROP_Shadow); + + if (oShadowDistance || oShadowColor || aEffectProperties.maShadow.moShadowDir.has_value()) + { + css::table::ShadowFormat aFormat; + if (oShadowColor) + aFormat.Color = *oShadowColor; + if (aEffectProperties.maShadow.moShadowDir.has_value()) + { + css::table::ShadowLocation nLocation = css::table::ShadowLocation_NONE; + switch (aEffectProperties.maShadow.moShadowDir.value()) + { + case 13500000: + nLocation = css::table::ShadowLocation_TOP_LEFT; + break; + case 18900000: + nLocation = css::table::ShadowLocation_TOP_RIGHT; + break; + case 8100000: + nLocation = css::table::ShadowLocation_BOTTOM_LEFT; + break; + case 2700000: + nLocation = css::table::ShadowLocation_BOTTOM_RIGHT; + break; + } + aFormat.Location = nLocation; + } + aFormat.ShadowWidth = *oShadowDistance; + aShapeProps.setProperty(PROP_ShadowFormat, aFormat); + } + + } + else if (mbTextBox) + { + // This introduces a TextBox in a shape in Writer. ToDo: Can we restrict it to cases + // where the TextBox edit engine is really needed? tdf#82627 + aShapeProps.setProperty(PROP_TextBox, true); + } + + if (aServiceName != "com.sun.star.text.TextFrame" && isLinkedTxbx()) + { + uno::Reference<beans::XPropertySet> propertySet (mxShape, uno::UNO_QUERY); + uno::Sequence<beans::PropertyValue> aGrabBag; + propertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag; + sal_Int32 length = aGrabBag.getLength(); + aGrabBag.realloc( length + 3 ); + auto pGrabBag = aGrabBag.getArray(); + pGrabBag[length].Name = "TxbxHasLink"; + pGrabBag[length].Value <<= isLinkedTxbx(); + pGrabBag[length + 1 ].Name = "Txbx-Id"; + pGrabBag[length + 1 ].Value <<= getLinkedTxbxAttributes().id; + pGrabBag[length + 2 ].Name = "Txbx-Seq"; + pGrabBag[length + 2 ].Value <<= getLinkedTxbxAttributes().seq; + propertySet->setPropertyValue("InteropGrabBag",uno::Any(aGrabBag)); + } + + // If the shape is a picture placeholder. + if (aServiceName == "com.sun.star.presentation.GraphicObjectShape" && !bClearText) + { + // Placeholder text should be in center of the shape. + aShapeProps.setProperty(PROP_TextContourFrame, false); + + /* Placeholder icon should be at the center of the parent shape. + * We use negative graphic crop property because of that we don't + * have padding support. + */ + uno::Reference<beans::XPropertySet> xGraphic(xSet->getPropertyValue("Graphic"), uno::UNO_QUERY); + if (xGraphic.is()) + { + awt::Size aBitmapSize; + xGraphic->getPropertyValue("Size100thMM") >>= aBitmapSize; + sal_Int32 nXMargin = (aShapeRectHmm.Width - aBitmapSize.Width) / 2; + sal_Int32 nYMargin = (aShapeRectHmm.Height - aBitmapSize.Height) / 2; + if (nXMargin > 0 && nYMargin > 0) + { + text::GraphicCrop aGraphicCrop; + aGraphicCrop.Top = nYMargin * -1; + aGraphicCrop.Bottom = nYMargin * -1; + aGraphicCrop.Left = nXMargin * -1; + aGraphicCrop.Right = nXMargin * -1; + aShapeProps.setProperty(PROP_GraphicCrop, aGraphicCrop); + } + } + } + + PropertySet( xSet ).setProperties( aShapeProps ); + + if (mpTablePropertiesPtr && aServiceName == "com.sun.star.drawing.TableShape") + { + // Powerpoint exports desired row heights (i.e. what user attempted to set it as, not how it appears visually) + // Expand table height if there are rows that can't fit the content + if (auto* pTableShape = dynamic_cast<sdr::table::SdrTableObj*>(SdrObject::getSdrObjectFromXShape(mxShape))) + { + tools::Rectangle aArea{}; + pTableShape->LayoutTableHeight(aArea); + sal_Int32 nCorrectedHeight = aArea.GetHeight(); + const auto& aShapeSize = mxShape->getSize(); + if( nCorrectedHeight > aShapeSize.Height ) + mxShape->setSize( {aShapeSize.Width, nCorrectedHeight} ); + pTableShape->SetSkipChangeLayout(false); + } + } + + if (mbLockedCanvas) + { + putPropertyToGrabBag( "LockedCanvas", Any( true ) ); + if (aServiceName == "com.sun.star.drawing.LineShape") + { + // It seems the position and size for lines inside a locked canvas is absolute. + mxShape->setPosition(awt::Point(aShapeRectHmm.X, aShapeRectHmm.Y)); + mxShape->setSize(awt::Size(aShapeRectHmm.Width, aShapeRectHmm.Height)); + } + } + + if (mbWordprocessingCanvas) + { + putPropertyToGrabBag("WordprocessingCanvas", Any(true)); + } + + // Store original fill and line colors of the shape and the theme color name to InteropGrabBag + std::vector<beans::PropertyValue> aProperties + { + comphelper::makePropertyValue("EmuLineWidth", aLineProperties.moLineWidth.value_or(0)), + comphelper::makePropertyValue("OriginalSolidFillClr", aShapeProps.getProperty(PROP_FillColor)), + comphelper::makePropertyValue("OriginalLnSolidFillClr", aShapeProps.getProperty(PROP_LineColor)) + }; + OUString sColorFillScheme = aFillProperties.maFillColor.getSchemeColorName(); + if( !aFillProperties.maFillColor.isPlaceHolder() && !sColorFillScheme.isEmpty() ) + { + aProperties.push_back(comphelper::makePropertyValue("SpPrSolidFillSchemeClr", sColorFillScheme)); + aProperties.push_back(comphelper::makePropertyValue("SpPrSolidFillSchemeClrTransformations", aFillProperties.maFillColor.getTransformations())); + } + OUString sLnColorFillScheme = aLineProperties.maLineFill.maFillColor.getSchemeColorName(); + if( !aLineProperties.maLineFill.maFillColor.isPlaceHolder() && !sLnColorFillScheme.isEmpty() ) + { + aProperties.push_back(comphelper::makePropertyValue("SpPrLnSolidFillSchemeClr", sLnColorFillScheme)); + auto aResolvedSchemeClr = aLineProperties.maLineFill.maFillColor; + aResolvedSchemeClr.clearTransformations(); + aProperties.push_back(comphelper::makePropertyValue("SpPrLnSolidFillResolvedSchemeClr", aResolvedSchemeClr.getColor(rGraphicHelper, nFillPhClr))); + aProperties.push_back(comphelper::makePropertyValue("SpPrLnSolidFillSchemeClrTransformations", aLineProperties.maLineFill.maFillColor.getTransformations())); + } + putPropertiesToGrabBag(comphelper::containerToSequence(aProperties)); + + // Store original gradient fill of the shape to InteropGrabBag + // LibreOffice doesn't support all the kinds of gradient so we save its complete definition + if( aShapeProps.hasProperty( PROP_FillGradient ) ) + { + std::vector<beans::PropertyValue> aGradientStops; + size_t i = 0; + for( const auto& [rPos, rColor] : aFillProperties.maGradientProps.maGradientStops ) + { // for each stop in the gradient definition: + + // save position + std::vector<beans::PropertyValue> aGradientStop + { + comphelper::makePropertyValue("Pos", rPos) + }; + + OUString sStopColorScheme = rColor.getSchemeColorName(); + if( sStopColorScheme.isEmpty() ) + { + // save RGB color + aGradientStop.push_back(comphelper::makePropertyValue("RgbClr", rColor.getColor(rGraphicHelper, nFillPhClr))); + // in the case of a RGB color, transformations are already applied to + // the color with the exception of alpha transformations. We only need + // to keep the transparency value to calculate the alpha value later. + if( rColor.hasTransparency() ) + aGradientStop.push_back(comphelper::makePropertyValue("Transparency", rColor.getTransparency())); + } + else + { + // save color with scheme name + aGradientStop.push_back(comphelper::makePropertyValue("SchemeClr", sStopColorScheme)); + // save all color transformations + aGradientStop.push_back(comphelper::makePropertyValue("Transformations", rColor.getTransformations())); + } + + aGradientStops.push_back(comphelper::makePropertyValue(OUString::number(i), comphelper::containerToSequence(aGradientStop))); + ++i; + } + // If getFillProperties.moFillType is unused that means gradient is defined by a theme + // which is already saved into StyleFillRef property, so no need to save the explicit values too + if( getFillProperties().moFillType.has_value() ) + putPropertyToGrabBag( "GradFillDefinition", uno::Any(comphelper::containerToSequence(aGradientStops))); + putPropertyToGrabBag( "OriginalGradFill", aShapeProps.getProperty(PROP_FillGradient) ); + } + + // store unsupported effect attributes in the grab bag + if (!aEffectProperties.m_Effects.empty()) + { + std::vector<beans::PropertyValue> aEffects; + for (auto const& it : aEffectProperties.m_Effects) + { + PropertyValue aEffect = it->getEffect(); + if( !aEffect.Name.isEmpty() ) + { + std::vector<beans::PropertyValue> aEffectsGrabBag + { + comphelper::makePropertyValue("Attribs", aEffect.Value) + }; + + Color& aColor( it->moColor ); + OUString sColorScheme = aColor.getSchemeColorName(); + if( sColorScheme.isEmpty() ) + { + // RGB color and transparency value + aEffectsGrabBag.push_back(comphelper::makePropertyValue("RgbClr", aColor.getColor(rGraphicHelper, nFillPhClr))); + aEffectsGrabBag.push_back(comphelper::makePropertyValue("RgbClrTransparency", aColor.getTransparency())); + } + else + { + // scheme color with name and transformations + aEffectsGrabBag.push_back(comphelper::makePropertyValue("SchemeClr", sColorScheme)); + aEffectsGrabBag.push_back(comphelper::makePropertyValue("SchemeClrTransformations", aColor.getTransformations())); + } + aEffects.push_back(comphelper::makePropertyValue(aEffect.Name, comphelper::containerToSequence(aEffectsGrabBag))); + } + } + putPropertyToGrabBag("EffectProperties", uno::Any(comphelper::containerToSequence(aEffects))); + } + + // add 3D effects if any + Sequence< PropertyValue > aCamera3DEffects = get3DProperties().getCameraAttributes(); + Sequence< PropertyValue > aLightRig3DEffects = get3DProperties().getLightRigAttributes(); + Sequence< PropertyValue > aShape3DEffects = get3DProperties().getShape3DAttributes( rGraphicHelper, nFillPhClr ); + if( aCamera3DEffects.hasElements() || aLightRig3DEffects.hasElements() || aShape3DEffects.hasElements() ) + { + uno::Sequence<beans::PropertyValue> a3DEffectsGrabBag = comphelper::InitPropertySequence( + { + {"Camera", uno::Any(aCamera3DEffects)}, + {"LightRig", uno::Any(aLightRig3DEffects)}, + {"Shape3D", uno::Any(aShape3DEffects)} + }); + putPropertyToGrabBag( "3DEffectProperties", Any( a3DEffectsGrabBag ) ); + } + + if( bIsCustomShape && getTextBody()) + { + + Sequence< PropertyValue > aTextCamera3DEffects = getTextBody()->get3DProperties().getCameraAttributes(); + Sequence< PropertyValue > aTextLightRig3DEffects = getTextBody()->get3DProperties().getLightRigAttributes(); + Sequence< PropertyValue > aTextShape3DEffects = getTextBody()->get3DProperties().getShape3DAttributes( rGraphicHelper, nFillPhClr ); + if( aTextCamera3DEffects.hasElements() || aTextLightRig3DEffects.hasElements() || aTextShape3DEffects.hasElements() ) + { + uno::Sequence<beans::PropertyValue> aText3DEffectsGrabBag = comphelper::InitPropertySequence( + { + {"Camera", uno::Any(aTextCamera3DEffects)}, + {"LightRig", uno::Any(aTextLightRig3DEffects)}, + {"Shape3D", uno::Any(aTextShape3DEffects)} + }); + putPropertyToGrabBag( "Text3DEffectProperties", Any( aText3DEffectsGrabBag ) ); + } + } + + // store bitmap artistic effects in the grab bag + if( !mpGraphicPropertiesPtr->maBlipProps.maEffect.isEmpty() ) + putPropertyToGrabBag( "ArtisticEffectProperties", + Any( mpGraphicPropertiesPtr->maBlipProps.maEffect.getEffect() ) ); + } + + else if( mbLockedCanvas ) + { + //If we have aServiceName as "com.sun.star.drawing.GroupShape" and lockedCanvas + putPropertyToGrabBag( "LockedCanvas", Any( true ) ); + } + else if (mbWordprocessingCanvas) + { + putPropertyToGrabBag("WordprocessingCanvas", Any(true)); + putPropertyToGrabBag("mso-edit-as", Any(OUString("canvas"))); // for export VML Fallback + } + + // These can have a custom geometry, so position should be set here, + // after creation but before custom shape handling, using the position + // we got from the caller. + if (mbWps && aServiceName == "com.sun.star.drawing.LineShape" && !pParentGroupShape) + mxShape->setPosition(maPosition); + + if (bIsConnectorShape) + { + msConnectorName = mpCustomShapePropertiesPtr->getShapePresetTypeName(); + + auto aAdjustmentList = mpCustomShapePropertiesPtr->getAdjustmentGuideList(); + for (size_t i = 0; i < aAdjustmentList.size(); i++) + maConnectorAdjustmentList.push_back(aAdjustmentList[i].maFormula); + + sal_Int32 nType = mpCustomShapePropertiesPtr->getShapePresetType(); + switch (nType) + { + case XML_line: + case XML_straightConnector1: + xSet->setPropertyValue("EdgeKind", Any(ConnectorType_LINE)); + break; + case XML_bentConnector2: + case XML_bentConnector3: + case XML_bentConnector4: + case XML_bentConnector5: + xSet->setPropertyValue("EdgeKind", Any(ConnectorType_STANDARD)); + break; + case XML_curvedConnector2: + case XML_curvedConnector3: + case XML_curvedConnector4: + case XML_curvedConnector5: + xSet->setPropertyValue("EdgeKind", Any(ConnectorType_CURVE)); + break; + default: + break; + } + } + + if( bIsCustomShape ) + { + if ( mbFlipH ) + mpCustomShapePropertiesPtr->setMirroredX( true ); + if ( mbFlipV ) + mpCustomShapePropertiesPtr->setMirroredY( true ); + if( getTextBody() ) + { + sal_Int32 nTextCameraZRotation = getTextBody()->get3DProperties().maCameraRotation.mnRevolution.value_or(0); + mpCustomShapePropertiesPtr->setTextCameraZRotateAngle( nTextCameraZRotation / 60000 ); + + // TextPreRotateAngle. Text rotates inside the text area. Might be used for diagram layout 'upr' and 'grav'. + sal_Int32 nTextPreRotateAngle = static_cast< sal_Int32 >( getTextBody()->getTextProperties().moTextPreRotation.value_or( 0 ) ); + + nTextPreRotateAngle -= mnDiagramRotation; // Use of mnDiagramRotation is unclear. It seems to be always 0 here. + + // TextRotateAngle. The text area rotates. + sal_Int32 nTextAreaRotateAngle = getTextBody()->getTextProperties().moTextAreaRotation.value_or(0); + if (getTextBody()->getTextProperties().moUpright) + { + // When upright is set, any text area transformation and shape rotation is ignored + // in MS Office. To simulate this behaviour, we rotate the text area into the + // opposite direction of the shape rotation by as much as the shape was rotated + // and so compensate the shape rotation, which is added in rendering. + nTextAreaRotateAngle = -mnRotation; + // If 45° <= shape rotation < 135° or 225° <= shape rotation < 315°, + // then MS Office adds an additional 90° rotation to the text area. + const sal_Int32 nDeg(mnRotation / 60000); + if ((nDeg >= 45 && nDeg < 135) || (nDeg >= 225 && nDeg < 315)) + { + nTextAreaRotateAngle += 5400000; + nTextPreRotateAngle -= 5400000; // compensate the additional text area rotation + } + putPropertyToGrabBag("Upright", Any(true)); + } + /* OOX measures text rotation clockwise in 1/60000th degrees, + relative to the containing shape. set*Angle wants degrees counter-clockwise. */ + mpCustomShapePropertiesPtr->setTextPreRotateAngle(-nTextPreRotateAngle / 60000); + if (nTextAreaRotateAngle != 0) + mpCustomShapePropertiesPtr->setTextAreaRotateAngle(-nTextAreaRotateAngle / 60000); + + auto sHorzOverflow = getTextBody()->getTextProperties().msHorzOverflow; + if (!sHorzOverflow.isEmpty()) + putPropertyToGrabBag("horzOverflow", uno::Any(getTextBody()->getTextProperties().msHorzOverflow)); + if (XML_ellipsis == getTextBody()->getTextProperties().moVertOverflow) + putPropertyToGrabBag("vertOverflow", uno::Any(OUString{"ellipsis"})); + } + + // Note that the script oox/source/drawingml/customshapes/generatePresetsData.pl looks + // for these ==cscode== and ==csdata== markers, so don't "clean up" these SAL_INFOs + SAL_INFO("oox.cscode", "==cscode== shape name: '" << msName << "'"); + SAL_INFO("oox.csdata", "==csdata== shape name: '" << msName << "'"); + + mpCustomShapePropertiesPtr->pushToPropSet(xSet, maSize); + + // Consider WordArt + if (mpTextBody && !mpTextBody->getTextProperties().msPrst.isEmpty() + && mpTextBody->getTextProperties().msPrst != u"textNoShape") + { + bool bFromWordArt(aShapeProps.hasProperty(PROP_FromWordArt) + ? aShapeProps.getProperty(PROP_FromWordArt).get<bool>() + : false); + FontworkHelpers::putCustomShapeIntoTextPathMode( + mxShape, mpCustomShapePropertiesPtr, mpTextBody->getTextProperties().msPrst, + bFromWordArt); + } + } + else if( getTextBody() ) + getTextBody()->getTextProperties().pushVertSimulation(); + + // tdf#133037: a bit hackish: force Shape to rotate in the opposite direction the camera would rotate + PropertySet aPropertySet(mxShape); + if ( !bUseRotationTransform && (mnRotation != 0 || nCameraRotation != 0) ) + { + // use the same logic for rotation from VML exporter (SimpleShape::implConvertAndInsert at vmlshape.cxx) + Degree100 nAngle = NormAngle36000( Degree100((mnRotation - nCameraRotation) / -600) ); + aPropertySet.setAnyProperty( PROP_RotateAngle, Any( sal_Int32( nAngle.get() ) ) ); + aPropertySet.setAnyProperty( PROP_HoriOrientPosition, Any( maPosition.X ) ); + aPropertySet.setAnyProperty( PROP_VertOrientPosition, Any( maPosition.Y ) ); + } + + // in some cases, we don't have any text body. + if( mpTextBody && ( !bDoNotInsertEmptyTextBody || !mpTextBody->isEmpty() ) ) + { + Reference < XText > xText( mxShape, UNO_QUERY ); + if ( xText.is() ) // not every shape is supporting an XText interface (e.g. GroupShape) + { + TextCharacterProperties aCharStyleProperties; + if( const ShapeStyleRef* pFontRef = getShapeStyleRef( XML_fontRef ) ) + { + if( pFontRef->mnThemedIdx != 0 ) + { + if( pTheme ) + if( const TextCharacterProperties* pCharProps = pTheme->getFontStyle( pFontRef->mnThemedIdx ) ) + aCharStyleProperties.assignUsed( *pCharProps ); + SAL_INFO("oox.drawingml", "Shape::createAndInsert: use font color"); + if ( pFontRef->maPhClr.isUsed() ) + { + aCharStyleProperties.maFillProperties.maFillColor = pFontRef->maPhClr; + aCharStyleProperties.maFillProperties.moFillType = XML_solidFill; + } + } + } + xText->setString(""); + Reference < XTextCursor > xAt = xText->createTextCursor(); + getTextBody()->insertAt( rFilterBase, xText, xAt, aCharStyleProperties, mpMasterTextListStyle ); + + const TextParagraphVector& rParagraphs = getTextBody()->getParagraphs(); + if (!rParagraphs.empty()) + { + const std::shared_ptr<TextParagraph>& pParagraph = rParagraphs[0]; + if (pParagraph->getProperties().getParaAdjust()) + { + style::ParagraphAdjust eAdjust = *pParagraph->getProperties().getParaAdjust(); + if (eAdjust == style::ParagraphAdjust_CENTER) + { + // If the first paragraph is centered, then set the para adjustment of + // the shape itself to centered as well. + aPropertySet.setAnyProperty(PROP_ParaAdjust, uno::Any(eAdjust)); + } + } + + // tdf#144092 For empty textboxes push character styles & + // endParaRPr into the Shape's properties + if (rParagraphs.size() == 1 && pParagraph->getRuns().empty()) + { + TextCharacterProperties aTextCharacterProps{ pParagraph->getCharacterStyle( + aCharStyleProperties, *mpMasterTextListStyle, + getTextBody()->getTextListStyle()) }; + aTextCharacterProps.assignUsed(pParagraph->getEndProperties()); + aTextCharacterProps.pushToPropSet(aPropertySet, rFilterBase); + } + } + + // MS Office has e.g. fill and stroke of WordArt in the character properties, + // LibreOffice uses shape properties. + if (!mpTextBody->getTextProperties().msPrst.isEmpty() + && mpTextBody->getTextProperties().msPrst != u"textNoShape") + { + lcl_copyCharPropsToShape(mxShape, mpTextBody, rFilterBase); + } + } + } + else if (mbTextBox) + { + // No drawingML text, but WPS text is expected: save the theme + // character color on the shape, then. + if(const ShapeStyleRef* pFontRef = getShapeStyleRef(XML_fontRef)) + { + ::Color nCharColor = pFontRef->maPhClr.getColor(rGraphicHelper); + aPropertySet.setAnyProperty(PROP_CharColor, uno::Any(nCharColor)); + } + } + + // Set glow effect properties + if (aEffectProperties.maGlow.moGlowRad.has_value() + && aServiceName != "com.sun.star.drawing.GroupShape") + { + uno::Reference<beans::XPropertySet> propertySet (mxShape, uno::UNO_QUERY); + propertySet->setPropertyValue("GlowEffectRadius", Any(convertEmuToHmm(aEffectProperties.maGlow.moGlowRad.value()))); + propertySet->setPropertyValue("GlowEffectColor", Any(aEffectProperties.maGlow.moGlowColor.getColor(rGraphicHelper))); + propertySet->setPropertyValue("GlowEffectTransparency", Any(aEffectProperties.maGlow.moGlowColor.getTransparency())); + } + + // Set soft edge effect properties + if (aEffectProperties.maSoftEdge.moRad.has_value()) + { + uno::Reference<beans::XPropertySet> propertySet(mxShape, uno::UNO_QUERY); + propertySet->setPropertyValue( + "SoftEdgeRadius", Any(convertEmuToHmm(aEffectProperties.maSoftEdge.moRad.value()))); + } + + // Set the stroke and fill-color properties of the OLE shape + if (aServiceName == "com.sun.star.drawing.OLE2Shape" && mxOleObjectInfo + && !mxOleObjectInfo->maShapeId.isEmpty()) + if (::oox::vml::Drawing* pVmlDrawing = rFilterBase.getVmlDrawing()) + if (const ::oox::vml::ShapeBase* pVmlShape + = pVmlDrawing->getShapes().getShapeById(mxOleObjectInfo->maShapeId)) + { + // Apply stroke props from the type model of the related VML shape. + ShapePropertyMap aPropMap(rFilterBase.getModelObjectHelper()); + pVmlShape->getTypeModel().maStrokeModel.pushToPropMap( + aPropMap, rFilterBase.getGraphicHelper()); + // And, fill-color properties as well... + pVmlShape->getTypeModel().maFillModel.pushToPropMap( + aPropMap, rFilterBase.getGraphicHelper()); + PropertySet(xSet).setProperties(aPropMap); + } + } + + if (mxShape.is()) + { + finalizeXShape( rFilterBase, rxShapes ); + + if (mpTextBody) + { + // tdf#151518. The method readjustTextDistances is fix for tdf#148321, but conflicts with + // text position in some of the SmartArt types in Writer. So exclude Writer here. + OUString sDocumentService; + rFilterBase.getMediaDescriptor()[utl::MediaDescriptor::PROP_DOCUMENTSERVICE] >>= sDocumentService; + if (sDocumentService != u"com.sun.star.text.TextDocument") + mpTextBody->getTextProperties().readjustTextDistances(mxShape); + } + } + return mxShape; +} + +void Shape::keepDiagramDrawing(XmlFilterBase& rFilterBase, const OUString& rFragmentPath) +{ + + sal_Int32 length = maDiagramDoms.getLength(); + maDiagramDoms.realloc(length + 1); + + // drawingValue[0] => dom, drawingValue[1] => Sequence of associated relationships + uno::Sequence<uno::Any> diagramDrawing{ + uno::Any(rFilterBase.importFragment(rFragmentPath)), + uno::Any(resolveRelationshipsOfTypeFromOfficeDoc(rFilterBase, rFragmentPath, u"image")) + }; + + beans::PropertyValue* pValue = maDiagramDoms.getArray(); + pValue[length].Name = "OOXDrawing"; + pValue[length].Value <<= diagramDrawing; +} + +void Shape::keepDiagramCompatibilityInfo() +{ + try + { + if( !maDiagramDoms.hasElements() ) + return; + + Reference < XPropertySet > xSet( mxShape, UNO_QUERY_THROW ); + Reference < XPropertySetInfo > xSetInfo( xSet->getPropertySetInfo() ); + if ( !xSetInfo.is() ) + return; + + const OUString aGrabBagPropName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG; + if( !xSetInfo->hasPropertyByName( aGrabBagPropName ) ) + return; + + Sequence < PropertyValue > aGrabBag; + xSet->getPropertyValue( aGrabBagPropName ) >>= aGrabBag; + + // We keep the previous items, if present + if ( aGrabBag.hasElements() ) + xSet->setPropertyValue( aGrabBagPropName, Any( comphelper::concatSequences(aGrabBag, maDiagramDoms) ) ); + else + xSet->setPropertyValue( aGrabBagPropName, Any( maDiagramDoms ) ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "oox.drawingml", "Shape::keepDiagramCompatibilityInfo" ); + } +} + +void Shape::convertSmartArtToMetafile(XmlFilterBase const & rFilterBase) +{ + try + { + Reference<XPropertySet> xSet(mxShape, UNO_QUERY_THROW); + + xSet->setPropertyValue("MoveProtect", Any(true)); + xSet->setPropertyValue("SizeProtect", Any(true)); + + // Replace existing shapes with a new Graphic Object rendered + // from them + Reference<XShape> xShape(renderDiagramToGraphic(rFilterBase)); + Reference<XShapes> xShapes(mxShape, UNO_QUERY_THROW); + tools::Rectangle aBackgroundRect + = SdrObject::getSdrObjectFromXShape( + Reference<XShape>(xShapes->getByIndex(0), UNO_QUERY_THROW)) + ->GetLogicRect(); + while (xShapes->hasElements()) + xShapes->remove(Reference<XShape>(xShapes->getByIndex(0), UNO_QUERY_THROW)); + xShapes->add(xShape); + SdrObject::getSdrObjectFromXShape( + Reference<XShape>(xShapes->getByIndex(0), UNO_QUERY_THROW)) + ->NbcSetLogicRect(aBackgroundRect); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("oox.drawingml", "Shape::convertSmartArtToMetafile"); + } +} + +Reference < XShape > Shape::renderDiagramToGraphic( XmlFilterBase const & rFilterBase ) +{ + Reference< XShape > xShape; + + try + { + if( !maDiagramDoms.hasElements() ) + return xShape; + + // Stream in which to place the rendered shape + SvMemoryStream aTempStream; + Reference < io::XStream > xStream( new utl::OStreamWrapper( aTempStream ) ); + Reference < io::XOutputStream > xOutputStream( xStream->getOutputStream() ); + + // Size of the rendering + awt::Size aActualSize = mxShape->getSize(); + Size aResolution(Application::GetDefaultDevice()->LogicToPixel(Size(100, 100), MapMode(MapUnit::MapCM))); + double fPixelsPer100thmm = static_cast < double > ( aResolution.Width() ) / 100000.0; + awt::Size aSize( static_cast < sal_Int32 > ( ( fPixelsPer100thmm * aActualSize.Width ) + 0.5 ), + static_cast < sal_Int32 > ( ( fPixelsPer100thmm * aActualSize.Height ) + 0.5 ) ); + + Sequence< PropertyValue > aFilterData{ + comphelper::makePropertyValue("PixelWidth", aSize.Width), + comphelper::makePropertyValue("PixelHeight", aSize.Height), + comphelper::makePropertyValue("LogicalWidth", aActualSize.Width), + comphelper::makePropertyValue("LogicalHeight", aActualSize.Height) + }; + + Sequence < PropertyValue > aDescriptor{ + comphelper::makePropertyValue("OutputStream", xOutputStream), + comphelper::makePropertyValue("FilterName", OUString("SVM")), // Rendering format + comphelper::makePropertyValue("FilterData", aFilterData) + }; + + Reference < lang::XComponent > xSourceDoc( mxShape, UNO_QUERY_THROW ); + Reference < XGraphicExportFilter > xGraphicExporter = GraphicExportFilter::create( rFilterBase.getComponentContext() ); + xGraphicExporter->setSourceDocument( xSourceDoc ); + xGraphicExporter->filter( aDescriptor ); + + aTempStream.Seek( STREAM_SEEK_TO_BEGIN ); + + Graphic aGraphic; + GraphicFilter aFilter( false ); + if ( aFilter.ImportGraphic( aGraphic, u"", aTempStream, GRFILTER_FORMAT_NOTFOUND, nullptr, GraphicFilterImportFlags::NONE ) != ERRCODE_NONE ) + { + SAL_WARN( "oox.drawingml", "Shape::renderDiagramToGraphic: Unable to import rendered stream into graphic object" ); + return xShape; + } + + Reference < graphic::XGraphic > xGraphic( aGraphic.GetXGraphic() ); + Reference < lang::XMultiServiceFactory > xServiceFact( rFilterBase.getModel(), UNO_QUERY_THROW ); + xShape.set( xServiceFact->createInstance( "com.sun.star.drawing.GraphicObjectShape" ), UNO_QUERY_THROW ); + Reference < XPropertySet > xPropSet( xShape, UNO_QUERY_THROW ); + xPropSet->setPropertyValue( "Graphic", Any( xGraphic ) ); + xPropSet->setPropertyValue( "MoveProtect", Any( true ) ); + xPropSet->setPropertyValue( "SizeProtect", Any( true ) ); + xPropSet->setPropertyValue( "Name", Any( OUString( "RenderedShapes" ) ) ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "oox.drawingml", "Shape::renderDiagramToGraphic" ); + } + + return xShape; +} + +void Shape::setTextBody(const TextBodyPtr & pTextBody) +{ + mpTextBody = pTextBody; +} + +void Shape::setMasterTextListStyle( const TextListStylePtr& pMasterTextListStyle ) +{ + SAL_INFO("oox.drawingml", "Shape::setMasterTextListStyle: Set master text list style to shape id='" << msId << "'"); + + mpMasterTextListStyle = pMasterTextListStyle; +} + +OUString Shape::finalizeServiceName( XmlFilterBase& rFilter, const OUString& rServiceName, const awt::Rectangle& rShapeRect ) +{ + OUString aServiceName = rServiceName; + switch( meFrameType ) + { + case FRAMETYPE_OLEOBJECT: + { + awt::Size aOleSize( rShapeRect.Width, rShapeRect.Height ); + if( rFilter.getOleObjectHelper().importOleObject( maShapeProperties, *mxOleObjectInfo, aOleSize ) ) + aServiceName = "com.sun.star.drawing.OLE2Shape"; + + // get the path to the representation graphic + OUString aGraphicPath; + if( !mxOleObjectInfo->maShapeId.isEmpty() ) + if( ::oox::vml::Drawing* pVmlDrawing = rFilter.getVmlDrawing() ) + if( const ::oox::vml::ShapeBase* pVmlShape = pVmlDrawing->getShapes().getShapeById( mxOleObjectInfo->maShapeId ) ) + aGraphicPath = pVmlShape->getGraphicPath(); + + // import and store the graphic + if( !aGraphicPath.isEmpty() ) + { + // Transfer shape's width and height to graphicsfilter (can be used by WMF/EMF) + WmfExternal aExtHeader; + aExtHeader.mapMode = 8; // MM_ANISOTROPIC + aExtHeader.xExt = rShapeRect.Width; + aExtHeader.yExt = rShapeRect.Height; + + Reference< graphic::XGraphic > xGraphic = rFilter.getGraphicHelper().importEmbeddedGraphic( aGraphicPath, &aExtHeader ); + if( xGraphic.is() ) + maShapeProperties.setProperty(PROP_Graphic, xGraphic); + } + } + break; + + default:; + } + return aServiceName; +} + +void Shape::finalizeXShape( XmlFilterBase& rFilter, const Reference< XShapes >& rxShapes ) +{ + switch( meFrameType ) + { + case FRAMETYPE_CHART: + { + OSL_ENSURE( !mxChartShapeInfo->maFragmentPath.isEmpty(), "Shape::finalizeXShape - missing chart fragment" ); + if( mxShape.is() && !mxChartShapeInfo->maFragmentPath.isEmpty() ) try + { + // set the chart2 OLE class ID at the OLE shape + PropertySet aShapeProp( mxShape ); + aShapeProp.setProperty( PROP_CLSID, OUString( "12dcae26-281f-416f-a234-c3086127382e" ) ); + + // get the XModel interface of the embedded object from the OLE shape + Reference< frame::XModel > xDocModel; + aShapeProp.getProperty( xDocModel, PROP_Model ); + Reference< chart2::XChartDocument > xChartDoc( xDocModel, UNO_QUERY_THROW ); + + // load the chart data from the XML fragment +#if ENABLE_WASM_STRIP_CHART + (void) rFilter; + (void) rxShapes; +#else + // WASM_CHART change + // TODO: Instead of using convertFromModel an alternative may be + // added to convert not to Chart/OLE SdrObejct, but to GraphicObject + // with the Chart visualization. There should be a preview available + // in the imported chart data + bool bMSO2007Doc = rFilter.isMSO2007Document(); + chart::ChartSpaceModel aModel(bMSO2007Doc); + oox::ppt::PowerPointImport* pPowerPointImport + = dynamic_cast<oox::ppt::PowerPointImport*>(&rFilter); + + ClrMapPtr pClrMap; // The original color map + if (pPowerPointImport) + { + // Use a copy of current color map, which the fragment may override locally + pClrMap = pPowerPointImport->getActualSlidePersist()->getClrMap(); + aModel.mpClrMap = pClrMap ? std::make_shared<ClrMap>(*pClrMap) + : std::make_shared<ClrMap>(); + pPowerPointImport->getActualSlidePersist()->setClrMap(aModel.mpClrMap); + } + + rtl::Reference<chart::ChartSpaceFragment> pChartSpaceFragment = new chart::ChartSpaceFragment( + rFilter, mxChartShapeInfo->maFragmentPath, aModel ); + const OUString aThemeOverrideFragmentPath( pChartSpaceFragment-> + getFragmentPathFromFirstTypeFromOfficeDoc(u"themeOverride") ); + rFilter.importFragment( pChartSpaceFragment ); + + // The original theme. + ThemePtr pTheme; + + if (!aThemeOverrideFragmentPath.isEmpty() && pPowerPointImport) + { + // Handle theme override. + uno::Reference< xml::sax::XFastSAXSerializable > xDoc( + rFilter.importFragment(aThemeOverrideFragmentPath), uno::UNO_QUERY_THROW); + pTheme = pPowerPointImport->getActualSlidePersist()->getTheme(); + auto pThemeOverride = std::make_shared<Theme>(*pTheme); + rFilter.importFragment( + new ThemeOverrideFragmentHandler(rFilter, aThemeOverrideFragmentPath, *pThemeOverride, *pThemeOverride->getTheme()), + xDoc); + pPowerPointImport->getActualSlidePersist()->setTheme(pThemeOverride); + } + + // convert imported chart model to chart document + Reference< drawing::XShapes > xExternalPage; + if( !mxChartShapeInfo->mbEmbedShapes ) + xExternalPage = rxShapes; + if( rFilter.getChartConverter() ) + { + rFilter.getChartConverter()->convertFromModel( rFilter, aModel, xChartDoc, xExternalPage, mxShape->getPosition(), mxShape->getSize() ); + if( !xChartDoc->hasInternalDataProvider() ) + { + Reference< chart2::data::XDataReceiver > xDataRec( xChartDoc, UNO_QUERY ); + Reference< chart2::data::XDataSource > xData = xDataRec->getUsedData(); + if( !xData->getDataSequences().hasElements() || !xData->getDataSequences()[0]->getValues().is() || + !xData->getDataSequences()[0]->getValues()->getData().hasElements() ) + { + rFilter.useInternalChartDataTable( true ); + rFilter.getChartConverter()->convertFromModel( rFilter, aModel, xChartDoc, xExternalPage, mxShape->getPosition(), mxShape->getSize() ); + rFilter.useInternalChartDataTable( false ); + } + } + + } + + if (pPowerPointImport) + { + if (!aThemeOverrideFragmentPath.isEmpty()) + { + // Restore the original theme. + pPowerPointImport->getActualSlidePersist()->setTheme(pTheme); + } + // Restore the original color map + pPowerPointImport->getActualSlidePersist()->setClrMap(pClrMap); + } +#endif + } + catch( Exception& ) + { + } + } + break; + + default:; + } +} + +void Shape::putPropertyToGrabBag( const OUString& sPropertyName, const Any& aPropertyValue ) +{ + PropertyValue aNewProperty; + aNewProperty.Name = sPropertyName; + aNewProperty.Value = aPropertyValue; + putPropertyToGrabBag( aNewProperty ); +} + +void Shape::putPropertyToGrabBag( const PropertyValue& pProperty ) +{ + Reference< XPropertySet > xSet( mxShape, UNO_QUERY ); + Reference< XPropertySetInfo > xSetInfo( xSet->getPropertySetInfo() ); + const OUString aGrabBagPropName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG; + if( mxShape.is() && xSet.is() && xSetInfo.is() && xSetInfo->hasPropertyByName( aGrabBagPropName ) ) + { + Sequence< PropertyValue > aGrabBag; + xSet->getPropertyValue( aGrabBagPropName ) >>= aGrabBag; + + sal_Int32 length = aGrabBag.getLength(); + aGrabBag.realloc( length + 1 ); + aGrabBag.getArray()[length] = pProperty; + + xSet->setPropertyValue( aGrabBagPropName, Any( aGrabBag ) ); + } +} + +void Shape::putPropertiesToGrabBag( const Sequence< PropertyValue >& aProperties ) +{ + Reference< XPropertySet > xSet( mxShape, UNO_QUERY ); + Reference< XPropertySetInfo > xSetInfo( xSet->getPropertySetInfo() ); + const OUString aGrabBagPropName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG; + if( !(mxShape.is() && xSet.is() && xSetInfo.is() && xSetInfo->hasPropertyByName( aGrabBagPropName )) ) + return; + + // get existing grab bag + Sequence< PropertyValue > aGrabBag; + xSet->getPropertyValue( aGrabBagPropName ) >>= aGrabBag; + + std::vector<PropertyValue> aVec; + aVec.reserve(aProperties.getLength()); + + // put the new items + std::transform(aProperties.begin(), aProperties.end(), std::back_inserter(aVec), + [](const PropertyValue& rProp) { + PropertyValue aProp; + aProp.Name = rProp.Name; + aProp.Value = rProp.Value; + return aProp; + }); + + // put it back to the shape + xSet->setPropertyValue( aGrabBagPropName, Any( comphelper::concatSequences(aGrabBag, aVec) ) ); +} + +FillProperties Shape::getActualFillProperties(const Theme* pTheme, const FillProperties* pParentShapeFillProps) const +{ + FillProperties aFillProperties; + aFillProperties.moFillType = XML_noFill; + + // Reference shape properties + aFillProperties.assignUsed( *mpShapeRefFillPropPtr ); + + // Theme + if( pTheme != nullptr ) + { + if( const ShapeStyleRef* pFillRef = getShapeStyleRef( XML_fillRef ) ) + { + if( const FillProperties* pFillProps = pTheme->getFillStyle( pFillRef->mnThemedIdx ) ) + aFillProperties.assignUsed( *pFillProps ); + } + } + + // Properties specified directly for this shape + aFillProperties.assignUsed(getFillProperties()); + + // Parent shape's properties + if ( pParentShapeFillProps != nullptr) + if( getFillProperties().moFillType.has_value() && getFillProperties().moFillType.value() == XML_grpFill ) + aFillProperties.assignUsed( *pParentShapeFillProps ); + + return aFillProperties; +} + +LineProperties Shape::getActualLineProperties(const Theme* pTheme) const +{ + LineProperties aLineProperties; + aLineProperties.maLineFill.moFillType = XML_noFill; + + // Reference shape properties + aLineProperties.assignUsed( *mpShapeRefLinePropPtr ); + + // Theme + if( pTheme != nullptr ) + { + if( const ShapeStyleRef* pLineRef = getShapeStyleRef( XML_lnRef ) ) + { + if( const LineProperties* pLineProps = pTheme->getLineStyle( pLineRef->mnThemedIdx ) ) + aLineProperties.assignUsed( *pLineProps ); + } + } + + // Properties specified directly for this shape + aLineProperties.assignUsed( getLineProperties() ); + + return aLineProperties; +} + +EffectProperties Shape::getActualEffectProperties(const Theme* pTheme) const +{ + EffectProperties aEffectProperties; + + // Reference shape properties + aEffectProperties.assignUsed( *mpShapeRefEffectPropPtr ); + + // Theme + if( pTheme != nullptr ) + { + if( const ShapeStyleRef* pEffectRef = getShapeStyleRef( XML_effectRef ) ) + { + if( const EffectProperties* pEffectProps = pTheme->getEffectStyle( pEffectRef->mnThemedIdx ) ) + aEffectProperties.assignUsed( *pEffectProps ); + } + } + + // Properties specified directly for this shape + aEffectProperties.assignUsed ( getEffectProperties() ); + + return aEffectProperties; +} + +uno::Sequence< uno::Sequence< uno::Any > > Shape::resolveRelationshipsOfTypeFromOfficeDoc(core::XmlFilterBase& rFilter, const OUString& sFragment, std::u16string_view sType ) +{ + uno::Sequence< uno::Sequence< uno::Any > > xRelListTemp; + sal_Int32 counter = 0; + + core::RelationsRef xRels = rFilter.importRelations( sFragment ); + if ( xRels ) + { + core::RelationsRef xImageRels = xRels->getRelationsFromTypeFromOfficeDoc( sType ); + if ( xImageRels ) + { + xRelListTemp.realloc( xImageRels->size() ); + auto pxRelListTemp = xRelListTemp.getArray(); + for (auto const& imageRel : *xImageRels) + { + uno::Sequence< uno::Any > diagramRelTuple (3); + auto pdiagramRelTuple = diagramRelTuple.getArray(); + // [0] => RID, [1] => InputStream [2] => extension + OUString sRelId = imageRel.second.maId; + + pdiagramRelTuple[0] <<= sRelId; + OUString sTarget = xImageRels->getFragmentPathFromRelId( sRelId ); + + uno::Reference< io::XInputStream > xImageInputStrm( rFilter.openInputStream( sTarget ), uno::UNO_SET_THROW ); + StreamDataSequence dataSeq; + if ( rFilter.importBinaryData( dataSeq, sTarget ) ) + { + pdiagramRelTuple[1] <<= dataSeq; + } + + pdiagramRelTuple[2] <<= sTarget.copy( sTarget.lastIndexOf(".") ); + + pxRelListTemp[counter] = diagramRelTuple; + ++counter; + } + xRelListTemp.realloc(counter); + + } + } + return xRelListTemp; +} + +void Shape::cloneFillProperties() +{ + auto pFillProperties = std::make_shared<FillProperties>(); + pFillProperties->assignUsed(*mpFillPropertiesPtr); + mpFillPropertiesPtr = pFillProperties; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |