/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "diagram/datamodel.hxx" #include "diagram/diagramhelper.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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() : mpLinePropertiesPtr( std::make_shared() ) , mpShapeRefLinePropPtr( std::make_shared() ) , mpFillPropertiesPtr( std::make_shared() ) , mpShapeRefFillPropPtr( std::make_shared() ) , mpGraphicPropertiesPtr( std::make_shared() ) , mpCustomShapePropertiesPtr( std::make_shared() ) , mp3DPropertiesPtr( std::make_shared() ) , mpEffectPropertiesPtr( std::make_shared() ) , mpShapeRefEffectPropPtr( std::make_shared() ) , mpMasterTextListStyle( std::make_shared() ) , 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 ) { setDefaults(/*bDefaultHeight*/true); } Shape::Shape( const OUString& rServiceName, bool bDefaultHeight ) : mpLinePropertiesPtr( std::make_shared() ) , mpShapeRefLinePropPtr( std::make_shared() ) , mpFillPropertiesPtr( std::make_shared() ) , mpShapeRefFillPropPtr( std::make_shared() ) , mpGraphicPropertiesPtr( std::make_shared() ) , mpCustomShapePropertiesPtr( std::make_shared() ) , mp3DPropertiesPtr( std::make_shared() ) , mpEffectPropertiesPtr( std::make_shared() ) , mpShapeRefEffectPropPtr( std::make_shared() ) , mpMasterTextListStyle( std::make_shared() ) , 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 ) { msServiceName = rServiceName; 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, bool bSelfCreated) { // Prepare Diagram data collecting for this Shape if( nullptr == mpDiagramHelper && FRAMETYPE_DIAGRAM == meFrameType ) { mpDiagramHelper = new AdvancedDiagramHelper( rDiagramPtr, rTheme, getSize(), bSelfCreated); } } void Shape::propagateDiagramHelper() { // Propagate collected Diagram data to data holder if (FRAMETYPE_DIAGRAM == meFrameType && nullptr != mpDiagramHelper) { SdrObjGroup* pAnchorObj = dynamic_cast(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(); 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(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 = u"com.sun.star.drawing.temporaryForXMLImportOLE2Shape"_ustr; else msServiceName = u"com.sun.star.drawing.OLE2Shape"_ustr; mxChartShapeInfo = std::make_shared( bEmbedShapes ); return *mxChartShapeInfo; } void Shape::setDiagramType() { OSL_ENSURE( meFrameType == FRAMETYPE_GENERIC, "Shape::setDiagramType - multiple frame types" ); meFrameType = FRAMETYPE_DIAGRAM; msServiceName = u"com.sun.star.drawing.GroupShape"_ustr; mnSubType = 0; } void Shape::setTableType() { OSL_ENSURE( meFrameType == FRAMETYPE_GENERIC, "Shape::setTableType - multiple frame types" ); meFrameType = FRAMETYPE_TABLE; msServiceName = u"com.sun.star.drawing.TableShape"_ustr; mnSubType = 0; } 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, const 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& xBgShape = pBgShape->getXShape(); Reference xBgProps(xBgShape, uno::UNO_QUERY); drawing::FillStyle eFillStyle = drawing::FillStyle_NONE; xBgProps->getPropertyValue(u"FillStyle"_ustr) >>= eFillStyle; drawing::LineStyle eLineStyle = drawing::LineStyle_NONE; xBgProps->getPropertyValue(u"LineStyle"_ustr) >>= 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 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(); 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(&rFilterBase) != nullptr; if (!officecfg::Office::Common::Filter::Microsoft::Import::SmartArtToShapes::get() && !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 = std::move(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( *rReferencedShape.mpTextBody ); else mpTextBody.reset(); maShapeProperties = rReferencedShape.maShapeProperties; mpShapeRefLinePropPtr = std::make_shared( rReferencedShape.getActualLineProperties(nullptr) ); mpShapeRefFillPropPtr = std::make_shared( rReferencedShape.getActualFillProperties(nullptr, nullptr) ); mpCustomShapePropertiesPtr = std::make_shared( *rReferencedShape.mpCustomShapePropertiesPtr ); mpTablePropertiesPtr = rReferencedShape.mpTablePropertiesPtr ? std::make_shared( *rReferencedShape.mpTablePropertiesPtr ) : nullptr; mpShapeRefEffectPropPtr = std::make_shared( rReferencedShape.getActualEffectProperties(nullptr) ); mpMasterTextListStyle = std::make_shared( *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 const& xShape) : m_xLockable(xShape, UNO_QUERY) { if (m_xLockable.is()) { m_xLockable->addActionLock(); } } ~ActionLockGuard() { if (m_xLockable.is()) { m_xLockable->removeActionLock(); } } private: Reference 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; } static TextHorizontalAdjust lcl_convertTextAdjust(ParagraphAdjust eAdjust) { if (eAdjust == ParagraphAdjust_LEFT) return drawing::TextHorizontalAdjust_LEFT; else if (eAdjust == ParagraphAdjust_RIGHT) return drawing::TextHorizontalAdjust_RIGHT; else return drawing::TextHorizontalAdjust_BLOCK; } // 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& xShape, const TextBodyPtr& pTextBody, const ::oox::core::XmlFilterBase& rFilter) { if (!xShape.is() || !pTextBody) return; Reference 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 pParagraph) { return pParagraph->getRuns().empty(); }); if (aParaIt != rParagraphs.cend()) { const std::shared_ptr& pParagraph = *aParaIt; const TextRunVector& rRuns = pParagraph->getRuns(); auto aRunIt = std::find_if_not(rRuns.cbegin(), rRuns.cend(), [](const std::shared_ptr pRun) { return pRun->getText().isEmpty() || pRun->getText() == " " || pRun->getText().toChar() == 0xA0; // NBSP }); if (aRunIt != rRuns.cend()) { const std::shared_ptr& pRun = *aRunIt; TextCharacterProperties& rCharProps = pRun->getTextCharacterProperties(); // set language if (rCharProps.moLang.has_value() && !rCharProps.moLang.value().isEmpty()) { LanguageTag aTag(rCharProps.moLang.value()); const 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(u"ParaAdjust"_ustr, 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; } Degree100 lcl_MSORotateAngleToAPIAngle(const sal_Int32 nMSORotationAngle) { // Converts a shape rotation angle from MSO to angle for API property RotateAngle // from unit 1/60000 deg to unit 1/100 deg Degree100 nAngle(nMSORotationAngle / 600); // API RotateAngle has opposite direction than nMSORotationAngle, thus 'minus'. return NormAngle36000(-nAngle); } } 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, const 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(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 = u"com.sun.star.drawing.OLE2Shape"_ustr; 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, u"com.sun.star.presentation.MediaShape"_ustr, 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"); // Look for 3D. Its z-rotation and extrusion color become shape properties. We consider a // z-rotation of an image even we currently do not extrude an image to 3D-scene. bool bBlockExtrusion = !bIsCustomShape && mp3DPropertiesPtr->mnPreset.has_value(); double fShapeRotateInclCamera = 0.0; // unit rad; same orientation as shape property RotateAngle Color aExtrusionColor; Scene3DHelper aScene3DHelper; bool bHas3DEffect = aScene3DHelper.setExtrusionProperties( mp3DPropertiesPtr, mnRotation, getCustomShapeProperties()->getExtrusionPropertyMap(), fShapeRotateInclCamera, aExtrusionColor, bBlockExtrusion); // Currently the other places use unit 1/60000deg and MSO shape rotate orientation. sal_Int32 nShapeRotateInclCamera = -basegfx::rad2deg<60000>(fShapeRotateInclCamera); bool bIs3DGraphic = aServiceName == "com.sun.star.drawing.GraphicObjectShape" && bHas3DEffect; bIsCustomShape |= bIs3DGraphic; // The extrusion color does not belong to the extrusion properties but is secondary color in // the style of the shape, FillColor2 in API. The case that no extrusion color was set is handled // further down when FillProperties and LineProperties are handled. if (aExtrusionColor.isUsed()) { // FillColor2 is not yet transformed to ComplexColor. ::Color aColor = aExtrusionColor.getColor(rFilterBase.getGraphicHelper()); maShapeProperties.setProperty(PROP_FillColor2, aColor); } if (bHas3DEffect) { aScene3DHelper.setLightingProperties(mp3DPropertiesPtr, fShapeRotateInclCamera, getCustomShapeProperties()->getExtrusionPropertyMap()); oox::Scene3DHelper::setMaterialProperties( mp3DPropertiesPtr, getCustomShapeProperties()->getExtrusionPropertyMap()); } if (bIsCroppedGraphic || bIs3DGraphic) { aServiceName = "com.sun.star.drawing.CustomShape"; mpGraphicPropertiesPtr->mbIsCustomShape = true; mpGraphicPropertiesPtr->mbIsExtruded = bIs3DGraphic; } 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); } bool bLineShape = aServiceName == "com.sun.star.drawing.LineShape"; bool bTopWriterLine = !pParentGroupShape && mbWps && bLineShape; // 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 (bLineShape || aServiceName == "com.sun.star.drawing.ConnectorShape") { // For toplevel Writer lines, size is included in the point coordinates. if (!bTopWriterLine) { 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. if (bUseRotationTransform && nShapeRotateInclCamera != 0) { lcl_RotateAtCenter(aTransformation, nOrientation * nShapeRotateInclCamera); } 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); if (!bTopWriterLine) { 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 (bLineShape) { ::basegfx::B2DPolygon aPoly; aPoly.insert( 0, ::basegfx::B2DPoint( 0, 0 ) ); if (bTopWriterLine) { // No transform of individual points, everything apart from size is part of the // transform matrix. sal_Int32 nMM100Width = o3tl::convert(maSize.Width, o3tl::Length::emu, o3tl::Length::mm100); sal_Int32 nMM100Height = o3tl::convert(maSize.Height, o3tl::Length::emu, o3tl::Length::mm100); aPoly.insert(1, ::basegfx::B2DPoint(nMM100Width, nMM100Height)); } else { 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(aPoint.getX()), static_cast(aPoint.getY())); } uno::Sequence< uno::Sequence< awt::Point > > aPolyPolySequence( 1 ); aPolyPolySequence.getArray()[ 0 ] = std::move(aPointSequence); maShapeProperties.setProperty(PROP_PolyPolygon, aPolyPolySequence); } 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 if (!bLineShape || bTopWriterLine) { // 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( u"Description"_ustr, Any( msDescription ) ); } if (m_isDecorative) { xSet->setPropertyValue(u"Decorative"_ustr, Any(m_isDecorative)); } if (!msMacro.isEmpty()) { putPropertyToGrabBag(u"mso-sp-macro"_ustr, Any(msMacro)); } if (!msTextLink.isEmpty()) { putPropertyToGrabBag(u"mso-sp-textlink"_ustr, Any(msTextLink)); } if (!mbFLocksText) // set only if "false", otherwise it will use "true" by default { putPropertyToGrabBag(u"mso-sp-fLocksText"_ustr, Any(mbFLocksText)); } if (mbFPublished) { putPropertyToGrabBag(u"mso-sp-fPublished"_ustr, Any(mbFPublished)); } if (!msTitle.isEmpty()) { xSet->setPropertyValue(u"Title"_ustr, Any(msTitle)); } // get tooltip attribute of OUString sTooltip; getShapeProperties().getProperty(PROP_Representation) >>= sTooltip; if (!sTooltip.isEmpty()) putPropertyToGrabBag(u"mso-hlinkClick-tooltip"_ustr, Any(sTooltip)); // Placeholder uses the height set on the slide instead of the height from the master slide, // if it has the "TextAutoGrowHeight" property if (getTextBody() && mxShape->getShapeType().startsWith("com.sun.star.presentation.")) { bool bAutoGrowHeight = getTextBody() ->getTextProperties() .maPropertyMap.getProperty(PROP_TextAutoGrowHeight) .get(); if (bAutoGrowHeight) { ppt::PowerPointImport* pPPT = dynamic_cast(&rFilterBase); if (!pPPT->getActualSlidePersist()->isMasterPage()) { sal_Int32 nUpper = 0; sal_Int32 nLower = 0; sal_Int32 nHeight = maSize.Height / 360; if (getTextBody()->getTextProperties().moInsets[1].has_value() && getTextBody()->getTextProperties().moInsets[3].has_value()) { nUpper = *getTextBody()->getTextProperties().moInsets[1]; nLower = *getTextBody()->getTextProperties().moInsets[3]; } else { maDefaultShapeProperties.getProperty(PROP_TextUpperDistance) >>= nUpper; maDefaultShapeProperties.getProperty(PROP_TextLowerDistance) >>= nLower; } nHeight -= (nUpper + nLower); mxShape->setSize(awt::Size(0, nHeight)); } } else // the placeholder uses the height set on the master slide mxShape->setSize(awt::Size(0, 0)); } 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( u"Visible"_ustr, Any( false ) ); // In Excel hidden means not printed, let's use visibility for now until that's handled separately xSet->setPropertyValue( u"Printable"_ustr, Any( false ) ); } if (mbLocked) { xSet->setPropertyValue(u"MoveProtect"_ustr, Any(true)); xSet->setPropertyValue(u"SizeProtect"_ustr, 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( u""_ustr ); } } 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(u"CLSID"_ustr, uno::Any(name.GetHexName())); uno::Reference const xObj( xSet->getPropertyValue(u"EmbeddedObject"_ustr), uno::UNO_QUERY); if (xObj.is()) { uno::Reference const xMathModel(xObj->getComponent()); oox::FormulaImExportBase *const pMagic( dynamic_cast(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(u"mso-rotation-angle"_ustr, 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 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( u"StyleLnRef"_ustr, 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 aProperties = comphelper::InitPropertySequence( { {"SchemeClr", uno::Any(sColorScheme)}, {"Idx", uno::Any(pFillRef->mnThemedIdx)}, {"Color", uno::Any(nFillPhClr)}, {"Transformations", uno::Any(pFillRef->maPhClr.getTransformations())} }); putPropertyToGrabBag( u"StyleFillRef"_ustr, 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 aProperties = comphelper::InitPropertySequence( { {"SchemeClr", uno::Any(pEffectRef->maPhClr.getSchemeColorName())}, {"Idx", uno::Any(pEffectRef->mnThemedIdx)}, {"Transformations", uno::Any(pEffectRef->maPhClr.getTransformations())} }); putPropertyToGrabBag( u"StyleEffectRef"_ustr, Any( aProperties ) ); } } ShapePropertyMap aShapeProps( rFilterBase.getModelObjectHelper() ); // add properties from textbody to shape properties if( mpTextBody ) { // tdf#67347: In case of Stacked, PP calculates in the vertical direction with the // horizontal alignment. // In LO, we simulate it by setting TextVerticalAdjust based on the ParagraphAdjust // of the 1. paragraph // It is not perfect, because we have 1 TextVerticalAdjust / 1 shape, and it // does not support justified, while we can have many ParagraphAdjust / 1 shape // (if the shape have more paragraphs) if (mpTextBody->getTextProperties().maPropertyMap.hasProperty(PROP_WritingMode) && mpTextBody->getTextProperties().maPropertyMap.getProperty(PROP_WritingMode) == uno::Any(text::WritingMode2::STACKED) && mpTextBody->getParagraphs().size() > 0 && aServiceName != "com.sun.star.drawing.GroupShape") { std::optional& oParaAdjust = mpTextBody->getParagraphs()[0]->getProperties().getParaAdjust(); if (oParaAdjust) { switch (*oParaAdjust) { case ParagraphAdjust::ParagraphAdjust_LEFT: mpTextBody->getTextProperties().meVA = TextVerticalAdjust::TextVerticalAdjust_TOP; break; case ParagraphAdjust::ParagraphAdjust_CENTER: mpTextBody->getTextProperties().meVA = TextVerticalAdjust::TextVerticalAdjust_CENTER; break; case ParagraphAdjust::ParagraphAdjust_RIGHT: mpTextBody->getTextProperties().meVA = TextVerticalAdjust::TextVerticalAdjust_BOTTOM; break; default: break; } mpTextBody->getTextProperties().maPropertyMap.setProperty( PROP_TextVerticalAdjust, mpTextBody->getTextProperties().meVA); } } // tdf#162571: In case of shapes with TextAutoGrowHeight, PP calculates/grow the // shapes size in edit mode (typing) based on the text horizontal alignment. // In LO, we simulate it by setting TextHorizontalAdjust based on the ParagraphAdjust // of the 1. paragraph // It is not perfect, because we have 1 TextHorizontalAdjust / 1 shape, // while we can have many ParagraphAdjust / 1 shape if (!mpTextBody->getTextProperties().maPropertyMap.hasProperty(PROP_WritingMode) && mpTextBody->getParagraphs().size() > 0) { std::optional& oParaAdjust = mpTextBody->getParagraphs()[0]->getProperties().getParaAdjust(); bool bAutoHeight = false; Reference< XPropertySetInfo > xSetInfo(xSet->getPropertySetInfo()); const OUString& rPropName = PropertyMap::getPropertyName(PROP_TextAutoGrowHeight); if (xSetInfo.is() && xSetInfo->hasPropertyByName(rPropName)) { uno::Any aTextAutoGrowHeight = xSet->getPropertyValue(u"TextAutoGrowHeight"_ustr); aTextAutoGrowHeight >>= bAutoHeight; } if (bAutoHeight && nShapeRotateInclCamera == 0) { mpTextBody->getTextProperties().maPropertyMap.setProperty( PROP_TextHorizontalAdjust, lcl_convertTextAdjust( oParaAdjust ? *oParaAdjust : ParagraphAdjust_LEFT)); } } 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(nShapeRotateInclCamera != 0 && bIsCustomShape) aShapeProps.setProperty(PROP_RotateAngle, sal_Int32(lcl_MSORotateAngleToAPIAngle(nShapeRotateInclCamera))); 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(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 && !bIs3DGraphic) 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 ) ); // For extruded shapes, MSO uses the line color if no extrusion color is specified. LO uses // fill color in 'automatic' case. Thus we set extrusion color explicitly. if (bHas3DEffect && !aExtrusionColor.isUsed()) { const OUString& rFillColor2PropName = PropertyMap::getPropertyName(PROP_FillColor2); if (xSetInfo.is() && xSetInfo->hasPropertyByName(rFillColor2PropName)) { Color aComplexColor; if (aLineProperties.maLineFill.moFillType.has_value() && aLineProperties.maLineFill.moFillType.value() != XML_noFill) aComplexColor = aLineProperties.maLineFill.getBestSolidColor(); else if (aFillProperties.moFillType.has_value() && aFillProperties.moFillType.value() != XML_noFill) aComplexColor = aFillProperties.getBestSolidColor(); if (aComplexColor.isUsed()) { const ::Color aSimpleColor = aComplexColor.getColor(rFilterBase.getGraphicHelper()); xSet->setPropertyValue(rFillColor2PropName, Any(aSimpleColor)); } } } // 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 propertySet (mxShape, uno::UNO_QUERY); uno::Sequence aGrabBag; propertySet->getPropertyValue(u"FrameInteropGrabBag"_ustr) >>= 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(u"FrameInteropGrabBag"_ustr,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 propertySet (mxShape, uno::UNO_QUERY); uno::Sequence aGrabBag; propertySet->getPropertyValue(u"FrameInteropGrabBag"_ustr) >>= 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(u"FrameInteropGrabBag"_ustr,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(); uno::Reference xBitmap = rFilterBase.getModelObjectHelper().getFillBitmap(aFillBitmapName); uno::Reference 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 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(); aBorderLine.Color = aShapeProps.getProperty(PROP_LineColor).get(); if (aLineProperties.moLineWidth.has_value()) aBorderLine.LineWidth = convertEmuToHmm(aLineProperties.moLineWidth.value()); aShapeProps.setProperty(nBorder, aBorderLine); } aShapeProps.erase(PROP_LineColor); } if(mnRotation) { uno::Reference xPropertySet(mxShape, uno::UNO_QUERY); static constexpr OUString aGrabBagPropName = u"FrameInteropGrabBag"_ustr; uno::Sequence aGrabBag; xPropertySet->getPropertyValue(aGrabBagPropName) >>= aGrabBag; beans::PropertyValue aPair(comphelper::makePropertyValue(u"mso-rotation-angle"_ustr, mnRotation)); if (aGrabBag.hasElements()) { sal_Int32 nLength = aGrabBag.getLength(); aGrabBag.realloc(nLength + 1); aGrabBag.getArray()[nLength] = std::move(aPair); } else { aGrabBag = { std::move(aPair) }; } xPropertySet->setPropertyValue(aGrabBagPropName, uno::Any(aGrabBag)); } // TextFrames have ShadowFormat, not individual shadow properties. std::optional oShadowDistance; if (aShapeProps.hasProperty(PROP_ShadowXDistance)) { oShadowDistance = aShapeProps.getProperty(PROP_ShadowXDistance).get(); 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 oShadowColor; if (aShapeProps.hasProperty(PROP_ShadowColor)) { oShadowColor = aShapeProps.getProperty(PROP_ShadowColor).get(); 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 propertySet (mxShape, uno::UNO_QUERY); uno::Sequence aGrabBag; propertySet->getPropertyValue(u"InteropGrabBag"_ustr) >>= 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(u"InteropGrabBag"_ustr,uno::Any(aGrabBag)); } 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(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( u"LockedCanvas"_ustr, 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(u"WordprocessingCanvas"_ustr, Any(true)); } // Store original fill and line colors of the shape and the theme color name to InteropGrabBag std::vector aProperties { comphelper::makePropertyValue(u"EmuLineWidth"_ustr, aLineProperties.moLineWidth.value_or(0)), comphelper::makePropertyValue(u"OriginalSolidFillClr"_ustr, aShapeProps.getProperty(PROP_FillColor)), comphelper::makePropertyValue(u"OriginalLnSolidFillClr"_ustr, aShapeProps.getProperty(PROP_LineColor)) }; OUString sColorFillScheme = aFillProperties.maFillColor.getSchemeColorName(); if( !aFillProperties.maFillColor.isPlaceHolder() && !sColorFillScheme.isEmpty() ) { aProperties.push_back(comphelper::makePropertyValue(u"SpPrSolidFillSchemeClr"_ustr, sColorFillScheme)); aProperties.push_back(comphelper::makePropertyValue(u"SpPrSolidFillSchemeClrTransformations"_ustr, aFillProperties.maFillColor.getTransformations())); } OUString sLnColorFillScheme = aLineProperties.maLineFill.maFillColor.getSchemeColorName(); if( !aLineProperties.maLineFill.maFillColor.isPlaceHolder() && !sLnColorFillScheme.isEmpty() ) { aProperties.push_back(comphelper::makePropertyValue(u"SpPrLnSolidFillSchemeClr"_ustr, sLnColorFillScheme)); auto aResolvedSchemeClr = aLineProperties.maLineFill.maFillColor; aResolvedSchemeClr.clearTransformations(); aProperties.push_back(comphelper::makePropertyValue(u"SpPrLnSolidFillResolvedSchemeClr"_ustr, aResolvedSchemeClr.getColor(rGraphicHelper, nFillPhClr))); aProperties.push_back(comphelper::makePropertyValue(u"SpPrLnSolidFillSchemeClrTransformations"_ustr, 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 aGradientStops; size_t i = 0; for( const auto& [rPos, rColor] : aFillProperties.maGradientProps.maGradientStops ) { // for each stop in the gradient definition: // save position std::vector aGradientStop { comphelper::makePropertyValue(u"Pos"_ustr, rPos) }; OUString sStopColorScheme = rColor.getSchemeColorName(); if( sStopColorScheme.isEmpty() ) { // save RGB color aGradientStop.push_back(comphelper::makePropertyValue(u"RgbClr"_ustr, 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(u"Transparency"_ustr, rColor.getTransparency())); } else { // save color with scheme name aGradientStop.push_back(comphelper::makePropertyValue(u"SchemeClr"_ustr, sStopColorScheme)); // save all color transformations aGradientStop.push_back(comphelper::makePropertyValue(u"Transformations"_ustr, 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( u"GradFillDefinition"_ustr, uno::Any(comphelper::containerToSequence(aGradientStops))); putPropertyToGrabBag( u"OriginalGradFill"_ustr, aShapeProps.getProperty(PROP_FillGradient) ); } // store unsupported effect attributes in the grab bag if (!aEffectProperties.m_Effects.empty()) { std::vector aEffects; for (auto const& it : aEffectProperties.m_Effects) { PropertyValue aEffect = it->getEffect(); if( !aEffect.Name.isEmpty() ) { std::vector aEffectsGrabBag { comphelper::makePropertyValue(u"Attribs"_ustr, aEffect.Value) }; Color& aColor( it->moColor ); OUString sColorScheme = aColor.getSchemeColorName(); if( sColorScheme.isEmpty() ) { // RGB color and transparency value aEffectsGrabBag.push_back(comphelper::makePropertyValue(u"RgbClr"_ustr, aColor.getColor(rGraphicHelper, nFillPhClr))); aEffectsGrabBag.push_back(comphelper::makePropertyValue(u"RgbClrTransparency"_ustr, aColor.getTransparency())); } else { // scheme color with name and transformations aEffectsGrabBag.push_back(comphelper::makePropertyValue(u"SchemeClr"_ustr, sColorScheme)); aEffectsGrabBag.push_back(comphelper::makePropertyValue(u"SchemeClrTransformations"_ustr, aColor.getTransformations())); } aEffects.push_back(comphelper::makePropertyValue(aEffect.Name, comphelper::containerToSequence(aEffectsGrabBag))); } } putPropertyToGrabBag(u"EffectProperties"_ustr, uno::Any(comphelper::containerToSequence(aEffects))); } // add 3D effects if any to GrabBag. They are still used in export. 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 a3DEffectsGrabBag = comphelper::InitPropertySequence( { {"Camera", uno::Any(aCamera3DEffects)}, {"LightRig", uno::Any(aLightRig3DEffects)}, {"Shape3D", uno::Any(aShape3DEffects)} }); putPropertyToGrabBag( u"3DEffectProperties"_ustr, 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 aText3DEffectsGrabBag = comphelper::InitPropertySequence( { {"Camera", uno::Any(aTextCamera3DEffects)}, {"LightRig", uno::Any(aTextLightRig3DEffects)}, {"Shape3D", uno::Any(aTextShape3DEffects)} }); putPropertyToGrabBag( u"Text3DEffectProperties"_ustr, Any( aText3DEffectsGrabBag ) ); } } // store bitmap artistic effects in the grab bag if( !mpGraphicPropertiesPtr->maBlipProps.maEffect.isEmpty() ) putPropertyToGrabBag( u"ArtisticEffectProperties"_ustr, Any( mpGraphicPropertiesPtr->maBlipProps.maEffect.getEffect() ) ); } else if( mbLockedCanvas ) { //If we have aServiceName as "com.sun.star.drawing.GroupShape" and lockedCanvas putPropertyToGrabBag( u"LockedCanvas"_ustr, Any( true ) ); } else if (mbWordprocessingCanvas) { putPropertyToGrabBag(u"WordprocessingCanvas"_ustr, Any(true)); putPropertyToGrabBag(u"mso-edit-as"_ustr, Any(u"canvas"_ustr)); // 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(); const 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(u"EdgeKind"_ustr, Any(ConnectorType_LINE)); break; case XML_bentConnector2: case XML_bentConnector3: case XML_bentConnector4: case XML_bentConnector5: xSet->setPropertyValue(u"EdgeKind"_ustr, Any(ConnectorType_STANDARD)); break; case XML_curvedConnector2: case XML_curvedConnector3: case XML_curvedConnector4: case XML_curvedConnector5: xSet->setPropertyValue(u"EdgeKind"_ustr, 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(u"Upright"_ustr, 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(u"horzOverflow"_ustr, uno::Any(getTextBody()->getTextProperties().msHorzOverflow)); if (XML_ellipsis == getTextBody()->getTextProperties().moVertOverflow) putPropertyToGrabBag(u"vertOverflow"_ustr, uno::Any(u"ellipsis"_ustr)); } // 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() : 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 && (nShapeRotateInclCamera !=0) ) { Degree100 nAngle(lcl_MSORotateAngleToAPIAngle(nShapeRotateInclCamera)); aPropertySet.setAnyProperty(PROP_RotateAngle, Any( sal_Int32(nAngle))); aPropertySet.setAnyProperty( PROP_HoriOrientPosition, Any( maPosition.X ) ); aPropertySet.setAnyProperty( PROP_VertOrientPosition, Any( maPosition.Y ) ); } // Make sure to not set text to placeholders. Doing it here would eventually call // SvxTextEditSourceImpl::UpdateData, SdrObject::SetEmptyPresObj(false), and that // would make the object behave like a standard outline object. // TODO/FIXME: support custom prompt text in placeholders. if (rServiceName == "com.sun.star.presentation.GraphicObjectShape") mpTextBody.reset(); // 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(u""_ustr); Reference < XTextCursor > xAt = xText->createTextCursor(); getTextBody()->insertAt( rFilterBase, xText, xAt, aCharStyleProperties, mpMasterTextListStyle ); const TextParagraphVector& rParagraphs = getTextBody()->getParagraphs(); if (!rParagraphs.empty()) { const std::shared_ptr& 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 propertySet (mxShape, uno::UNO_QUERY); propertySet->setPropertyValue(u"GlowEffectRadius"_ustr, Any(convertEmuToHmm(aEffectProperties.maGlow.moGlowRad.value()))); propertySet->setPropertyValue(u"GlowEffectColor"_ustr, Any(aEffectProperties.maGlow.moGlowColor.getColor(rGraphicHelper))); propertySet->setPropertyValue(u"GlowEffectTransparency"_ustr, Any(aEffectProperties.maGlow.moGlowColor.getTransparency())); } // Set soft edge effect properties if (aEffectProperties.maSoftEdge.moRad.has_value()) { uno::Reference propertySet(mxShape, uno::UNO_QUERY); propertySet->setPropertyValue( u"SoftEdgeRadius"_ustr, Any(convertEmuToHmm(aEffectProperties.maSoftEdge.moRad.value()))); } // Set text glow effect for shapes if (mpTextBody && (!bDoNotInsertEmptyTextBody || !mpTextBody->isEmpty())) { const TextParagraphVector& rParagraphs = mpTextBody->getParagraphs(); if (!rParagraphs.empty()) { EffectProperties aTextEffectProperties; for (TextParagraphVector::const_iterator aPIt = rParagraphs.begin(), aPEnd = rParagraphs.end(); aPIt != aPEnd; ++aPIt) { const TextParagraph& rTextPara = **aPIt; const TextCharacterProperties & rParaProps = rTextPara.getProperties().getTextCharacterProperties(); if (rParaProps.getEffectProperties().maGlow.moGlowRad.has_value()) { aTextEffectProperties.assignUsed(rParaProps.getEffectProperties()); goto found; } else { for (TextRunVector::const_iterator aRIt = rTextPara.getRuns().begin(), aREnd = rTextPara.getRuns().end(); aRIt != aREnd; ++aRIt) { const TextRun& rTextRun = **aRIt; const TextCharacterProperties& rRunrops = rTextRun.getTextCharacterProperties(); if (rRunrops.getEffectProperties().maGlow.moGlowRad.has_value()) { aTextEffectProperties.assignUsed(rRunrops.getEffectProperties()); goto found; } } } } found: if (aTextEffectProperties.maGlow.moGlowRad.has_value()) { xSet->setPropertyValue(u"GlowTextEffectRadius"_ustr, uno::Any(convertEmuToHmm(aTextEffectProperties.maGlow.moGlowRad.value()))); xSet->setPropertyValue(u"GlowTextEffectColor"_ustr, uno::Any(aTextEffectProperties.maGlow.moGlowColor.getColor(rGraphicHelper))); xSet->setPropertyValue(u"GlowTextEffectTransparency"_ustr, uno::Any(aTextEffectProperties.maGlow.moGlowColor.getTransparency())); } } } // 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); // tdf#156857: ooxml files can have shape size with spAutoFit=true and the first priority of // shape size is the fix size even if TextAutoGrowHeight is true. bool bAutoHeight = false; Reference< XPropertySetInfo > xSetInfo(xSet->getPropertySetInfo()); const OUString& rPropName = PropertyMap::getPropertyName(PROP_TextAutoGrowHeight); if (xSetInfo.is() && xSetInfo->hasPropertyByName(rPropName)) { uno::Any aTextAutoGrowHeight = xSet->getPropertyValue(u"TextAutoGrowHeight"_ustr); aTextAutoGrowHeight >>= bAutoHeight; } SdrObject* pShape = SdrObject::getSdrObjectFromXShape(mxShape); if (pShape && bAutoHeight && bIsCustomShape) { tools::Rectangle aOrigSize(aShapeRectHmm.X, aShapeRectHmm.Y, aShapeRectHmm.X + aShapeRectHmm.Width, aShapeRectHmm.Y + aShapeRectHmm.Height); tools::Rectangle aAdaptSize = pShape->GetLogicRect(); // a little tolerance same as in \svx\source\svdraw\svdoashp.cxx:AdjustTextFrameWidthAndHeight if (std::abs(aOrigSize.GetHeight() - aAdaptSize.GetHeight()) > 1) { aAdaptSize.setHeight(aOrigSize.GetHeight()); pShape->NbcSetLogicRect(aAdaptSize, false); } } } } 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 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 xSet(mxShape, UNO_QUERY_THROW); xSet->setPropertyValue(u"MoveProtect"_ustr, Any(true)); xSet->setPropertyValue(u"SizeProtect"_ustr, Any(true)); // Replace existing shapes with a new Graphic Object rendered // from them Reference xShape(renderDiagramToGraphic(rFilterBase)); Reference xShapes(mxShape, UNO_QUERY_THROW); tools::Rectangle aBackgroundRect = SdrObject::getSdrObjectFromXShape( Reference(xShapes->getByIndex(0), UNO_QUERY_THROW)) ->GetLogicRect(); while (xShapes->hasElements()) xShapes->remove(Reference(xShapes->getByIndex(0), UNO_QUERY_THROW)); xShapes->add(xShape); SdrObject::getSdrObjectFromXShape( Reference(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(u"PixelWidth"_ustr, aSize.Width), comphelper::makePropertyValue(u"PixelHeight"_ustr, aSize.Height), comphelper::makePropertyValue(u"LogicalWidth"_ustr, aActualSize.Width), comphelper::makePropertyValue(u"LogicalHeight"_ustr, aActualSize.Height) }; Sequence < PropertyValue > aDescriptor{ comphelper::makePropertyValue(u"OutputStream"_ustr, xOutputStream), comphelper::makePropertyValue(u"FilterName"_ustr, u"SVM"_ustr), // Rendering format comphelper::makePropertyValue(u"FilterData"_ustr, 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; 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( u"com.sun.star.drawing.GraphicObjectShape"_ustr ), UNO_QUERY_THROW ); Reference < XPropertySet > xPropSet( xShape, UNO_QUERY_THROW ); xPropSet->setPropertyValue( u"Graphic"_ustr, Any( xGraphic ) ); xPropSet->setPropertyValue( u"MoveProtect"_ustr, Any( true ) ); xPropSet->setPropertyValue( u"SizeProtect"_ustr, Any( true ) ); xPropSet->setPropertyValue( u"Name"_ustr, Any( u"RenderedShapes"_ustr ) ); } 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, u"12dcae26-281f-416f-a234-c3086127382e"_ustr ); // 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(&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(*pClrMap) : std::make_shared(); pPowerPointImport->getActualSlidePersist()->setClrMap(aModel.mpClrMap); } rtl::Reference 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(*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(std::move(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 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] = std::move(diagramRelTuple); ++counter; } xRelListTemp.realloc(counter); } } return xRelListTemp; } void Shape::cloneFillProperties() { auto pFillProperties = std::make_shared(); pFillProperties->assignUsed(*mpFillPropertiesPtr); mpFillPropertiesPtr = std::move(pFillProperties); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */