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