diff options
Diffstat (limited to 'xmloff/source/draw/shapeexport.cxx')
-rw-r--r-- | xmloff/source/draw/shapeexport.cxx | 5183 |
1 files changed, 5183 insertions, 0 deletions
diff --git a/xmloff/source/draw/shapeexport.cxx b/xmloff/source/draw/shapeexport.cxx new file mode 100644 index 0000000000..cbb0278297 --- /dev/null +++ b/xmloff/source/draw/shapeexport.cxx @@ -0,0 +1,5183 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_wasm_strip.h> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/matrix/b3dhommatrix.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b3dpolypolygon.hxx> +#include <basegfx/polygon/b3dpolypolygontools.hxx> +#include <basegfx/tuple/b2dtuple.hxx> +#include <basegfx/vector/b3dvector.hxx> + +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/beans/PropertyValues.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XIdentifierAccess.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/document/XEventsSupplier.hpp> +#include <com/sun/star/drawing/Alignment.hpp> +#include <com/sun/star/drawing/CameraGeometry.hpp> +#include <com/sun/star/drawing/CircleKind.hpp> +#include <com/sun/star/drawing/ConnectorType.hpp> +#include <com/sun/star/drawing/Direction3D.hpp> +#include <com/sun/star/drawing/EscapeDirection.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeGluePointType.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeMetalType.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeTextPathMode.hpp> +#include <com/sun/star/drawing/GluePoint2.hpp> +#include <com/sun/star/drawing/HomogenMatrix.hpp> +#include <com/sun/star/drawing/HomogenMatrix3.hpp> +#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp> +#include <com/sun/star/drawing/PolyPolygonShape3D.hpp> +#include <com/sun/star/drawing/Position3D.hpp> +#include <com/sun/star/drawing/ProjectionMode.hpp> +#include <com/sun/star/drawing/ShadeMode.hpp> +#include <com/sun/star/drawing/XControlShape.hpp> +#include <com/sun/star/drawing/XCustomShapeEngine.hpp> +#include <com/sun/star/drawing/XGluePointsSupplier.hpp> +#include <com/sun/star/drawing/BarCode.hpp> +#include <com/sun/star/drawing/BarCodeErrorCorrection.hpp> +#include <com/sun/star/drawing/XShapes3.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/io/XSeekableInputStream.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/lang/ServiceNotRegisteredException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/media/ZoomLevel.hpp> +#include <com/sun/star/presentation/AnimationSpeed.hpp> +#include <com/sun/star/presentation/ClickAction.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/table/XColumnRowRange.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include <com/sun/star/text/XText.hpp> + +#include <comphelper/classids.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/storagehelper.hxx> +#include <officecfg/Office/Common.hxx> + +#include <o3tl/any.hxx> +#include <o3tl/typed_flags_set.hxx> +#include <o3tl/string_view.hxx> + +#include <rtl/math.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> + +#include <sax/tools/converter.hxx> + +#include <tools/debug.hxx> +#include <tools/globname.hxx> +#include <tools/helpers.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/graph.hxx> + +#include <xmloff/contextid.hxx> +#include <xmloff/families.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/shapeexport.hxx> +#include <xmloff/unointerfacetouniqueidentifiermapper.hxx> +#include <xmloff/xmlexp.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/table/XMLTableExport.hxx> +#include <xmloff/ProgressBarHelper.hxx> + +#include <anim.hxx> +#include <EnhancedCustomShapeToken.hxx> +#include "sdpropls.hxx" +#include <xexptran.hxx> +#include "ximpshap.hxx" +#include <XMLBase64Export.hxx> +#include <XMLImageMapExport.hxx> +#include <memory> + +using namespace ::com::sun::star; +using namespace ::xmloff::EnhancedCustomShapeToken; +using namespace ::xmloff::token; + +constexpr OUStringLiteral XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE = u"vnd.sun.star.GraphicObject:"; + +namespace { + +bool supportsText(XmlShapeType eShapeType) +{ + return eShapeType != XmlShapeType::PresChartShape && + eShapeType != XmlShapeType::PresOLE2Shape && + eShapeType != XmlShapeType::DrawSheetShape && + eShapeType != XmlShapeType::PresSheetShape && + eShapeType != XmlShapeType::Draw3DSceneObject && + eShapeType != XmlShapeType::Draw3DCubeObject && + eShapeType != XmlShapeType::Draw3DSphereObject && + eShapeType != XmlShapeType::Draw3DLatheObject && + eShapeType != XmlShapeType::Draw3DExtrudeObject && + eShapeType != XmlShapeType::DrawPageShape && + eShapeType != XmlShapeType::PresPageShape && + eShapeType != XmlShapeType::DrawGroupShape; + +} + +} + +constexpr OUString gsZIndex( u"ZOrder"_ustr ); +constexpr OUStringLiteral gsPrintable( u"Printable" ); +constexpr OUStringLiteral gsVisible( u"Visible" ); +constexpr OUString gsModel( u"Model"_ustr ); +constexpr OUStringLiteral gsStartShape( u"StartShape" ); +constexpr OUStringLiteral gsEndShape( u"EndShape" ); +constexpr OUString gsOnClick( u"OnClick"_ustr ); +constexpr OUStringLiteral gsEventType( u"EventType" ); +constexpr OUStringLiteral gsPresentation( u"Presentation" ); +constexpr OUStringLiteral gsMacroName( u"MacroName" ); +constexpr OUString gsScript( u"Script"_ustr ); +constexpr OUStringLiteral gsLibrary( u"Library" ); +constexpr OUStringLiteral gsClickAction( u"ClickAction" ); +constexpr OUString gsBookmark( u"Bookmark"_ustr ); +constexpr OUStringLiteral gsEffect( u"Effect" ); +constexpr OUStringLiteral gsPlayFull( u"PlayFull" ); +constexpr OUStringLiteral gsVerb( u"Verb" ); +constexpr OUStringLiteral gsSoundURL( u"SoundURL" ); +constexpr OUStringLiteral gsSpeed( u"Speed" ); +constexpr OUStringLiteral gsStarBasic( u"StarBasic" ); +constexpr OUStringLiteral gsHyperlink( u"Hyperlink" ); + +XMLShapeExport::XMLShapeExport(SvXMLExport& rExp, + SvXMLExportPropertyMapper *pExtMapper ) +: mrExport( rExp ), + maCurrentShapesIter(maShapesInfos.end()), + mbExportLayer( false ), + // #88546# init to sal_False + mbHandleProgressBar( false ) +{ + // construct PropertySetMapper + mxPropertySetMapper = CreateShapePropMapper( mrExport ); + if( pExtMapper ) + { + rtl::Reference < SvXMLExportPropertyMapper > xExtMapper( pExtMapper ); + mxPropertySetMapper->ChainExportMapper( xExtMapper ); + } + +/* + // chain text attributes + xPropertySetMapper->ChainExportMapper(XMLTextParagraphExport::CreateParaExtPropMapper(rExp)); +*/ + + mrExport.GetAutoStylePool()->AddFamily( + XmlStyleFamily::SD_GRAPHICS_ID, + XML_STYLE_FAMILY_SD_GRAPHICS_NAME, + GetPropertySetMapper(), + XML_STYLE_FAMILY_SD_GRAPHICS_PREFIX); + mrExport.GetAutoStylePool()->AddFamily( + XmlStyleFamily::SD_PRESENTATION_ID, + XML_STYLE_FAMILY_SD_PRESENTATION_NAME, + GetPropertySetMapper(), + XML_STYLE_FAMILY_SD_PRESENTATION_PREFIX); + + // create table export helper and let him add his families in time + GetShapeTableExport(); +} + +XMLShapeExport::~XMLShapeExport() +{ +} + +// sj: replacing CustomShapes with standard objects that are also supported in OpenOffice.org format +uno::Reference< drawing::XShape > XMLShapeExport::checkForCustomShapeReplacement( const uno::Reference< drawing::XShape >& xShape ) +{ + uno::Reference< drawing::XShape > xCustomShapeReplacement; + + if( !( GetExport().getExportFlags() & SvXMLExportFlags::OASIS ) ) + { + OUString aType( xShape->getShapeType() ); + if( aType == "com.sun.star.drawing.CustomShape" ) + { + uno::Reference< beans::XPropertySet > xSet( xShape, uno::UNO_QUERY ); + if( xSet.is() ) + { + OUString aEngine; + xSet->getPropertyValue("CustomShapeEngine") >>= aEngine; + if ( aEngine.isEmpty() ) + { + aEngine = "com.sun.star.drawing.EnhancedCustomShapeEngine"; + } + uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + + if ( !aEngine.isEmpty() ) + { + uno::Sequence< beans::PropertyValue > aPropValues{ + comphelper::makePropertyValue("CustomShape", xShape), + comphelper::makePropertyValue("ForceGroupWithText", true) + }; + uno::Sequence< uno::Any > aArgument = { uno::Any(aPropValues) }; + uno::Reference< uno::XInterface > xInterface( + xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aEngine, aArgument, xContext) ); + if ( xInterface.is() ) + { + uno::Reference< drawing::XCustomShapeEngine > xCustomShapeEngine( + uno::Reference< drawing::XCustomShapeEngine >( xInterface, uno::UNO_QUERY ) ); + if ( xCustomShapeEngine.is() ) + xCustomShapeReplacement = xCustomShapeEngine->render(); + } + } + } + } + } + return xCustomShapeReplacement; +} + +// This method collects all automatic styles for the given XShape +void XMLShapeExport::collectShapeAutoStyles(const uno::Reference< drawing::XShape >& xShape ) +{ + if( maCurrentShapesIter == maShapesInfos.end() ) + { + OSL_FAIL( "XMLShapeExport::collectShapeAutoStyles(): no call to seekShapes()!" ); + return; + } + sal_Int32 nZIndex = 0; + uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if( xPropSet.is() ) + xPropSet->getPropertyValue(gsZIndex) >>= nZIndex; + + ImplXMLShapeExportInfoVector& aShapeInfoVector = (*maCurrentShapesIter).second; + + if( static_cast<sal_Int32>(aShapeInfoVector.size()) <= nZIndex ) + { + OSL_FAIL( "XMLShapeExport::collectShapeAutoStyles(): no shape info allocated for a given shape" ); + return; + } + + ImplXMLShapeExportInfo& aShapeInfo = aShapeInfoVector[nZIndex]; + + uno::Reference< drawing::XShape > xCustomShapeReplacement = checkForCustomShapeReplacement( xShape ); + if ( xCustomShapeReplacement.is() ) + aShapeInfo.xCustomShapeReplacement = xCustomShapeReplacement; + + // first compute the shapes type + ImpCalcShapeType(xShape, aShapeInfo.meShapeType); + + // #i118485# enabled XmlShapeType::DrawChartShape and XmlShapeType::DrawOLE2Shape + // to have text + const bool bObjSupportsText = + supportsText(aShapeInfo.meShapeType); + + const bool bObjSupportsStyle = + aShapeInfo.meShapeType != XmlShapeType::DrawGroupShape; + + bool bIsEmptyPresObj = false; + + if ( aShapeInfo.xCustomShapeReplacement.is() ) + xPropSet.clear(); + + // prep text styles + if( xPropSet.is() && bObjSupportsText ) + { + uno::Reference< text::XText > xText(xShape, uno::UNO_QUERY); + if (xText.is()) + { + try + { + // tdf#153161: it seems that the call to XTextRange::getString flushes the changes + // for some objects, that otherwise fail to get exported correctly. Maybe at some + // point it would make sense to find a better place for more targeted flush. + xText->getString(); + } + catch (uno::RuntimeException const&) + { + // E.g., SwXTextFrame that contains only a table will throw; this is not an error + } + + uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xPropSet->getPropertySetInfo() ); + + if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("IsEmptyPresentationObject") ) + { + uno::Any aAny = xPropSet->getPropertyValue("IsEmptyPresentationObject"); + aAny >>= bIsEmptyPresObj; + } + + if(!bIsEmptyPresObj) + { + GetExport().GetTextParagraphExport()->collectTextAutoStyles( xText ); + } + } + } + + // compute the shape parent style + if( xPropSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xPropertySetInfo( xPropSet->getPropertySetInfo() ); + + OUString aParentName; + uno::Reference< style::XStyle > xStyle; + + if( bObjSupportsStyle ) + { + if( xPropertySetInfo.is() && xPropertySetInfo->hasPropertyByName("Style") ) + xPropSet->getPropertyValue("Style") >>= xStyle; + + if(xStyle.is()) + { + // get family ID + uno::Reference< beans::XPropertySet > xStylePropSet(xStyle, uno::UNO_QUERY); + SAL_WARN_IF( !xStylePropSet.is(), "xmloff", "style without a XPropertySet?" ); + try + { + if(xStylePropSet.is()) + { + OUString aFamilyName; + xStylePropSet->getPropertyValue("Family") >>= aFamilyName; + if( !aFamilyName.isEmpty() && aFamilyName != "graphics" ) + aShapeInfo.mnFamily = XmlStyleFamily::SD_PRESENTATION_ID; + } + } + catch(const beans::UnknownPropertyException&) + { + // Ignored. + SAL_WARN( "xmloff", + "XMLShapeExport::collectShapeAutoStyles: style has no 'Family' property"); + } + + // get parent-style name + if(XmlStyleFamily::SD_PRESENTATION_ID == aShapeInfo.mnFamily) + { + aParentName = msPresentationStylePrefix; + } + + aParentName += xStyle->getName(); + } + } + + if (aParentName.isEmpty() && xPropertySetInfo->hasPropertyByName("TextBox") && xPropSet->getPropertyValue("TextBox").hasValue() && xPropSet->getPropertyValue("TextBox").get<bool>()) + { + // Shapes with a Writer TextBox always have a parent style. + // If there would be none, then assign the default one. + aParentName = "Frame"; + } + + // filter propset + std::vector< XMLPropertyState > aPropStates; + + sal_Int32 nCount = 0; + if( !bIsEmptyPresObj || (aShapeInfo.meShapeType != XmlShapeType::PresPageShape) ) + { + aPropStates = GetPropertySetMapper()->Filter(mrExport, xPropSet); + + if (XmlShapeType::DrawControlShape == aShapeInfo.meShapeType) + { + // for control shapes, we additionally need the number format style (if any) + uno::Reference< drawing::XControlShape > xControl(xShape, uno::UNO_QUERY); + DBG_ASSERT(xControl.is(), "XMLShapeExport::collectShapeAutoStyles: ShapeType control, but no XControlShape!"); + if (xControl.is()) + { + uno::Reference< beans::XPropertySet > xControlModel(xControl->getControl(), uno::UNO_QUERY); + DBG_ASSERT(xControlModel.is(), "XMLShapeExport::collectShapeAutoStyles: no control model on the control shape!"); + + OUString sNumberStyle = mrExport.GetFormExport()->getControlNumberStyle(xControlModel); + if (!sNumberStyle.isEmpty()) + { + sal_Int32 nIndex = GetPropertySetMapper()->getPropertySetMapper()->FindEntryIndex(CTF_SD_CONTROL_SHAPE_DATA_STYLE); + // TODO : this retrieval of the index could be moved into the ctor, holding the index + // as member, thus saving time. + DBG_ASSERT(-1 != nIndex, "XMLShapeExport::collectShapeAutoStyles: could not obtain the index for our context id!"); + + XMLPropertyState aNewState(nIndex, uno::Any(sNumberStyle)); + aPropStates.push_back(aNewState); + } + } + } + + nCount = std::count_if(aPropStates.cbegin(), aPropStates.cend(), + [](const XMLPropertyState& rProp) { return rProp.mnIndex != -1; }); + } + + if(nCount == 0) + { + // no hard attributes, use parent style name for export + aShapeInfo.msStyleName = aParentName; + } + else + { + // there are filtered properties -> hard attributes + // try to find this style in AutoStylePool + aShapeInfo.msStyleName = mrExport.GetAutoStylePool()->Find(aShapeInfo.mnFamily, aParentName, aPropStates); + + if(aShapeInfo.msStyleName.isEmpty()) + { + // Style did not exist, add it to AutoStalePool + aShapeInfo.msStyleName = mrExport.GetAutoStylePool()->Add(aShapeInfo.mnFamily, aParentName, std::move(aPropStates)); + } + } + + // optionally generate auto style for text attributes + if( (!bIsEmptyPresObj || (aShapeInfo.meShapeType != XmlShapeType::PresPageShape)) && bObjSupportsText ) + { + aPropStates = GetExport().GetTextParagraphExport()->GetParagraphPropertyMapper()->Filter(mrExport, xPropSet); + + // yet more additionally, we need to care for the ParaAdjust property + if ( XmlShapeType::DrawControlShape == aShapeInfo.meShapeType ) + { + uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xPropSet->getPropertySetInfo() ); + uno::Reference< beans::XPropertyState > xPropState( xPropSet, uno::UNO_QUERY ); + if ( xPropSetInfo.is() && xPropState.is() ) + { + // this is because: + // * if controls shapes have a ParaAdjust property, then this is the Align property of the control model + // * control models are allowed to have an Align of "void" + // * the Default for control model's Align is TextAlign_LEFT + // * defaults for style properties are not written, but we need to write the "left", + // because we need to distinguish this "left" from the case where not align attribute + // is present which means "void" + if ( xPropSetInfo->hasPropertyByName( "ParaAdjust" ) + && ( beans::PropertyState_DEFAULT_VALUE == xPropState->getPropertyState( "ParaAdjust" ) ) + ) + { + sal_Int32 nIndex = GetExport().GetTextParagraphExport()->GetParagraphPropertyMapper()->getPropertySetMapper()->FindEntryIndex( CTF_SD_SHAPE_PARA_ADJUST ); + // TODO : this retrieval of the index should be moved into the ctor, holding the index + // as member, thus saving time. + DBG_ASSERT(-1 != nIndex, "XMLShapeExport::collectShapeAutoStyles: could not obtain the index for the ParaAdjust context id!"); + + uno::Any aParaAdjustValue = xPropSet->getPropertyValue( "ParaAdjust" ); + XMLPropertyState aAlignDefaultState( nIndex, aParaAdjustValue ); + + aPropStates.push_back( aAlignDefaultState ); + } + } + } + + nCount = std::count_if(aPropStates.cbegin(), aPropStates.cend(), + [](const XMLPropertyState& rProp) { return rProp.mnIndex != -1; }); + + if( nCount ) + { + aShapeInfo.msTextStyleName = mrExport.GetAutoStylePool()->Find( XmlStyleFamily::TEXT_PARAGRAPH, "", aPropStates ); + if(aShapeInfo.msTextStyleName.isEmpty()) + { + // Style did not exist, add it to AutoStalePool + aShapeInfo.msTextStyleName = mrExport.GetAutoStylePool()->Add(XmlStyleFamily::TEXT_PARAGRAPH, "", std::move(aPropStates)); + } + } + } + } + + // prepare animation information if needed + if( mxAnimationsExporter.is() ) + XMLAnimationsExporter::prepare( xShape ); + + // check for special shapes + + switch( aShapeInfo.meShapeType ) + { + case XmlShapeType::DrawConnectorShape: + { + uno::Reference< uno::XInterface > xConnection; + + // create shape ids for export later + xPropSet->getPropertyValue( gsStartShape ) >>= xConnection; + if( xConnection.is() ) + mrExport.getInterfaceToIdentifierMapper().registerReference( xConnection ); + + xPropSet->getPropertyValue( gsEndShape ) >>= xConnection; + if( xConnection.is() ) + mrExport.getInterfaceToIdentifierMapper().registerReference( xConnection ); + break; + } + case XmlShapeType::PresTableShape: + case XmlShapeType::DrawTableShape: + { + try + { + uno::Reference< table::XColumnRowRange > xRange( xPropSet->getPropertyValue( gsModel ), uno::UNO_QUERY_THROW ); + GetShapeTableExport()->collectTableAutoStyles( xRange ); + } + catch(const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION( "xmloff", "collecting auto styles for a table" ); + } + break; + } + default: + break; + } + + // check for shape collections (group shape or 3d scene) + // and collect contained shapes style infos + const uno::Reference< drawing::XShape >& xCollection = aShapeInfo.xCustomShapeReplacement.is() + ? aShapeInfo.xCustomShapeReplacement : xShape; + { + uno::Reference< drawing::XShapes > xShapes( xCollection, uno::UNO_QUERY ); + if( xShapes.is() ) + { + collectShapesAutoStyles( xShapes ); + } + } +} + +namespace +{ + class NewTextListsHelper + { + public: + explicit NewTextListsHelper( SvXMLExport& rExp ) + : mrExport( rExp ) + { + mrExport.GetTextParagraphExport()->PushNewTextListsHelper(); + } + + ~NewTextListsHelper() + { + mrExport.GetTextParagraphExport()->PopTextListsHelper(); + } + + private: + SvXMLExport& mrExport; + }; +} +// This method exports the given XShape +void XMLShapeExport::exportShape(const uno::Reference< drawing::XShape >& xShape, + XMLShapeExportFlags nFeatures /* = SEF_DEFAULT */, + css::awt::Point* pRefPoint /* = NULL */, + comphelper::AttributeList* pAttrList /* = NULL */ ) +{ + SAL_INFO("xmloff", xShape->getShapeType()); + if( maCurrentShapesIter == maShapesInfos.end() ) + { + SAL_WARN( "xmloff", "XMLShapeExport::exportShape(): no auto styles where collected before export" ); + return; + } + sal_Int32 nZIndex = 0; + uno::Reference< beans::XPropertySet > xSet( xShape, uno::UNO_QUERY ); + OUString sHyperlink; + try + { + xSet->getPropertyValue(gsHyperlink) >>= sHyperlink; + } + catch (beans::UnknownPropertyException) + { + } + + std::unique_ptr< SvXMLElementExport > pHyperlinkElement; + + // Need to stash the attributes that are pre-loaded for the shape export + // (otherwise they will become attributes of the draw:a element) + uno::Reference<xml::sax::XAttributeList> xSaveAttribs( + new comphelper::AttributeList(GetExport().GetAttrList())); + GetExport().ClearAttrList(); + if( xSet.is() && (GetExport().GetModelType() == SvtModuleOptions::EFactory::DRAW) ) + { + // export hyperlinks with <a><shape/></a>. Currently only in draw since draw + // does not support document events + try + { + presentation::ClickAction eAction = presentation::ClickAction_NONE; + xSet->getPropertyValue(gsOnClick) >>= eAction; + + if( (eAction == presentation::ClickAction_DOCUMENT) || + (eAction == presentation::ClickAction_BOOKMARK) ) + { + OUString sURL; + xSet->getPropertyValue(gsBookmark) >>= sURL; + + if( !sURL.isEmpty() ) + { + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sURL ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED ); + pHyperlinkElement.reset( new SvXMLElementExport(mrExport, XML_NAMESPACE_DRAW, XML_A, true, true) ); + } + } + } + catch(const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff", "XMLShapeExport::exportShape(): exception during hyperlink export"); + } + } + else if (xSet.is() && !sHyperlink.isEmpty()) + { + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sHyperlink ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + pHyperlinkElement.reset( new SvXMLElementExport(mrExport, XML_NAMESPACE_DRAW, XML_A, true, true) ); + } + // re-add stashed attributes + GetExport().AddAttributeList(xSaveAttribs); + + if( xSet.is() ) + xSet->getPropertyValue(gsZIndex) >>= nZIndex; + + ImplXMLShapeExportInfoVector& aShapeInfoVector = (*maCurrentShapesIter).second; + + if( static_cast<sal_Int32>(aShapeInfoVector.size()) <= nZIndex ) + { + SAL_WARN( "xmloff", "XMLShapeExport::exportShape(): no shape info collected for a given shape" ); + return; + } + + NewTextListsHelper aNewTextListsHelper( mrExport ); + + const ImplXMLShapeExportInfo& aShapeInfo = aShapeInfoVector[nZIndex]; + +#ifdef DBG_UTIL + // check if this is the correct ShapesInfo + uno::Reference< container::XChild > xChild( xShape, uno::UNO_QUERY ); + if( xChild.is() ) + { + uno::Reference< drawing::XShapes > xParent( xChild->getParent(), uno::UNO_QUERY ); + SAL_WARN_IF( !xParent.is() && xParent.get() == (*maCurrentShapesIter).first.get(), "xmloff", "XMLShapeExport::exportShape(): Wrong call to XMLShapeExport::seekShapes()" ); + } + + // first compute the shapes type + { + XmlShapeType eShapeType(XmlShapeType::NotYetSet); + ImpCalcShapeType(xShape, eShapeType); + + SAL_WARN_IF( eShapeType != aShapeInfo.meShapeType, "xmloff", "exportShape callings do not correspond to collectShapeAutoStyles calls!: " << xShape->getShapeType() ); + } +#endif + + // collect animation information if needed + if( mxAnimationsExporter.is() ) + mxAnimationsExporter->collect( xShape, mrExport ); + + /* Export shapes name if he has one (#i51726#) + Export of the shape name for text documents only if the OpenDocument + file format is written - exceptions are group shapes. + Note: Writer documents in OpenOffice.org file format doesn't contain + any names for shapes, except for group shapes. + */ + { + if ( ( GetExport().GetModelType() != SvtModuleOptions::EFactory::WRITER && + GetExport().GetModelType() != SvtModuleOptions::EFactory::WRITERWEB && + GetExport().GetModelType() != SvtModuleOptions::EFactory::WRITERGLOBAL ) || + ( GetExport().getExportFlags() & SvXMLExportFlags::OASIS ) || + aShapeInfo.meShapeType == XmlShapeType::DrawGroupShape || + ( aShapeInfo.meShapeType == XmlShapeType::DrawCustomShape && + aShapeInfo.xCustomShapeReplacement.is() ) ) + { + uno::Reference< container::XNamed > xNamed( xShape, uno::UNO_QUERY ); + if( xNamed.is() ) + { + const OUString aName( xNamed->getName() ); + if( !aName.isEmpty() ) + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_NAME, aName ); + } + } + } + + // export style name + if( !aShapeInfo.msStyleName.isEmpty() ) + { + if(XmlStyleFamily::SD_GRAPHICS_ID == aShapeInfo.mnFamily) + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_STYLE_NAME, mrExport.EncodeStyleName( aShapeInfo.msStyleName) ); + else + mrExport.AddAttribute(XML_NAMESPACE_PRESENTATION, XML_STYLE_NAME, mrExport.EncodeStyleName( aShapeInfo.msStyleName) ); + } + + // export text style name + if( !aShapeInfo.msTextStyleName.isEmpty() ) + { + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_TEXT_STYLE_NAME, aShapeInfo.msTextStyleName ); + } + + // export shapes id if needed + { + uno::Reference< uno::XInterface > xRef( xShape, uno::UNO_QUERY ); + const OUString& rShapeId = mrExport.getInterfaceToIdentifierMapper().getIdentifier( xRef ); + if( !rShapeId.isEmpty() ) + { + mrExport.AddAttributeIdLegacy(XML_NAMESPACE_DRAW, rShapeId); + } + } + + // export layer information + if( mbExportLayer ) + { + // check for group or scene shape and not export layer if this is one + uno::Reference< drawing::XShapes > xShapes( xShape, uno::UNO_QUERY ); + if( !xShapes.is() ) + { + try + { + uno::Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + OUString aLayerName; + xProps->getPropertyValue("LayerName") >>= aLayerName; + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_LAYER, aLayerName ); + + } + catch(const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION( "xmloff", "exporting layer name for shape" ); + } + } + } + + // export draw:display (do not export in ODF 1.3 or older) + if (xSet.is() && (mrExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)) + { + if( aShapeInfo.meShapeType != XmlShapeType::DrawPageShape && aShapeInfo.meShapeType != XmlShapeType::PresPageShape && + aShapeInfo.meShapeType != XmlShapeType::HandoutShape && aShapeInfo.meShapeType != XmlShapeType::DrawChartShape ) + try + { + bool bVisible = true; + bool bPrintable = true; + + xSet->getPropertyValue(gsVisible) >>= bVisible; + xSet->getPropertyValue(gsPrintable) >>= bPrintable; + + XMLTokenEnum eDisplayToken = XML_TOKEN_INVALID; + const unsigned short nDisplay = (bVisible ? 2 : 0) | (bPrintable ? 1 : 0); + switch( nDisplay ) + { + case 0: eDisplayToken = XML_NONE; break; + case 1: eDisplayToken = XML_PRINTER; break; + case 2: eDisplayToken = XML_SCREEN; break; + // case 3: eDisplayToken = XML_ALWAYS break; this is the default + } + + if( eDisplayToken != XML_TOKEN_INVALID ) + mrExport.AddAttribute(XML_NAMESPACE_DRAW_EXT, XML_DISPLAY, eDisplayToken ); + } + catch(const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("xmloff.draw"); + } + } + + // #82003# test export count + // #91587# ALWAYS increment since now ALL to be exported shapes are counted. + if(mrExport.GetShapeExport()->IsHandleProgressBarEnabled()) + { + mrExport.GetProgressBarHelper()->Increment(); + } + + onExport( xShape ); + + // export shape element + switch(aShapeInfo.meShapeType) + { + case XmlShapeType::DrawRectangleShape: + { + ImpExportRectangleShape(xShape, nFeatures, pRefPoint ); + break; + } + case XmlShapeType::DrawEllipseShape: + { + ImpExportEllipseShape(xShape, nFeatures, pRefPoint ); + break; + } + case XmlShapeType::DrawLineShape: + { + ImpExportLineShape(xShape, nFeatures, pRefPoint ); + break; + } + case XmlShapeType::DrawPolyPolygonShape: // closed PolyPolygon + case XmlShapeType::DrawPolyLineShape: // open PolyPolygon + case XmlShapeType::DrawClosedBezierShape: // closed tools::PolyPolygon containing curves + case XmlShapeType::DrawOpenBezierShape: // open tools::PolyPolygon containing curves + { + ImpExportPolygonShape(xShape, aShapeInfo.meShapeType, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawTextShape: + case XmlShapeType::PresTitleTextShape: + case XmlShapeType::PresOutlinerShape: + case XmlShapeType::PresSubtitleShape: + case XmlShapeType::PresNotesShape: + case XmlShapeType::PresHeaderShape: + case XmlShapeType::PresFooterShape: + case XmlShapeType::PresSlideNumberShape: + case XmlShapeType::PresDateTimeShape: + { + ImpExportTextBoxShape(xShape, aShapeInfo.meShapeType, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawGraphicObjectShape: + case XmlShapeType::PresGraphicObjectShape: + { + ImpExportGraphicObjectShape(xShape, aShapeInfo.meShapeType, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawChartShape: + case XmlShapeType::PresChartShape: + { + ImpExportChartShape(xShape, aShapeInfo.meShapeType, nFeatures, pRefPoint, pAttrList ); + break; + } + + case XmlShapeType::DrawControlShape: + { + ImpExportControlShape(xShape, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawConnectorShape: + { + ImpExportConnectorShape(xShape, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawMeasureShape: + { + ImpExportMeasureShape(xShape, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawOLE2Shape: + case XmlShapeType::PresOLE2Shape: + case XmlShapeType::DrawSheetShape: + case XmlShapeType::PresSheetShape: + { + ImpExportOLE2Shape(xShape, aShapeInfo.meShapeType, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::PresTableShape: + case XmlShapeType::DrawTableShape: + { + ImpExportTableShape( xShape, aShapeInfo.meShapeType, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawPageShape: + case XmlShapeType::PresPageShape: + case XmlShapeType::HandoutShape: + { + ImpExportPageShape(xShape, aShapeInfo.meShapeType, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawCaptionShape: + { + ImpExportCaptionShape(xShape, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::Draw3DCubeObject: + case XmlShapeType::Draw3DSphereObject: + case XmlShapeType::Draw3DLatheObject: + case XmlShapeType::Draw3DExtrudeObject: + { + ImpExport3DShape(xShape, aShapeInfo.meShapeType); + break; + } + + case XmlShapeType::Draw3DSceneObject: + { + ImpExport3DSceneShape( xShape, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawGroupShape: + { + // empty group + ImpExportGroupShape( xShape, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawFrameShape: + { + ImpExportFrameShape(xShape, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawAppletShape: + { + ImpExportAppletShape(xShape, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawPluginShape: + { + ImpExportPluginShape(xShape, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::DrawCustomShape: + { + if ( aShapeInfo.xCustomShapeReplacement.is() ) + ImpExportGroupShape( aShapeInfo.xCustomShapeReplacement, nFeatures, pRefPoint ); + else + ImpExportCustomShape( xShape, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::PresMediaShape: + case XmlShapeType::DrawMediaShape: + { + ImpExportMediaShape( xShape, aShapeInfo.meShapeType, nFeatures, pRefPoint ); + break; + } + + case XmlShapeType::PresOrgChartShape: + case XmlShapeType::Unknown: + case XmlShapeType::NotYetSet: + default: + { + // this should never happen and is an error + OSL_FAIL("XMLEXP: WriteShape: unknown or unexpected type of shape in export!"); + break; + } + } + + pHyperlinkElement.reset(); + + // #97489# #97111# + // if there was an error and no element for the shape was exported + // we need to clear the attribute list or the attributes will be + // set on the next exported element, which can result in corrupt + // xml files due to duplicate attributes + + mrExport.CheckAttrList(); // asserts in non pro if we have attributes left + mrExport.ClearAttrList(); // clears the attributes +} + +// This method collects all automatic styles for the shapes inside the given XShapes collection +void XMLShapeExport::collectShapesAutoStyles( const uno::Reference < drawing::XShapes >& xShapes ) +{ + ShapesInfos::iterator aOldCurrentShapesIter = maCurrentShapesIter; + seekShapes( xShapes ); + + uno::Reference< drawing::XShape > xShape; + const sal_Int32 nShapeCount(xShapes->getCount()); + for(sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++) + { + xShapes->getByIndex(nShapeId) >>= xShape; + SAL_WARN_IF( !xShape.is(), "xmloff", "Shape without a XShape?" ); + if(!xShape.is()) + continue; + + collectShapeAutoStyles( xShape ); + } + + maCurrentShapesIter = aOldCurrentShapesIter; +} + +// This method exports all XShape inside the given XShapes collection +void XMLShapeExport::exportShapes( const uno::Reference < drawing::XShapes >& xShapes, XMLShapeExportFlags nFeatures /* = SEF_DEFAULT */, awt::Point* pRefPoint /* = NULL */ ) +{ + ShapesInfos::iterator aOldCurrentShapesIter = maCurrentShapesIter; + seekShapes( xShapes ); + + uno::Reference< drawing::XShape > xShape; + const sal_Int32 nShapeCount(xShapes->getCount()); + for(sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++) + { + xShapes->getByIndex(nShapeId) >>= xShape; + SAL_WARN_IF( !xShape.is(), "xmloff", "Shape without a XShape?" ); + if(!xShape.is()) + continue; + + exportShape( xShape, nFeatures, pRefPoint ); + } + + maCurrentShapesIter = aOldCurrentShapesIter; +} + +namespace xmloff { + +void FixZOrder(uno::Reference<drawing::XShapes> const& xShapes, + std::function<unsigned int (uno::Reference<beans::XPropertySet> const&)> const& rGetLayer) +{ + uno::Reference<drawing::XShapes3> const xShapes3(xShapes, uno::UNO_QUERY); + assert(xShapes3.is()); + if (!xShapes3.is()) + { + return; // only SvxDrawPage implements this + } + struct Layer { std::vector<sal_Int32> shapes; sal_Int32 nMin = SAL_MAX_INT32; sal_Int32 nMax = 0; }; + std::vector<Layer> layers; + // shapes are sorted by ZOrder + sal_Int32 const nCount(xShapes->getCount()); + for (sal_Int32 i = 0; i < nCount; ++i) + { + uno::Reference<beans::XPropertySet> const xShape(xShapes->getByIndex(i), uno::UNO_QUERY); + if (!xShape.is()) + { + SAL_WARN("xmloff", "FixZOrder: null shape, cannot sort"); + return; + } + unsigned int const nLayer(rGetLayer(xShape)); + if (layers.size() <= nLayer) + { + layers.resize(nLayer + 1); + } + layers[nLayer].shapes.emplace_back(i); + if (i < layers[nLayer].nMin) + { + layers[nLayer].nMin = i; + } + if (layers[nLayer].nMax < i) + { + layers[nLayer].nMax = i; + } + } + std::erase_if(layers, [](Layer const& rLayer) { return rLayer.shapes.empty(); }); + bool isSorted(true); + for (size_t i = 1; i < layers.size(); ++i) + { + assert(layers[i].nMin != layers[i-1].nMax); // unique! + if (layers[i].nMin < layers[i-1].nMax) + { + isSorted = false; + break; + } + } + if (isSorted) + { + return; // nothing to do + } + uno::Sequence<sal_Int32> aNewOrder(nCount); + auto iterInsert(aNewOrder.getArray()); + for (auto const& rLayer : layers) + { + assert(rLayer.nMin <= rLayer.nMax); // empty layers have been removed + iterInsert = std::copy(rLayer.shapes.begin(), rLayer.shapes.end(), iterInsert); + } + try + { + xShapes3->sort(aNewOrder); + } + catch (uno::Exception const&) + { + SAL_WARN("xmloff", "FixZOrder: exception"); + } +} + +} // namespace xmloff + +void XMLShapeExport::seekShapes( const uno::Reference< drawing::XShapes >& xShapes ) noexcept +{ + if( xShapes.is() ) + { + maCurrentShapesIter = maShapesInfos.find( xShapes ); + if( maCurrentShapesIter == maShapesInfos.end() ) + { + auto itPair = maShapesInfos.emplace( xShapes, ImplXMLShapeExportInfoVector( static_cast<ShapesInfos::size_type>(xShapes->getCount()) ) ); + + maCurrentShapesIter = itPair.first; + + SAL_WARN_IF( maCurrentShapesIter == maShapesInfos.end(), "xmloff", "XMLShapeExport::seekShapes(): insert into stl::map failed" ); + } + + SAL_WARN_IF( (*maCurrentShapesIter).second.size() != static_cast<ShapesInfos::size_type>(xShapes->getCount()), "xmloff", "XMLShapeExport::seekShapes(): XShapes size varied between calls" ); + + } + else + { + maCurrentShapesIter = maShapesInfos.end(); + } +} + +void XMLShapeExport::exportAutoStyles() +{ + // export all autostyle infos + + // ...for graphic + { + GetExport().GetAutoStylePool()->exportXML( XmlStyleFamily::SD_GRAPHICS_ID ); + } + + // ...for presentation + { + GetExport().GetAutoStylePool()->exportXML( XmlStyleFamily::SD_PRESENTATION_ID ); + } + + if( mxShapeTableExport.is() ) + mxShapeTableExport->exportAutoStyles(); +} + +/// returns the export property mapper for external chaining +SvXMLExportPropertyMapper* XMLShapeExport::CreateShapePropMapper( + SvXMLExport& rExport ) +{ + rtl::Reference< XMLPropertyHandlerFactory > xFactory = new XMLSdPropHdlFactory( rExport.GetModel(), rExport ); + rtl::Reference < XMLPropertySetMapper > xMapper = new XMLShapePropertySetMapper( xFactory, true ); + rExport.GetTextParagraphExport(); // get or create text paragraph export + SvXMLExportPropertyMapper* pResult = + new XMLShapeExportPropertyMapper( xMapper, rExport ); + // chain text attributes + return pResult; +} + +void XMLShapeExport::ImpCalcShapeType(const uno::Reference< drawing::XShape >& xShape, + XmlShapeType& eShapeType) +{ + // set in every case, so init here + eShapeType = XmlShapeType::Unknown; + + if(!xShape.is()) + return; + + OUString aType(xShape->getShapeType()); + + if(!aType.match("com.sun.star.")) + return; + + if(aType.match("drawing.", 13)) + { + // drawing shapes + if (aType.match("Rectangle", 21)) { eShapeType = XmlShapeType::DrawRectangleShape; } + + // #i72177# Note: Correcting CustomShape, CustomShape->Custom, len from 9 (was wrong anyways) to 6. + // As can be seen at the other compares, the appendix "Shape" is left out of the comparison. + else if(aType.match("Custom", 21)) { eShapeType = XmlShapeType::DrawCustomShape; } + + else if(aType.match("Ellipse", 21)) { eShapeType = XmlShapeType::DrawEllipseShape; } + else if(aType.match("Control", 21)) { eShapeType = XmlShapeType::DrawControlShape; } + else if(aType.match("Connector", 21)) { eShapeType = XmlShapeType::DrawConnectorShape; } + else if(aType.match("Measure", 21)) { eShapeType = XmlShapeType::DrawMeasureShape; } + else if(aType.match("Line", 21)) { eShapeType = XmlShapeType::DrawLineShape; } + + // #i72177# Note: This covers two types by purpose, PolyPolygonShape and PolyPolygonPathShape + else if(aType.match("PolyPolygon", 21)) { eShapeType = XmlShapeType::DrawPolyPolygonShape; } + + // #i72177# Note: This covers two types by purpose, PolyLineShape and PolyLinePathShape + else if(aType.match("PolyLine", 21)) { eShapeType = XmlShapeType::DrawPolyLineShape; } + + else if(aType.match("OpenBezier", 21)) { eShapeType = XmlShapeType::DrawOpenBezierShape; } + else if(aType.match("ClosedBezier", 21)) { eShapeType = XmlShapeType::DrawClosedBezierShape; } + + // #i72177# FreeHand (opened and closed) now supports the types OpenFreeHandShape and + // ClosedFreeHandShape respectively. Represent them as bezier shapes + else if(aType.match("OpenFreeHand", 21)) { eShapeType = XmlShapeType::DrawOpenBezierShape; } + else if(aType.match("ClosedFreeHand", 21)) { eShapeType = XmlShapeType::DrawClosedBezierShape; } + + else if(aType.match("GraphicObject", 21)) { eShapeType = XmlShapeType::DrawGraphicObjectShape; } + else if(aType.match("Group", 21)) { eShapeType = XmlShapeType::DrawGroupShape; } + else if(aType.match("Text", 21)) { eShapeType = XmlShapeType::DrawTextShape; } + else if(aType.match("OLE2", 21)) + { + eShapeType = XmlShapeType::DrawOLE2Shape; + + // get info about presentation shape + uno::Reference <beans::XPropertySet> xPropSet(xShape, uno::UNO_QUERY); + + if(xPropSet.is()) + { + OUString sCLSID; + if(xPropSet->getPropertyValue("CLSID") >>= sCLSID) + { +#if !ENABLE_WASM_STRIP_CHART + // WASM_CHART change + // TODO: With Chart extracted this cannot really happen since + // no Chart could've been added at all + if (sCLSID == mrExport.GetChartExport()->getChartCLSID() || +#else + if( +#endif + sCLSID == SvGlobalName( SO3_RPTCH_CLASSID ).GetHexName() ) + { + eShapeType = XmlShapeType::DrawChartShape; + } + else if (sCLSID == SvGlobalName( SO3_SC_CLASSID ).GetHexName() ) + { + eShapeType = XmlShapeType::DrawSheetShape; + } + else + { + // general OLE2 Object + } + } + } + } + else if(aType.match("Page", 21)) { eShapeType = XmlShapeType::DrawPageShape; } + else if(aType.match("Frame", 21)) { eShapeType = XmlShapeType::DrawFrameShape; } + else if(aType.match("Caption", 21)) { eShapeType = XmlShapeType::DrawCaptionShape; } + else if(aType.match("Plugin", 21)) { eShapeType = XmlShapeType::DrawPluginShape; } + else if(aType.match("Applet", 21)) { eShapeType = XmlShapeType::DrawAppletShape; } + else if(aType.match("MediaShape", 21)) { eShapeType = XmlShapeType::DrawMediaShape; } + else if(aType.match("TableShape", 21)) { eShapeType = XmlShapeType::DrawTableShape; } + + // 3D shapes + else if(aType.match("Scene", 21 + 7)) { eShapeType = XmlShapeType::Draw3DSceneObject; } + else if(aType.match("Cube", 21 + 7)) { eShapeType = XmlShapeType::Draw3DCubeObject; } + else if(aType.match("Sphere", 21 + 7)) { eShapeType = XmlShapeType::Draw3DSphereObject; } + else if(aType.match("Lathe", 21 + 7)) { eShapeType = XmlShapeType::Draw3DLatheObject; } + else if(aType.match("Extrude", 21 + 7)) { eShapeType = XmlShapeType::Draw3DExtrudeObject; } + } + else if(aType.match("presentation.", 13)) + { + // presentation shapes + if (aType.match("TitleText", 26)) { eShapeType = XmlShapeType::PresTitleTextShape; } + else if(aType.match("Outliner", 26)) { eShapeType = XmlShapeType::PresOutlinerShape; } + else if(aType.match("Subtitle", 26)) { eShapeType = XmlShapeType::PresSubtitleShape; } + else if(aType.match("GraphicObject", 26)) { eShapeType = XmlShapeType::PresGraphicObjectShape; } + else if(aType.match("Page", 26)) { eShapeType = XmlShapeType::PresPageShape; } + else if(aType.match("OLE2", 26)) + { + eShapeType = XmlShapeType::PresOLE2Shape; + + // get info about presentation shape + uno::Reference <beans::XPropertySet> xPropSet(xShape, uno::UNO_QUERY); + + if(xPropSet.is()) try + { + OUString sCLSID; + if(xPropSet->getPropertyValue("CLSID") >>= sCLSID) + { + if( sCLSID == SvGlobalName( SO3_SC_CLASSID ).GetHexName() ) + { + eShapeType = XmlShapeType::PresSheetShape; + } + } + } + catch(const uno::Exception&) + { + SAL_WARN( "xmloff", "XMLShapeExport::ImpCalcShapeType(), expected ole shape to have the CLSID property?" ); + } + } + else if(aType.match("Chart", 26)) { eShapeType = XmlShapeType::PresChartShape; } + else if(aType.match("OrgChart", 26)) { eShapeType = XmlShapeType::PresOrgChartShape; } + else if(aType.match("CalcShape", 26)) { eShapeType = XmlShapeType::PresSheetShape; } + else if(aType.match("TableShape", 26)) { eShapeType = XmlShapeType::PresTableShape; } + else if(aType.match("Notes", 26)) { eShapeType = XmlShapeType::PresNotesShape; } + else if(aType.match("HandoutShape", 26)) { eShapeType = XmlShapeType::HandoutShape; } + else if(aType.match("HeaderShape", 26)) { eShapeType = XmlShapeType::PresHeaderShape; } + else if(aType.match("FooterShape", 26)) { eShapeType = XmlShapeType::PresFooterShape; } + else if(aType.match("SlideNumberShape", 26)) { eShapeType = XmlShapeType::PresSlideNumberShape; } + else if(aType.match("DateTimeShape", 26)) { eShapeType = XmlShapeType::PresDateTimeShape; } + else if(aType.match("MediaShape", 26)) { eShapeType = XmlShapeType::PresMediaShape; } + } +} + +/** exports all user defined gluepoints */ +void XMLShapeExport::ImpExportGluePoints( const uno::Reference< drawing::XShape >& xShape ) +{ + uno::Reference< drawing::XGluePointsSupplier > xSupplier( xShape, uno::UNO_QUERY ); + if( !xSupplier.is() ) + return; + + uno::Reference< container::XIdentifierAccess > xGluePoints( xSupplier->getGluePoints(), uno::UNO_QUERY ); + if( !xGluePoints.is() ) + return; + + drawing::GluePoint2 aGluePoint; + + const uno::Sequence< sal_Int32 > aIdSequence( xGluePoints->getIdentifiers() ); + + for( const sal_Int32 nIdentifier : aIdSequence ) + { + if( (xGluePoints->getByIdentifier( nIdentifier ) >>= aGluePoint) && aGluePoint.IsUserDefined ) + { + // export only user defined gluepoints + + const OUString sId( OUString::number( nIdentifier ) ); + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_ID, sId ); + + mrExport.GetMM100UnitConverter().convertMeasureToXML(msBuffer, + aGluePoint.Position.X); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_X, msBuffer.makeStringAndClear()); + + mrExport.GetMM100UnitConverter().convertMeasureToXML(msBuffer, + aGluePoint.Position.Y); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_Y, msBuffer.makeStringAndClear()); + + if( !aGluePoint.IsRelative ) + { + SvXMLUnitConverter::convertEnum( msBuffer, aGluePoint.PositionAlignment, aXML_GlueAlignment_EnumMap ); + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_ALIGN, msBuffer.makeStringAndClear() ); + } + + if( aGluePoint.Escape != drawing::EscapeDirection_SMART ) + { + SvXMLUnitConverter::convertEnum( msBuffer, aGluePoint.Escape, aXML_GlueEscapeDirection_EnumMap ); + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_ESCAPE_DIRECTION, msBuffer.makeStringAndClear() ); + } + + SvXMLElementExport aEventsElemt(mrExport, XML_NAMESPACE_DRAW, XML_GLUE_POINT, true, true); + } + } +} + +void XMLShapeExport::ImpExportSignatureLine(const uno::Reference<drawing::XShape>& xShape) +{ + uno::Reference<beans::XPropertySet> xPropSet(xShape, uno::UNO_QUERY); + + bool bIsSignatureLine = false; + xPropSet->getPropertyValue("IsSignatureLine") >>= bIsSignatureLine; + if (!bIsSignatureLine) + return; + + OUString aSignatureLineId; + xPropSet->getPropertyValue("SignatureLineId") >>= aSignatureLineId; + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_ID, aSignatureLineId); + + OUString aSuggestedSignerName; + xPropSet->getPropertyValue("SignatureLineSuggestedSignerName") >>= aSuggestedSignerName; + if (!aSuggestedSignerName.isEmpty()) + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_SUGGESTED_SIGNER_NAME, aSuggestedSignerName); + + OUString aSuggestedSignerTitle; + xPropSet->getPropertyValue("SignatureLineSuggestedSignerTitle") >>= aSuggestedSignerTitle; + if (!aSuggestedSignerTitle.isEmpty()) + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_SUGGESTED_SIGNER_TITLE, aSuggestedSignerTitle); + + OUString aSuggestedSignerEmail; + xPropSet->getPropertyValue("SignatureLineSuggestedSignerEmail") >>= aSuggestedSignerEmail; + if (!aSuggestedSignerEmail.isEmpty()) + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_SUGGESTED_SIGNER_EMAIL, aSuggestedSignerEmail); + + OUString aSigningInstructions; + xPropSet->getPropertyValue("SignatureLineSigningInstructions") >>= aSigningInstructions; + if (!aSigningInstructions.isEmpty()) + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_SIGNING_INSTRUCTIONS, aSigningInstructions); + + bool bShowSignDate = false; + xPropSet->getPropertyValue("SignatureLineShowSignDate") >>= bShowSignDate; + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_SHOW_SIGN_DATE, + bShowSignDate ? XML_TRUE : XML_FALSE); + + bool bCanAddComment = false; + xPropSet->getPropertyValue("SignatureLineCanAddComment") >>= bCanAddComment; + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_CAN_ADD_COMMENT, + bCanAddComment ? XML_TRUE : XML_FALSE); + + SvXMLElementExport aSignatureLineElement(mrExport, XML_NAMESPACE_LO_EXT, XML_SIGNATURELINE, true, + true); +} + +void XMLShapeExport::ImpExportQRCode(const uno::Reference<drawing::XShape>& xShape) +{ + uno::Reference<beans::XPropertySet> xPropSet(xShape, uno::UNO_QUERY); + + uno::Any aAny = xPropSet->getPropertyValue("BarCodeProperties"); + + css::drawing::BarCode aBarCode; + if(!(aAny >>= aBarCode)) + return; + + mrExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_STRING_VALUE, aBarCode.Payload); + /* Export QR Code as per customised schema, @see OpenDocument-schema-v1.3+libreoffice */ + OUString temp; + switch(aBarCode.ErrorCorrection){ + case css::drawing::BarCodeErrorCorrection::LOW : + temp = "low"; + break; + case css::drawing::BarCodeErrorCorrection::MEDIUM: + temp = "medium"; + break; + case css::drawing::BarCodeErrorCorrection::QUARTILE: + temp = "quartile"; + break; + case css::drawing::BarCodeErrorCorrection::HIGH: + temp = "high"; + break; + } + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_QRCODE_ERROR_CORRECTION, temp); + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_QRCODE_BORDER, OUStringBuffer(20).append(aBarCode.Border).makeStringAndClear()); + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_QRCODE_TYPE, OUStringBuffer(20).append(aBarCode.Type).makeStringAndClear()); + + SvXMLElementExport aBarCodeElement(mrExport, XML_NAMESPACE_LO_EXT, XML_QRCODE, true, + true); +} + +void XMLShapeExport::ExportGraphicDefaults() +{ + rtl::Reference<XMLStyleExport> aStEx(new XMLStyleExport(mrExport, mrExport.GetAutoStylePool().get())); + + // construct PropertySetMapper + rtl::Reference< SvXMLExportPropertyMapper > xPropertySetMapper( CreateShapePropMapper( mrExport ) ); + static_cast<XMLShapeExportPropertyMapper*>(xPropertySetMapper.get())->SetAutoStyles( false ); + + // chain text attributes + xPropertySetMapper->ChainExportMapper(XMLTextParagraphExport::CreateParaExtPropMapper(mrExport)); + + // chain special Writer/text frame default attributes + xPropertySetMapper->ChainExportMapper(XMLTextParagraphExport::CreateParaDefaultExtPropMapper(mrExport)); + + // write graphic family default style + uno::Reference< lang::XMultiServiceFactory > xFact( mrExport.GetModel(), uno::UNO_QUERY ); + if( !xFact.is() ) + return; + + try + { + uno::Reference< beans::XPropertySet > xDefaults( xFact->createInstance("com.sun.star.drawing.Defaults"), uno::UNO_QUERY ); + if( xDefaults.is() ) + { + aStEx->exportDefaultStyle( xDefaults, XML_STYLE_FAMILY_SD_GRAPHICS_NAME, xPropertySetMapper ); + + // write graphic styles (family name differs depending on the module) + aStEx->exportStyleFamily("graphics", XML_STYLE_FAMILY_SD_GRAPHICS_NAME, xPropertySetMapper, false, XmlStyleFamily::SD_GRAPHICS_ID); + aStEx->exportStyleFamily("GraphicStyles", XML_STYLE_FAMILY_SD_GRAPHICS_NAME, xPropertySetMapper, false, XmlStyleFamily::SD_GRAPHICS_ID); + } + } + catch(const lang::ServiceNotRegisteredException&) + { + } +} + +void XMLShapeExport::onExport( const css::uno::Reference < css::drawing::XShape >& ) +{ +} + +const rtl::Reference< XMLTableExport >& XMLShapeExport::GetShapeTableExport() +{ + if( !mxShapeTableExport.is() ) + { + rtl::Reference< XMLPropertyHandlerFactory > xFactory( new XMLSdPropHdlFactory( mrExport.GetModel(), mrExport ) ); + rtl::Reference < XMLPropertySetMapper > xMapper( new XMLShapePropertySetMapper( xFactory, true ) ); + mrExport.GetTextParagraphExport(); // get or create text paragraph export + rtl::Reference< SvXMLExportPropertyMapper > xPropertySetMapper( new XMLShapeExportPropertyMapper( xMapper, mrExport ) ); + mxShapeTableExport = new XMLTableExport( mrExport, xPropertySetMapper, xFactory ); + } + + return mxShapeTableExport; +} + +void XMLShapeExport::ImpExportNewTrans(const uno::Reference< beans::XPropertySet >& xPropSet, + XMLShapeExportFlags nFeatures, awt::Point* pRefPoint) +{ + // get matrix + ::basegfx::B2DHomMatrix aMatrix; + ImpExportNewTrans_GetB2DHomMatrix(aMatrix, xPropSet); + + // decompose and correct about pRefPoint + ::basegfx::B2DTuple aTRScale; + double fTRShear(0.0); + double fTRRotate(0.0); + ::basegfx::B2DTuple aTRTranslate; + ImpExportNewTrans_DecomposeAndRefPoint(aMatrix, aTRScale, fTRShear, fTRRotate, aTRTranslate, pRefPoint); + + // use features and write + ImpExportNewTrans_FeaturesAndWrite(aTRScale, fTRShear, fTRRotate, aTRTranslate, nFeatures); +} + +void XMLShapeExport::ImpExportNewTrans_GetB2DHomMatrix(::basegfx::B2DHomMatrix& rMatrix, + const uno::Reference< beans::XPropertySet >& xPropSet) +{ + /* Get <TransformationInHoriL2R>, if it exist + and if the document is exported into the OpenOffice.org file format. + This property only exists at service css::text::Shape - the + Writer UNO service for shapes. + This code is needed, because the positioning attributes in the + OpenOffice.org file format are given in horizontal left-to-right layout + regardless the layout direction the shape is in. In the OASIS Open Office + file format the positioning attributes are correctly given in the layout + direction the shape is in. Thus, this code provides the conversion from + the OASIS Open Office file format to the OpenOffice.org file format. (#i28749#) + */ + uno::Any aAny; + if ( !( GetExport().getExportFlags() & SvXMLExportFlags::OASIS ) && + xPropSet->getPropertySetInfo()->hasPropertyByName("TransformationInHoriL2R") ) + { + aAny = xPropSet->getPropertyValue("TransformationInHoriL2R"); + } + else + { + aAny = xPropSet->getPropertyValue("Transformation"); + } + drawing::HomogenMatrix3 aMatrix; + aAny >>= aMatrix; + + rMatrix.set(0, 0, aMatrix.Line1.Column1); + rMatrix.set(0, 1, aMatrix.Line1.Column2); + rMatrix.set(0, 2, aMatrix.Line1.Column3); + rMatrix.set(1, 0, aMatrix.Line2.Column1); + rMatrix.set(1, 1, aMatrix.Line2.Column2); + rMatrix.set(1, 2, aMatrix.Line2.Column3); + // For this to be a valid 2D transform matrix, the last row must be [0,0,1] + assert( aMatrix.Line3.Column1 == 0 ); + assert( aMatrix.Line3.Column2 == 0 ); + assert( aMatrix.Line3.Column3 == 1 ); +} + +void XMLShapeExport::ImpExportNewTrans_DecomposeAndRefPoint(const ::basegfx::B2DHomMatrix& rMatrix, ::basegfx::B2DTuple& rTRScale, + double& fTRShear, double& fTRRotate, ::basegfx::B2DTuple& rTRTranslate, css::awt::Point* pRefPoint) +{ + // decompose matrix + rMatrix.decompose(rTRScale, rTRTranslate, fTRRotate, fTRShear); + + // correct translation about pRefPoint + if(pRefPoint) + { + rTRTranslate -= ::basegfx::B2DTuple(pRefPoint->X, pRefPoint->Y); + } +} + +void XMLShapeExport::ImpExportNewTrans_FeaturesAndWrite(::basegfx::B2DTuple const & rTRScale, double fTRShear, + double fTRRotate, ::basegfx::B2DTuple const & rTRTranslate, const XMLShapeExportFlags nFeatures) +{ + // always write Size (rTRScale) since this statement carries the union + // of the object + OUString aStr; + OUStringBuffer sStringBuffer; + ::basegfx::B2DTuple aTRScale(rTRScale); + + // svg: width + if(!(nFeatures & XMLShapeExportFlags::WIDTH)) + { + aTRScale.setX(1.0); + } + else + { + if( aTRScale.getX() > 0.0 ) + aTRScale.setX(aTRScale.getX() - 1.0); + else if( aTRScale.getX() < 0.0 ) + aTRScale.setX(aTRScale.getX() + 1.0); + } + + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + FRound(aTRScale.getX())); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_WIDTH, aStr); + + // svg: height + if(!(nFeatures & XMLShapeExportFlags::HEIGHT)) + { + aTRScale.setY(1.0); + } + else + { + if( aTRScale.getY() > 0.0 ) + aTRScale.setY(aTRScale.getY() - 1.0); + else if( aTRScale.getY() < 0.0 ) + aTRScale.setY(aTRScale.getY() + 1.0); + } + + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + FRound(aTRScale.getY())); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_HEIGHT, aStr); + + // decide if transformation is necessary + bool bTransformationIsNecessary(fTRShear != 0.0 || fTRRotate != 0.0); + + if(bTransformationIsNecessary) + { + // write transformation, but WITHOUT scale which is exported as size above + SdXMLImExTransform2D aTransform; + + aTransform.AddSkewX(atan(fTRShear)); + + // #i78696# + // fTRRotate is mathematically correct, but due to the error + // we export/import it mirrored. Since the API implementation is fixed and + // uses the correctly oriented angle, it is necessary for compatibility to + // mirror the angle here to stay at the old behaviour. There is a follow-up + // task (#i78698#) to fix this in the next ODF FileFormat version + aTransform.AddRotate(-fTRRotate); + + aTransform.AddTranslate(rTRTranslate); + + // does transformation need to be exported? + if(aTransform.NeedsAction()) + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_TRANSFORM, aTransform.GetExportString(mrExport.GetMM100UnitConverter())); + } + else + { + // no shear, no rotate; just add object position to export and we are done + if(nFeatures & XMLShapeExportFlags::X) + { + // svg: x + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + FRound(rTRTranslate.getX())); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_X, aStr); + } + + if(nFeatures & XMLShapeExportFlags::Y) + { + // svg: y + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + FRound(rTRTranslate.getY())); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_Y, aStr); + } + } +} + +bool XMLShapeExport::ImpExportPresentationAttributes( const uno::Reference< beans::XPropertySet >& xPropSet, const OUString& rClass ) +{ + bool bIsEmpty = false; + + // write presentation class entry + mrExport.AddAttribute(XML_NAMESPACE_PRESENTATION, XML_CLASS, rClass); + + if( xPropSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xPropSet->getPropertySetInfo() ); + + + // is empty pres. shape? + if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("IsEmptyPresentationObject")) + { + xPropSet->getPropertyValue("IsEmptyPresentationObject") >>= bIsEmpty; + if( bIsEmpty ) + mrExport.AddAttribute(XML_NAMESPACE_PRESENTATION, XML_PLACEHOLDER, XML_TRUE); + } + + // is user-transformed? + if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("IsPlaceholderDependent")) + { + bool bTemp = false; + xPropSet->getPropertyValue("IsPlaceholderDependent") >>= bTemp; + if(!bTemp) + mrExport.AddAttribute(XML_NAMESPACE_PRESENTATION, XML_USER_TRANSFORMED, XML_TRUE); + } + } + + return bIsEmpty; +} + +void XMLShapeExport::ImpExportText( const uno::Reference< drawing::XShape >& xShape, TextPNS eExtensionNS ) +{ + if (eExtensionNS == TextPNS::EXTENSION) + { + if ((mrExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0) + { + return; // do not export to ODF 1.1/1.2/1.3 + } + } + uno::Reference< text::XText > xText( xShape, uno::UNO_QUERY ); + if( xText.is() ) + { + uno::Reference< container::XEnumerationAccess > xEnumAccess( xShape, uno::UNO_QUERY ); + if( xEnumAccess.is() && xEnumAccess->hasElements() ) + mrExport.GetTextParagraphExport()->exportText( xText, false, true, eExtensionNS ); + } +} + +namespace { + +enum class Found { + NONE = 0x0000, + CLICKACTION = 0x0001, + BOOKMARK = 0x0002, + EFFECT = 0x0004, + PLAYFULL = 0x0008, + VERB = 0x0010, + SOUNDURL = 0x0020, + SPEED = 0x0040, + CLICKEVENTTYPE = 0x0080, + MACRO = 0x0100, + LIBRARY = 0x0200, +}; + +} + +namespace o3tl { + template<> struct typed_flags<Found> : is_typed_flags<Found, 0x03ff> {}; +} + +void XMLShapeExport::ImpExportEvents( const uno::Reference< drawing::XShape >& xShape ) +{ + uno::Reference< document::XEventsSupplier > xEventsSupplier( xShape, uno::UNO_QUERY ); + if( !xEventsSupplier.is() ) + return; + + uno::Reference< container::XNameAccess > xEvents = xEventsSupplier->getEvents(); + SAL_WARN_IF( !xEvents.is(), "xmloff", "XEventsSupplier::getEvents() returned NULL" ); + if( !xEvents.is() ) + return; + + Found nFound = Found::NONE; + + OUString aClickEventType; + presentation::ClickAction eClickAction = presentation::ClickAction_NONE; + presentation::AnimationEffect eEffect = presentation::AnimationEffect_NONE; + presentation::AnimationSpeed eSpeed = presentation::AnimationSpeed_SLOW; + OUString aStrSoundURL; + bool bPlayFull = false; + sal_Int32 nVerb = 0; + OUString aStrMacro; + OUString aStrLibrary; + OUString aStrBookmark; + + uno::Sequence< beans::PropertyValue > aClickProperties; + if( xEvents->hasByName( gsOnClick ) && (xEvents->getByName( gsOnClick ) >>= aClickProperties) ) + { + for( const auto& rProperty : std::as_const(aClickProperties) ) + { + if( !( nFound & Found::CLICKEVENTTYPE ) && rProperty.Name == gsEventType ) + { + if( rProperty.Value >>= aClickEventType ) + nFound |= Found::CLICKEVENTTYPE; + } + else if( !( nFound & Found::CLICKACTION ) && rProperty.Name == gsClickAction ) + { + if( rProperty.Value >>= eClickAction ) + nFound |= Found::CLICKACTION; + } + else if( !( nFound & Found::MACRO ) && ( rProperty.Name == gsMacroName || rProperty.Name == gsScript ) ) + { + if( rProperty.Value >>= aStrMacro ) + nFound |= Found::MACRO; + } + else if( !( nFound & Found::LIBRARY ) && rProperty.Name == gsLibrary ) + { + if( rProperty.Value >>= aStrLibrary ) + nFound |= Found::LIBRARY; + } + else if( !( nFound & Found::EFFECT ) && rProperty.Name == gsEffect ) + { + if( rProperty.Value >>= eEffect ) + nFound |= Found::EFFECT; + } + else if( !( nFound & Found::BOOKMARK ) && rProperty.Name == gsBookmark ) + { + if( rProperty.Value >>= aStrBookmark ) + nFound |= Found::BOOKMARK; + } + else if( !( nFound & Found::SPEED ) && rProperty.Name == gsSpeed ) + { + if( rProperty.Value >>= eSpeed ) + nFound |= Found::SPEED; + } + else if( !( nFound & Found::SOUNDURL ) && rProperty.Name == gsSoundURL ) + { + if( rProperty.Value >>= aStrSoundURL ) + nFound |= Found::SOUNDURL; + } + else if( !( nFound & Found::PLAYFULL ) && rProperty.Name == gsPlayFull ) + { + if( rProperty.Value >>= bPlayFull ) + nFound |= Found::PLAYFULL; + } + else if( !( nFound & Found::VERB ) && rProperty.Name == gsVerb ) + { + if( rProperty.Value >>= nVerb ) + nFound |= Found::VERB; + } + } + } + + // create the XML elements + + if( aClickEventType == gsPresentation ) + { + if( !(nFound & Found::CLICKACTION) || (eClickAction == presentation::ClickAction_NONE) ) + return; + + SvXMLElementExport aEventsElemt(mrExport, XML_NAMESPACE_OFFICE, XML_EVENT_LISTENERS, true, true); + + enum XMLTokenEnum eStrAction; + + switch( eClickAction ) + { + case presentation::ClickAction_PREVPAGE: eStrAction = XML_PREVIOUS_PAGE; break; + case presentation::ClickAction_NEXTPAGE: eStrAction = XML_NEXT_PAGE; break; + case presentation::ClickAction_FIRSTPAGE: eStrAction = XML_FIRST_PAGE; break; + case presentation::ClickAction_LASTPAGE: eStrAction = XML_LAST_PAGE; break; + case presentation::ClickAction_INVISIBLE: eStrAction = XML_HIDE; break; + case presentation::ClickAction_STOPPRESENTATION:eStrAction = XML_STOP; break; + case presentation::ClickAction_PROGRAM: eStrAction = XML_EXECUTE; break; + case presentation::ClickAction_BOOKMARK: eStrAction = XML_SHOW; break; + case presentation::ClickAction_DOCUMENT: eStrAction = XML_SHOW; break; + case presentation::ClickAction_MACRO: eStrAction = XML_EXECUTE_MACRO; break; + case presentation::ClickAction_VERB: eStrAction = XML_VERB; break; + case presentation::ClickAction_VANISH: eStrAction = XML_FADE_OUT; break; + case presentation::ClickAction_SOUND: eStrAction = XML_SOUND; break; + default: + OSL_FAIL( "unknown presentation::ClickAction found!" ); + eStrAction = XML_UNKNOWN; + } + + OUString aEventQName( + mrExport.GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_DOM, "click" ) ); + mrExport.AddAttribute( XML_NAMESPACE_SCRIPT, XML_EVENT_NAME, aEventQName ); + mrExport.AddAttribute( XML_NAMESPACE_PRESENTATION, XML_ACTION, eStrAction ); + + if( eClickAction == presentation::ClickAction_VANISH ) + { + if( nFound & Found::EFFECT ) + { + XMLEffect eKind; + XMLEffectDirection eDirection; + sal_Int16 nStartScale; + bool bIn; + + SdXMLImplSetEffect( eEffect, eKind, eDirection, nStartScale, bIn ); + + if( eKind != EK_none ) + { + SvXMLUnitConverter::convertEnum( msBuffer, eKind, aXML_AnimationEffect_EnumMap ); + mrExport.AddAttribute( XML_NAMESPACE_PRESENTATION, XML_EFFECT, msBuffer.makeStringAndClear() ); + } + + if( eDirection != ED_none ) + { + SvXMLUnitConverter::convertEnum( msBuffer, eDirection, aXML_AnimationDirection_EnumMap ); + mrExport.AddAttribute( XML_NAMESPACE_PRESENTATION, XML_DIRECTION, msBuffer.makeStringAndClear() ); + } + + if( nStartScale != -1 ) + { + ::sax::Converter::convertPercent( msBuffer, nStartScale ); + mrExport.AddAttribute( XML_NAMESPACE_PRESENTATION, XML_START_SCALE, msBuffer.makeStringAndClear() ); + } + } + + if( nFound & Found::SPEED && eEffect != presentation::AnimationEffect_NONE ) + { + if( eSpeed != presentation::AnimationSpeed_MEDIUM ) + { + SvXMLUnitConverter::convertEnum( msBuffer, eSpeed, aXML_AnimationSpeed_EnumMap ); + mrExport.AddAttribute( XML_NAMESPACE_PRESENTATION, XML_SPEED, msBuffer.makeStringAndClear() ); + } + } + } + + if( eClickAction == presentation::ClickAction_PROGRAM || + eClickAction == presentation::ClickAction_BOOKMARK || + eClickAction == presentation::ClickAction_DOCUMENT ) + { + if( eClickAction == presentation::ClickAction_BOOKMARK ) + msBuffer.append( '#' ); + + msBuffer.append( aStrBookmark ); + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, GetExport().GetRelativeReference(msBuffer.makeStringAndClear()) ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONREQUEST ); + } + + if( ( nFound & Found::VERB ) && eClickAction == presentation::ClickAction_VERB ) + { + msBuffer.append( nVerb ); + mrExport.AddAttribute(XML_NAMESPACE_PRESENTATION, XML_VERB, msBuffer.makeStringAndClear()); + } + + SvXMLElementExport aEventElemt(mrExport, XML_NAMESPACE_PRESENTATION, XML_EVENT_LISTENER, true, true); + + if( eClickAction == presentation::ClickAction_VANISH || eClickAction == presentation::ClickAction_SOUND ) + { + if( ( nFound & Found::SOUNDURL ) && !aStrSoundURL.isEmpty() ) + { + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, GetExport().GetRelativeReference(aStrSoundURL) ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_SHOW, XML_NEW ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONREQUEST ); + if( nFound & Found::PLAYFULL && bPlayFull ) + mrExport.AddAttribute( XML_NAMESPACE_PRESENTATION, XML_PLAY_FULL, XML_TRUE ); + + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_PRESENTATION, XML_SOUND, true, true ); + } + } + } + else if( aClickEventType == gsStarBasic ) + { + if( nFound & Found::MACRO ) + { + SvXMLElementExport aEventsElemt(mrExport, XML_NAMESPACE_OFFICE, XML_EVENT_LISTENERS, true, true); + + mrExport.AddAttribute( XML_NAMESPACE_SCRIPT, XML_LANGUAGE, + mrExport.GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_OOO, + "starbasic" ) ); + OUString aEventQName( + mrExport.GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_DOM, "click" ) ); + mrExport.AddAttribute( XML_NAMESPACE_SCRIPT, XML_EVENT_NAME, aEventQName ); + + if( nFound & Found::LIBRARY ) + { + const OUString& sLocation( GetXMLToken( + (aStrLibrary.equalsIgnoreAsciiCase("StarOffice") || + aStrLibrary.equalsIgnoreAsciiCase("application") ) ? XML_APPLICATION + : XML_DOCUMENT ) ); + mrExport.AddAttribute(XML_NAMESPACE_SCRIPT, XML_MACRO_NAME, + sLocation + ":" + aStrMacro); + } + else + { + mrExport.AddAttribute( XML_NAMESPACE_SCRIPT, XML_MACRO_NAME, aStrMacro ); + } + + SvXMLElementExport aEventElemt(mrExport, XML_NAMESPACE_SCRIPT, XML_EVENT_LISTENER, true, true); + } + } + else if( aClickEventType == gsScript ) + { + if( nFound & Found::MACRO ) + { + SvXMLElementExport aEventsElemt(mrExport, XML_NAMESPACE_OFFICE, XML_EVENT_LISTENERS, true, true); + + mrExport.AddAttribute( XML_NAMESPACE_SCRIPT, XML_LANGUAGE, mrExport.GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_OOO, GetXMLToken(XML_SCRIPT) ) ); + OUString aEventQName( + mrExport.GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_DOM, "click" ) ); + mrExport.AddAttribute( XML_NAMESPACE_SCRIPT, XML_EVENT_NAME, aEventQName ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, aStrMacro ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, "simple" ); + + SvXMLElementExport aEventElemt(mrExport, XML_NAMESPACE_SCRIPT, XML_EVENT_LISTENER, true, true); + } + } +} + +/** #i68101# export shape Title and Description */ +void XMLShapeExport::ImpExportDescription( const uno::Reference< drawing::XShape >& xShape ) +{ + try + { + OUString aTitle; + OUString aDescription; + + uno::Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY_THROW ); + xProps->getPropertyValue("Title") >>= aTitle; + xProps->getPropertyValue("Description") >>= aDescription; + + if(!aTitle.isEmpty()) + { + SvXMLElementExport aEventElemt(mrExport, XML_NAMESPACE_SVG, XML_TITLE, true, false); + mrExport.Characters( aTitle ); + } + + if(!aDescription.isEmpty()) + { + SvXMLElementExport aEventElemt(mrExport, XML_NAMESPACE_SVG, XML_DESC, true, false ); + mrExport.Characters( aDescription ); + } + } + catch( uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION( "xmloff", "exporting Title and/or Description for shape" ); + } +} + +void XMLShapeExport::ImpExportGroupShape( const uno::Reference< drawing::XShape >& xShape, XMLShapeExportFlags nFeatures, awt::Point* pRefPoint) +{ + uno::Reference< drawing::XShapes > xShapes(xShape, uno::UNO_QUERY); + if(!(xShapes.is() && xShapes->getCount())) + return; + + // write group shape + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aPGR(mrExport, XML_NAMESPACE_DRAW, XML_G, bCreateNewline, true); + + ImpExportDescription( xShape ); // #i68101# + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + + // #89764# if export of position is suppressed for group shape, + // positions of contained objects should be written relative to + // the upper left edge of the group. + awt::Point aUpperLeft; + + if(!(nFeatures & XMLShapeExportFlags::POSITION)) + { + nFeatures |= XMLShapeExportFlags::POSITION; + aUpperLeft = xShape->getPosition(); + pRefPoint = &aUpperLeft; + } + + // write members + exportShapes( xShapes, nFeatures, pRefPoint ); +} + +void XMLShapeExport::ImpExportTextBoxShape( + const uno::Reference< drawing::XShape >& xShape, + XmlShapeType eShapeType, XMLShapeExportFlags nFeatures, awt::Point* pRefPoint) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + // presentation attribute (if presentation) + bool bIsPresShape(false); + bool bIsEmptyPresObj(false); + OUString aStr; + + switch(eShapeType) + { + case XmlShapeType::PresSubtitleShape: + { + aStr = GetXMLToken(XML_SUBTITLE); + bIsPresShape = true; + break; + } + case XmlShapeType::PresTitleTextShape: + { + aStr = GetXMLToken(XML_TITLE); + bIsPresShape = true; + break; + } + case XmlShapeType::PresOutlinerShape: + { + aStr = GetXMLToken(XML_PRESENTATION_OUTLINE); + bIsPresShape = true; + break; + } + case XmlShapeType::PresNotesShape: + { + aStr = GetXMLToken(XML_NOTES); + bIsPresShape = true; + break; + } + case XmlShapeType::PresHeaderShape: + { + aStr = GetXMLToken(XML_HEADER); + bIsPresShape = true; + break; + } + case XmlShapeType::PresFooterShape: + { + aStr = GetXMLToken(XML_FOOTER); + bIsPresShape = true; + break; + } + case XmlShapeType::PresSlideNumberShape: + { + aStr = GetXMLToken(XML_PAGE_NUMBER); + bIsPresShape = true; + break; + } + case XmlShapeType::PresDateTimeShape: + { + aStr = GetXMLToken(XML_DATE_TIME); + bIsPresShape = true; + break; + } + default: + break; + } + + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + if(bIsPresShape) + bIsEmptyPresObj = ImpExportPresentationAttributes( xPropSet, aStr ); + + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DRAW, + XML_FRAME, bCreateNewline, true ); + + // evtl. corner radius? + sal_Int32 nCornerRadius(0); + xPropSet->getPropertyValue("CornerRadius") >>= nCornerRadius; + if(nCornerRadius) + { + OUStringBuffer sStringBuffer; + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + nCornerRadius); + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_CORNER_RADIUS, sStringBuffer.makeStringAndClear()); + } + + { + // write text-box + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DRAW, XML_TEXT_BOX, true, true); + if(!bIsEmptyPresObj) + ImpExportText( xShape ); + } + + ImpExportDescription( xShape ); // #i68101# + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + +} + +void XMLShapeExport::ImpExportRectangleShape( + const uno::Reference< drawing::XShape >& xShape, + XMLShapeExportFlags nFeatures, css::awt::Point* pRefPoint) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + // evtl. corner radius? + sal_Int32 nCornerRadius(0); + xPropSet->getPropertyValue("CornerRadius") >>= nCornerRadius; + if(nCornerRadius) + { + OUStringBuffer sStringBuffer; + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + nCornerRadius); + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_CORNER_RADIUS, sStringBuffer.makeStringAndClear()); + } + + // write rectangle + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DRAW, XML_RECT, bCreateNewline, true); + + ImpExportDescription( xShape ); // #i68101# + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + ImpExportText( xShape ); +} + +void XMLShapeExport::ImpExportLineShape( + const uno::Reference< drawing::XShape >& xShape, + XMLShapeExportFlags nFeatures, awt::Point* pRefPoint) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + OUString aStr; + OUStringBuffer sStringBuffer; + awt::Point aStart(0,0); + awt::Point aEnd(1,1); + + // #85920# use 'Geometry' to get the points of the line + // since this slot take anchor pos into account. + + // get matrix + ::basegfx::B2DHomMatrix aMatrix; + ImpExportNewTrans_GetB2DHomMatrix(aMatrix, xPropSet); + + // decompose and correct about pRefPoint + ::basegfx::B2DTuple aTRScale; + double fTRShear(0.0); + double fTRRotate(0.0); + ::basegfx::B2DTuple aTRTranslate; + ImpExportNewTrans_DecomposeAndRefPoint(aMatrix, aTRScale, fTRShear, fTRRotate, aTRTranslate, pRefPoint); + + // create base position + awt::Point aBasePosition(FRound(aTRTranslate.getX()), FRound(aTRTranslate.getY())); + + if (xPropSet->getPropertySetInfo()->hasPropertyByName("Geometry")) + { + // get the two points + uno::Any aAny(xPropSet->getPropertyValue("Geometry")); + if (auto pSourcePolyPolygon + = o3tl::tryAccess<drawing::PointSequenceSequence>(aAny)) + { + if (pSourcePolyPolygon->getLength() > 0) + { + const drawing::PointSequence& rInnerSequence = (*pSourcePolyPolygon)[0]; + if (rInnerSequence.hasElements()) + { + const awt::Point& rPoint = rInnerSequence[0]; + aStart = awt::Point(rPoint.X + aBasePosition.X, rPoint.Y + aBasePosition.Y); + } + if (rInnerSequence.getLength() > 1) + { + const awt::Point& rPoint = rInnerSequence[1]; + aEnd = awt::Point(rPoint.X + aBasePosition.X, rPoint.Y + aBasePosition.Y); + } + } + } + } + + if( nFeatures & XMLShapeExportFlags::X ) + { + // svg: x1 + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + aStart.X); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_X1, aStr); + } + else + { + aEnd.X -= aStart.X; + } + + if( nFeatures & XMLShapeExportFlags::Y ) + { + // svg: y1 + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + aStart.Y); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_Y1, aStr); + } + else + { + aEnd.Y -= aStart.Y; + } + + // svg: x2 + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + aEnd.X); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_X2, aStr); + + // svg: y2 + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + aEnd.Y); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_Y2, aStr); + + // write line + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DRAW, XML_LINE, bCreateNewline, true); + + ImpExportDescription( xShape ); // #i68101# + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + ImpExportText( xShape ); + +} + +void XMLShapeExport::ImpExportEllipseShape( + const uno::Reference< drawing::XShape >& xShape, + XMLShapeExportFlags nFeatures, awt::Point* pRefPoint) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + // get size to decide between Circle and Ellipse + awt::Size aSize = xShape->getSize(); + sal_Int32 nRx((aSize.Width + 1) / 2); + sal_Int32 nRy((aSize.Height + 1) / 2); + bool bCircle(nRx == nRy); + + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + drawing::CircleKind eKind = drawing::CircleKind_FULL; + xPropSet->getPropertyValue("CircleKind") >>= eKind; + if( eKind != drawing::CircleKind_FULL ) + { + OUStringBuffer sStringBuffer; + sal_Int32 nStartAngle = 0; + sal_Int32 nEndAngle = 0; + xPropSet->getPropertyValue("CircleStartAngle") >>= nStartAngle; + xPropSet->getPropertyValue("CircleEndAngle") >>= nEndAngle; + + const double dStartAngle = nStartAngle / 100.0; + const double dEndAngle = nEndAngle / 100.0; + + // export circle kind + SvXMLUnitConverter::convertEnum( sStringBuffer, eKind, aXML_CircleKind_EnumMap ); + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_KIND, sStringBuffer.makeStringAndClear() ); + + // export start angle + ::sax::Converter::convertDouble( sStringBuffer, dStartAngle ); + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_START_ANGLE, sStringBuffer.makeStringAndClear() ); + + // export end angle + ::sax::Converter::convertDouble( sStringBuffer, dEndAngle ); + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_END_ANGLE, sStringBuffer.makeStringAndClear() ); + } + + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + + // write ellipse or circle + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DRAW, + bCircle ? XML_CIRCLE : XML_ELLIPSE, + bCreateNewline, true); + + ImpExportDescription( xShape ); // #i68101# + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + ImpExportText( xShape ); + +} + +void XMLShapeExport::ImpExportPolygonShape( + const uno::Reference< drawing::XShape >& xShape, + XmlShapeType eShapeType, XMLShapeExportFlags nFeatures, awt::Point* pRefPoint) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + bool bBezier(eShapeType == XmlShapeType::DrawClosedBezierShape + || eShapeType == XmlShapeType::DrawOpenBezierShape); + + // get matrix + ::basegfx::B2DHomMatrix aMatrix; + ImpExportNewTrans_GetB2DHomMatrix(aMatrix, xPropSet); + + // decompose and correct about pRefPoint + ::basegfx::B2DTuple aTRScale; + double fTRShear(0.0); + double fTRRotate(0.0); + ::basegfx::B2DTuple aTRTranslate; + ImpExportNewTrans_DecomposeAndRefPoint(aMatrix, aTRScale, fTRShear, fTRRotate, aTRTranslate, pRefPoint); + + // use features and write + ImpExportNewTrans_FeaturesAndWrite(aTRScale, fTRShear, fTRRotate, aTRTranslate, nFeatures); + + // create and export ViewBox + awt::Size aSize(FRound(aTRScale.getX()), FRound(aTRScale.getY())); + SdXMLImExViewBox aViewBox(0, 0, aSize.Width, aSize.Height); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_VIEWBOX, aViewBox.GetExportString()); + + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + + // prepare name (with most used) + enum ::xmloff::token::XMLTokenEnum eName(XML_PATH); + + uno::Any aAny( xPropSet->getPropertyValue("Geometry") ); + basegfx::B2DPolyPolygon aPolyPolygon; + + // tdf#145240 the Any can contain PolyPolygonBezierCoords or PointSequenceSequence + // (see OWN_ATTR_BASE_GEOMETRY in SvxShapePolyPolygon::getPropertyValueImpl), + // so be more flexible in interpreting it. Try to access bezier first: + { + auto pSourcePolyPolygon = o3tl::tryAccess<drawing::PolyPolygonBezierCoords>(aAny); + + if(pSourcePolyPolygon && pSourcePolyPolygon->Coordinates.getLength()) + { + aPolyPolygon = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon(*pSourcePolyPolygon); + } + } + + // if received no data, try to access point sequence second: + if(0 == aPolyPolygon.count()) + { + auto pSourcePolyPolygon = o3tl::tryAccess<drawing::PointSequenceSequence>(aAny); + + if(pSourcePolyPolygon) + { + aPolyPolygon = basegfx::utils::UnoPointSequenceSequenceToB2DPolyPolygon(*pSourcePolyPolygon); + } + } + + if(aPolyPolygon.count()) + { + if(!bBezier && !aPolyPolygon.areControlPointsUsed() && 1 == aPolyPolygon.count()) + { + // simple polygon shape, can be written as svg:points sequence + const basegfx::B2DPolygon& aPolygon(aPolyPolygon.getB2DPolygon(0)); + const OUString aPointString(basegfx::utils::exportToSvgPoints(aPolygon)); + + // write point array + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_POINTS, aPointString); + + // set name + eName = aPolygon.isClosed() ? XML_POLYGON : XML_POLYLINE; + } + else + { + // complex polygon shape, write as svg:d + const OUString aPolygonString( + basegfx::utils::exportToSvgD( + aPolyPolygon, + true, // bUseRelativeCoordinates + false, // bDetectQuadraticBeziers: not used in old, but maybe activated now + true)); // bHandleRelativeNextPointCompatible + + // write point array + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_D, aPolygonString); + } + } + + // write object, but after attributes are added since this call will + // consume all of these added attributes and the destructor will close the + // scope. Also before text is added; this may add sub-scopes as needed + SvXMLElementExport aOBJ( + mrExport, + XML_NAMESPACE_DRAW, + eName, + bCreateNewline, + true); + + ImpExportDescription( xShape ); // #i68101# + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + ImpExportText( xShape ); + +} + +namespace +{ + +OUString getNameFromStreamURL(std::u16string_view rURL) +{ + static constexpr std::u16string_view sPackageURL(u"vnd.sun.star.Package:"); + + OUString sResult; + + if (o3tl::starts_with(rURL, sPackageURL)) + { + std::u16string_view sRequestedName = rURL.substr(sPackageURL.size()); + size_t nLastIndex = sRequestedName.rfind('/') + 1; + if ((nLastIndex > 0) && (nLastIndex < sRequestedName.size())) + sRequestedName = sRequestedName.substr(nLastIndex); + nLastIndex = sRequestedName.rfind('.'); + if (nLastIndex != std::u16string_view::npos) + sRequestedName = sRequestedName.substr(0, nLastIndex); + if (!sRequestedName.empty()) + sResult = sRequestedName; + } + + return sResult; +} + +} // end anonymous namespace + +void XMLShapeExport::ImpExportGraphicObjectShape( + const uno::Reference< drawing::XShape >& xShape, + XmlShapeType eShapeType, XMLShapeExportFlags nFeatures, awt::Point* pRefPoint) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + bool bIsEmptyPresObj = false; + + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + if(eShapeType == XmlShapeType::PresGraphicObjectShape) + bIsEmptyPresObj = ImpExportPresentationAttributes( xPropSet, GetXMLToken(XML_GRAPHIC) ); + + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DRAW, + XML_FRAME, bCreateNewline, true ); + + if (!bIsEmptyPresObj) + { + uno::Reference<graphic::XGraphic> xGraphic; + OUString sOutMimeType; + + { + OUString aStreamURL; + xPropSet->getPropertyValue("GraphicStreamURL") >>= aStreamURL; + OUString sRequestedName = getNameFromStreamURL(aStreamURL); + + xPropSet->getPropertyValue("Graphic") >>= xGraphic; + + OUString sInternalURL; + + if (xGraphic.is()) + sInternalURL = mrExport.AddEmbeddedXGraphic(xGraphic, sOutMimeType, sRequestedName); + + if (!sInternalURL.isEmpty()) + { + // apply possible changed stream URL to embedded image object + if (!sRequestedName.isEmpty()) + { + OUString newStreamURL = "vnd.sun.star.Package:"; + if (sInternalURL[0] == '#') + { + newStreamURL += sInternalURL.subView(1, sInternalURL.getLength() - 1); + } + else + { + newStreamURL += sInternalURL; + } + + if (newStreamURL != aStreamURL) + { + xPropSet->setPropertyValue("GraphicStreamURL", uno::Any(newStreamURL)); + } + } + + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, sInternalURL); + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE); + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED); + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD); + } + } + + { + if (GetExport().getSaneDefaultVersion() > SvtSaveOptions::ODFSVER_012) + { + if (sOutMimeType.isEmpty()) + { + GetExport().GetGraphicMimeTypeFromStream(xGraphic, sOutMimeType); + } + if (!sOutMimeType.isEmpty()) + { // ODF 1.3 OFFICE-3943 + GetExport().AddAttribute( + SvtSaveOptions::ODFSVER_013 <= GetExport().getSaneDefaultVersion() + ? XML_NAMESPACE_DRAW + : XML_NAMESPACE_LO_EXT, + "mime-type", sOutMimeType); + } + } + + SvXMLElementExport aElement(mrExport, XML_NAMESPACE_DRAW, XML_IMAGE, true, true); + + // optional office:binary-data + if (xGraphic.is()) + { + mrExport.AddEmbeddedXGraphicAsBase64(xGraphic); + } + if (!bIsEmptyPresObj) + ImpExportText(xShape); + } + + //Resolves: fdo#62461 put preferred image first above, followed by + //fallback here + const bool bAddReplacementImages = officecfg::Office::Common::Save::Graphic::AddReplacementImages::get(); + if( !bIsEmptyPresObj && bAddReplacementImages) + { + uno::Reference<graphic::XGraphic> xReplacementGraphic; + xPropSet->getPropertyValue("ReplacementGraphic") >>= xReplacementGraphic; + + // If there is no url, then the graphic is empty + if (xReplacementGraphic.is()) + { + OUString aMimeType; + const OUString aHref = mrExport.AddEmbeddedXGraphic(xReplacementGraphic, aMimeType); + + if (aMimeType.isEmpty()) + mrExport.GetGraphicMimeTypeFromStream(xReplacementGraphic, aMimeType); + + if (!aHref.isEmpty()) + { + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, aHref); + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED ); + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD ); + } + + if (!aMimeType.isEmpty() && GetExport().getSaneDefaultVersion() > SvtSaveOptions::ODFSVER_012) + { // ODF 1.3 OFFICE-3943 + mrExport.AddAttribute( + SvtSaveOptions::ODFSVER_013 <= GetExport().getSaneDefaultVersion() + ? XML_NAMESPACE_DRAW + : XML_NAMESPACE_LO_EXT, + "mime-type", aMimeType); + } + + SvXMLElementExport aElement(mrExport, XML_NAMESPACE_DRAW, XML_IMAGE, true, true); + + // optional office:binary-data + mrExport.AddEmbeddedXGraphicAsBase64(xReplacementGraphic); + } + } + } + + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + + // image map + GetExport().GetImageMapExport().Export( xPropSet ); + ImpExportDescription( xShape ); // #i68101# + + // Signature Line, QR Code - needs to be after the images! + if (GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) + { + ImpExportSignatureLine(xShape); + ImpExportQRCode(xShape); + } +} + +void XMLShapeExport::ImpExportChartShape( + const uno::Reference< drawing::XShape >& xShape, + XmlShapeType eShapeType, XMLShapeExportFlags nFeatures, awt::Point* pRefPoint, + comphelper::AttributeList* pAttrList ) +{ + ImpExportOLE2Shape( xShape, eShapeType, nFeatures, pRefPoint, pAttrList ); +} + +void XMLShapeExport::ImpExportControlShape( + const uno::Reference< drawing::XShape >& xShape, + XMLShapeExportFlags nFeatures, awt::Point* pRefPoint) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(xPropSet.is()) + { + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + } + + uno::Reference< drawing::XControlShape > xControl( xShape, uno::UNO_QUERY ); + SAL_WARN_IF( !xControl.is(), "xmloff", "Control shape is not supporting XControlShape" ); + if( xControl.is() ) + { + uno::Reference< beans::XPropertySet > xControlModel( xControl->getControl(), uno::UNO_QUERY ); + SAL_WARN_IF( !xControlModel.is(), "xmloff", "Control shape has not XControlModel" ); + if( xControlModel.is() ) + { + OUString sControlId = mrExport.GetFormExport()->getControlId( xControlModel ); + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CONTROL, sControlId ); + } + } + + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DRAW, XML_CONTROL, bCreateNewline, true); + + ImpExportDescription( xShape ); // #i68101# +} + +void XMLShapeExport::ImpExportConnectorShape( + const uno::Reference< drawing::XShape >& xShape, + XMLShapeExportFlags nFeatures /* = SEF_DEFAULT */, awt::Point* pRefPoint /* = NULL */) +{ + uno::Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + + OUString aStr; + OUStringBuffer sStringBuffer; + + // export connection kind + drawing::ConnectorType eType = drawing::ConnectorType_STANDARD; + uno::Any aAny = xProps->getPropertyValue("EdgeKind"); + aAny >>= eType; + + if( eType != drawing::ConnectorType_STANDARD ) + { + SvXMLUnitConverter::convertEnum( sStringBuffer, eType, aXML_ConnectionKind_EnumMap ); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_TYPE, aStr); + } + + // export line skew + sal_Int32 nDelta1 = 0, nDelta2 = 0, nDelta3 = 0; + + aAny = xProps->getPropertyValue("EdgeLine1Delta"); + aAny >>= nDelta1; + aAny = xProps->getPropertyValue("EdgeLine2Delta"); + aAny >>= nDelta2; + aAny = xProps->getPropertyValue("EdgeLine3Delta"); + aAny >>= nDelta3; + + if( nDelta1 != 0 || nDelta2 != 0 || nDelta3 != 0 ) + { + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + nDelta1); + if( nDelta2 != 0 || nDelta3 != 0 ) + { + sStringBuffer.append( ' ' ); + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + nDelta2); + if( nDelta3 != 0 ) + { + sStringBuffer.append( ' ' ); + mrExport.GetMM100UnitConverter().convertMeasureToXML( + sStringBuffer, nDelta3); + } + } + + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_LINE_SKEW, aStr); + } + + // export start and end point + awt::Point aStart(0,0); + awt::Point aEnd(1,1); + + /* Get <StartPositionInHoriL2R> and + <EndPositionInHoriL2R>, if they exist and if the document is exported + into the OpenOffice.org file format. + These properties only exist at service css::text::Shape - the + Writer UNO service for shapes. + This code is needed, because the positioning attributes in the + OpenOffice.org file format are given in horizontal left-to-right layout + regardless the layout direction the shape is in. In the OASIS Open Office + file format the positioning attributes are correctly given in the layout + direction the shape is in. Thus, this code provides the conversion from + the OASIS Open Office file format to the OpenOffice.org file format. (#i36248#) + */ + if ( !( GetExport().getExportFlags() & SvXMLExportFlags::OASIS ) && + xProps->getPropertySetInfo()->hasPropertyByName("StartPositionInHoriL2R") && + xProps->getPropertySetInfo()->hasPropertyByName("EndPositionInHoriL2R") ) + { + xProps->getPropertyValue("StartPositionInHoriL2R") >>= aStart; + xProps->getPropertyValue("EndPositionInHoriL2R") >>= aEnd; + } + else + { + xProps->getPropertyValue("StartPosition") >>= aStart; + xProps->getPropertyValue("EndPosition") >>= aEnd; + } + + if( pRefPoint ) + { + aStart.X -= pRefPoint->X; + aStart.Y -= pRefPoint->Y; + aEnd.X -= pRefPoint->X; + aEnd.Y -= pRefPoint->Y; + } + + if( nFeatures & XMLShapeExportFlags::X ) + { + // svg: x1 + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + aStart.X); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_X1, aStr); + } + else + { + aEnd.X -= aStart.X; + } + + if( nFeatures & XMLShapeExportFlags::Y ) + { + // svg: y1 + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + aStart.Y); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_Y1, aStr); + } + else + { + aEnd.Y -= aStart.Y; + } + + // svg: x2 + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, aEnd.X); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_X2, aStr); + + // svg: y2 + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, aEnd.Y); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_Y2, aStr); + + // #i39320# + uno::Reference< uno::XInterface > xRefS; + uno::Reference< uno::XInterface > xRefE; + + // export start connection + xProps->getPropertyValue("StartShape") >>= xRefS; + if( xRefS.is() ) + { + const OUString& rShapeId = mrExport.getInterfaceToIdentifierMapper().getIdentifier( xRefS ); + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_START_SHAPE, rShapeId); + + aAny = xProps->getPropertyValue("StartGluePointIndex"); + sal_Int32 nGluePointId = 0; + if( aAny >>= nGluePointId ) + { + if( nGluePointId != -1 ) + { + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_START_GLUE_POINT, OUString::number( nGluePointId )); + } + } + } + + // export end connection + xProps->getPropertyValue("EndShape") >>= xRefE; + if( xRefE.is() ) + { + const OUString& rShapeId = mrExport.getInterfaceToIdentifierMapper().getIdentifier( xRefE ); + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_END_SHAPE, rShapeId); + + aAny = xProps->getPropertyValue("EndGluePointIndex"); + sal_Int32 nGluePointId = 0; + if( aAny >>= nGluePointId ) + { + if( nGluePointId != -1 ) + { + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_END_GLUE_POINT, OUString::number( nGluePointId )); + } + } + } + + // get PolygonBezier + aAny = xProps->getPropertyValue("PolyPolygonBezier"); + auto pSourcePolyPolygon = o3tl::tryAccess<drawing::PolyPolygonBezierCoords>(aAny); + if(pSourcePolyPolygon && pSourcePolyPolygon->Coordinates.getLength()) + { + const basegfx::B2DPolyPolygon aPolyPolygon( + basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon( + *pSourcePolyPolygon)); + const OUString aPolygonString( + basegfx::utils::exportToSvgD( + aPolyPolygon, + true, // bUseRelativeCoordinates + false, // bDetectQuadraticBeziers: not used in old, but maybe activated now + true)); // bHandleRelativeNextPointCompatible + + // write point array + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_D, aPolygonString); + } + + // get matrix + ::basegfx::B2DHomMatrix aMatrix; + ImpExportNewTrans_GetB2DHomMatrix(aMatrix, xProps); + + // decompose and correct about pRefPoint + ::basegfx::B2DTuple aTRScale; + double fTRShear(0.0); + double fTRRotate(0.0); + ::basegfx::B2DTuple aTRTranslate; + ImpExportNewTrans_DecomposeAndRefPoint(aMatrix, aTRScale, fTRShear, + fTRRotate, aTRTranslate, pRefPoint); + + // fdo#49678: create and export ViewBox + awt::Size aSize(FRound(aTRScale.getX()), FRound(aTRScale.getY())); + SdXMLImExViewBox aViewBox(0, 0, aSize.Width, aSize.Height); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_VIEWBOX, aViewBox.GetExportString()); + + // write connector shape. Add Export later. + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DRAW, XML_CONNECTOR, bCreateNewline, true); + + ImpExportDescription( xShape ); // #i68101# + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + ImpExportText( xShape ); +} + +void XMLShapeExport::ImpExportMeasureShape( + const uno::Reference< drawing::XShape >& xShape, + XMLShapeExportFlags nFeatures /* = SEF_DEFAULT */, awt::Point const * pRefPoint /* = NULL */) +{ + uno::Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + + OUString aStr; + OUStringBuffer sStringBuffer; + + // export start and end point + awt::Point aStart(0,0); + awt::Point aEnd(1,1); + + /* Get <StartPositionInHoriL2R> and + <EndPositionInHoriL2R>, if they exist and if the document is exported + into the OpenOffice.org file format. + These properties only exist at service css::text::Shape - the + Writer UNO service for shapes. + This code is needed, because the positioning attributes in the + OpenOffice.org file format are given in horizontal left-to-right layout + regardless the layout direction the shape is in. In the OASIS Open Office + file format the positioning attributes are correctly given in the layout + direction the shape is in. Thus, this code provides the conversion from + the OASIS Open Office file format to the OpenOffice.org file format. (#i36248#) + */ + if ( !( GetExport().getExportFlags() & SvXMLExportFlags::OASIS ) && + xProps->getPropertySetInfo()->hasPropertyByName("StartPositionInHoriL2R") && + xProps->getPropertySetInfo()->hasPropertyByName("EndPositionInHoriL2R") ) + { + xProps->getPropertyValue("StartPositionInHoriL2R") >>= aStart; + xProps->getPropertyValue("EndPositionInHoriL2R") >>= aEnd; + } + else + { + xProps->getPropertyValue("StartPosition") >>= aStart; + xProps->getPropertyValue("EndPosition") >>= aEnd; + } + + if( pRefPoint ) + { + aStart.X -= pRefPoint->X; + aStart.Y -= pRefPoint->Y; + aEnd.X -= pRefPoint->X; + aEnd.Y -= pRefPoint->Y; + } + + if( nFeatures & XMLShapeExportFlags::X ) + { + // svg: x1 + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + aStart.X); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_X1, aStr); + } + else + { + aEnd.X -= aStart.X; + } + + if( nFeatures & XMLShapeExportFlags::Y ) + { + // svg: y1 + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + aStart.Y); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_Y1, aStr); + } + else + { + aEnd.Y -= aStart.Y; + } + + // svg: x2 + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, aEnd.X); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_X2, aStr); + + // svg: y2 + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, aEnd.Y); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_Y2, aStr); + + // write measure shape + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DRAW, XML_MEASURE, bCreateNewline, true); + + ImpExportDescription( xShape ); // #i68101# + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + + uno::Reference< text::XText > xText( xShape, uno::UNO_QUERY ); + if( xText.is() ) + mrExport.GetTextParagraphExport()->exportText( xText ); +} + +void XMLShapeExport::ImpExportOLE2Shape( + const uno::Reference< drawing::XShape >& xShape, + XmlShapeType eShapeType, XMLShapeExportFlags nFeatures /* = SEF_DEFAULT */, awt::Point* pRefPoint /* = NULL */, + comphelper::AttributeList* pAttrList /* = NULL */ ) +{ + uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + uno::Reference< container::XNamed > xNamed(xShape, uno::UNO_QUERY); + + SAL_WARN_IF( !xPropSet.is() || !xNamed.is(), "xmloff", "ole shape is not implementing needed interfaces"); + if(!(xPropSet.is() && xNamed.is())) + return; + + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + bool bIsEmptyPresObj = false; + + // presentation settings + if(eShapeType == XmlShapeType::PresOLE2Shape) + bIsEmptyPresObj = ImpExportPresentationAttributes( xPropSet, GetXMLToken(XML_OBJECT) ); + else if(eShapeType == XmlShapeType::PresChartShape) + bIsEmptyPresObj = ImpExportPresentationAttributes( xPropSet, GetXMLToken(XML_CHART) ); + else if(eShapeType == XmlShapeType::PresSheetShape) + bIsEmptyPresObj = ImpExportPresentationAttributes( xPropSet, GetXMLToken(XML_TABLE) ); + + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + bool bExportEmbedded(mrExport.getExportFlags() & SvXMLExportFlags::EMBEDDED); + OUString sPersistName; + SvXMLElementExport aElement( mrExport, XML_NAMESPACE_DRAW, + XML_FRAME, bCreateNewline, true ); + + if (!bIsEmptyPresObj) + { + if (pAttrList) + { + mrExport.AddAttributeList(pAttrList); + } + + OUString sClassId; + OUString sURL; + bool bInternal = false; + xPropSet->getPropertyValue("IsInternal") >>= bInternal; + + { + + if ( bInternal ) + { + // OOo internal links have no storage persistence, URL is stored in the XML file + // the result LinkURL is empty in case the object is not a link + xPropSet->getPropertyValue("LinkURL") >>= sURL; + } + + xPropSet->getPropertyValue("PersistName") >>= sPersistName; + if ( sURL.isEmpty() ) + { + if( !sPersistName.isEmpty() ) + { + sURL = "vnd.sun.star.EmbeddedObject:" + sPersistName; + } + } + + if( !bInternal ) + xPropSet->getPropertyValue("CLSID") >>= sClassId; + + if( !sClassId.isEmpty() ) + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_CLASS_ID, sClassId ); + + if(!bExportEmbedded) + { + // xlink:href + if( !sURL.isEmpty() ) + { + // #96717# in theorie, if we don't have a URL we shouldn't even + // export this OLE shape. But practically it's too risky right now + // to change this so we better dispose this on load + sURL = mrExport.AddEmbeddedObject( sURL ); + + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, sURL ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD ); + } + else + { + // tdf#153179 Export the preview graphic of the object if the object is missing. + uno::Reference<graphic::XGraphic> xGraphic; + xPropSet->getPropertyValue("Graphic") >>= xGraphic; + + if (xGraphic.is()) + { + OUString aMimeType; + const OUString aHref = mrExport.AddEmbeddedXGraphic(xGraphic, aMimeType); + + if (aMimeType.isEmpty()) + mrExport.GetGraphicMimeTypeFromStream(xGraphic, aMimeType); + + if (!aHref.isEmpty()) + { + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, aHref); + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE); + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED); + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD); + } + + if (!aMimeType.isEmpty() + && GetExport().getSaneDefaultVersion() > SvtSaveOptions::ODFSVER_012) + { // ODF 1.3 OFFICE-3943 + mrExport.AddAttribute(SvtSaveOptions::ODFSVER_013 + <= GetExport().getSaneDefaultVersion() + ? XML_NAMESPACE_DRAW + : XML_NAMESPACE_LO_EXT, + "mime-type", aMimeType); + } + + SvXMLElementExport aImageElem(mrExport, XML_NAMESPACE_DRAW, XML_IMAGE, true, + true); + + // optional office:binary-data + mrExport.AddEmbeddedXGraphicAsBase64(xGraphic); + + ImpExportEvents(xShape); + ImpExportGluePoints(xShape); + ImpExportDescription(xShape); + + return; + } + } + } + } + + enum XMLTokenEnum eElem = sClassId.isEmpty() ? XML_OBJECT : XML_OBJECT_OLE ; + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DRAW, eElem, true, true ); + + // tdf#112547 export text as child of draw:object, where import expects it + if (!bIsEmptyPresObj && supportsText(eShapeType)) + { + // #i118485# Add text export, the draw OLE shape allows text now + ImpExportText( xShape, TextPNS::EXTENSION ); + } + + if(bExportEmbedded && !bIsEmptyPresObj) + { + if(bInternal) + { + // embedded XML + uno::Reference< lang::XComponent > xComp; + xPropSet->getPropertyValue("Model") >>= xComp; + SAL_WARN_IF( !xComp.is(), "xmloff", "no xModel for own OLE format" ); + mrExport.ExportEmbeddedOwnObject( xComp ); + } + else + { + // embed as Base64 + // this is an alien object ( currently MSOLE is the only supported type of such objects ) + // in case it is not an OASIS format the object should be asked to store replacement image if possible + + OUString sURLRequest( sURL ); + if ( !( mrExport.getExportFlags() & SvXMLExportFlags::OASIS ) ) + sURLRequest += "?oasis=false"; + mrExport.AddEmbeddedObjectAsBase64( sURLRequest ); + } + } + } + if( !bIsEmptyPresObj ) + { + OUString sURL = XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE + sPersistName; + if( !bExportEmbedded ) + { + sURL = GetExport().AddEmbeddedObject( sURL ); + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, sURL ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD ); + } + + SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_DRAW, + XML_IMAGE, false, true ); + + if( bExportEmbedded ) + GetExport().AddEmbeddedObjectAsBase64( sURL ); + } + + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + ImpExportDescription( xShape ); // #i68101# + +} + +void XMLShapeExport::ImpExportPageShape( + const uno::Reference< drawing::XShape >& xShape, + XmlShapeType eShapeType, XMLShapeExportFlags nFeatures /* = SEF_DEFAULT */, awt::Point* pRefPoint /* = NULL */) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + // #86163# Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + // export page number used for this page + uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xPropSet->getPropertySetInfo() ); + static constexpr OUString aPageNumberStr(u"PageNumber"_ustr); + if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(aPageNumberStr)) + { + sal_Int32 nPageNumber = 0; + xPropSet->getPropertyValue(aPageNumberStr) >>= nPageNumber; + if( nPageNumber ) + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_PAGE_NUMBER, OUString::number(nPageNumber)); + } + + // a presentation page shape, normally used on notes pages only. If + // it is used not as presentation shape, it may have been created with + // copy-paste exchange between draw and impress (this IS possible...) + if(eShapeType == XmlShapeType::PresPageShape) + { + mrExport.AddAttribute(XML_NAMESPACE_PRESENTATION, XML_CLASS, + XML_PAGE); + } + + // write Page shape + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DRAW, XML_PAGE_THUMBNAIL, bCreateNewline, true); +} + +void XMLShapeExport::ImpExportCaptionShape( + const uno::Reference< drawing::XShape >& xShape, + XMLShapeExportFlags nFeatures /* = SEF_DEFAULT */, awt::Point* pRefPoint /* = NULL */) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + // evtl. corner radius? + sal_Int32 nCornerRadius(0); + xPropSet->getPropertyValue("CornerRadius") >>= nCornerRadius; + if(nCornerRadius) + { + OUStringBuffer sStringBuffer; + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + nCornerRadius); + mrExport.AddAttribute(XML_NAMESPACE_DRAW, XML_CORNER_RADIUS, sStringBuffer.makeStringAndClear()); + } + + awt::Point aCaptionPoint; + xPropSet->getPropertyValue("CaptionPoint") >>= aCaptionPoint; + + mrExport.GetMM100UnitConverter().convertMeasureToXML(msBuffer, + aCaptionPoint.X); + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CAPTION_POINT_X, msBuffer.makeStringAndClear() ); + mrExport.GetMM100UnitConverter().convertMeasureToXML(msBuffer, + aCaptionPoint.Y); + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CAPTION_POINT_Y, msBuffer.makeStringAndClear() ); + + // write Caption shape. Add export later. + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + bool bAnnotation( (nFeatures & XMLShapeExportFlags::ANNOTATION) == XMLShapeExportFlags::ANNOTATION ); + + SvXMLElementExport aObj( mrExport, + (bAnnotation ? XML_NAMESPACE_OFFICE + : XML_NAMESPACE_DRAW), + (bAnnotation ? XML_ANNOTATION : XML_CAPTION), + bCreateNewline, true ); + + ImpExportDescription( xShape ); // #i68101# + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + if( bAnnotation ) + mrExport.exportAnnotationMeta( xShape ); + ImpExportText( xShape ); + +} + +void XMLShapeExport::ImpExportFrameShape( + const uno::Reference< drawing::XShape >& xShape, + XMLShapeExportFlags nFeatures, css::awt::Point* pRefPoint) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DRAW, + XML_FRAME, bCreateNewline, true ); + + // export frame url + OUString aStr; + xPropSet->getPropertyValue("FrameURL") >>= aStr; + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_HREF, GetExport().GetRelativeReference(aStr) ); + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED ); + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD ); + + // export name + xPropSet->getPropertyValue("FrameName") >>= aStr; + if( !aStr.isEmpty() ) + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_FRAME_NAME, aStr ); + + // write floating frame + { + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DRAW, XML_FLOATING_FRAME, true, true); + } + + ImpExportDescription(xShape); +} + +void XMLShapeExport::ImpExportAppletShape( + const uno::Reference< drawing::XShape >& xShape, + XMLShapeExportFlags nFeatures, css::awt::Point* pRefPoint) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aElement( mrExport, XML_NAMESPACE_DRAW, + XML_FRAME, bCreateNewline, true ); + + // export frame url + OUString aStr; + xPropSet->getPropertyValue("AppletCodeBase") >>= aStr; + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_HREF, GetExport().GetRelativeReference(aStr) ); + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED ); + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD ); + + // export draw:applet-name + xPropSet->getPropertyValue("AppletName") >>= aStr; + if( !aStr.isEmpty() ) + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_APPLET_NAME, aStr ); + + // export draw:code + xPropSet->getPropertyValue("AppletCode") >>= aStr; + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CODE, aStr ); + + // export draw:may-script + bool bIsScript = false; + xPropSet->getPropertyValue("AppletIsScript") >>= bIsScript; + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_MAY_SCRIPT, bIsScript ? XML_TRUE : XML_FALSE ); + + { + // write applet + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DRAW, XML_APPLET, true, true); + + // export parameters + uno::Sequence< beans::PropertyValue > aCommands; + xPropSet->getPropertyValue("AppletCommands") >>= aCommands; + for( const auto& rCommand : std::as_const(aCommands) ) + { + rCommand.Value >>= aStr; + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, rCommand.Name ); + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_VALUE, aStr ); + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DRAW, XML_PARAM, false, true ); + } + } + + ImpExportDescription(xShape); +} + +void XMLShapeExport::ImpExportPluginShape( + const uno::Reference< drawing::XShape >& xShape, + XMLShapeExportFlags nFeatures, css::awt::Point* pRefPoint) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aElement( mrExport, XML_NAMESPACE_DRAW, + XML_FRAME, bCreateNewline, true ); + + // export plugin url + OUString aStr; + xPropSet->getPropertyValue("PluginURL") >>= aStr; + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_HREF, GetExport().GetRelativeReference(aStr) ); + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED ); + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD ); + + // export mime-type + xPropSet->getPropertyValue("PluginMimeType") >>= aStr; + if(!aStr.isEmpty()) + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_MIME_TYPE, aStr ); + + { + // write plugin + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DRAW, XML_PLUGIN, true, true); + + // export parameters + uno::Sequence< beans::PropertyValue > aCommands; + xPropSet->getPropertyValue("PluginCommands") >>= aCommands; + for( const auto& rCommand : std::as_const(aCommands) ) + { + rCommand.Value >>= aStr; + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, rCommand.Name ); + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_VALUE, aStr ); + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DRAW, XML_PARAM, false, true ); + } + } + + ImpExportDescription(xShape); +} + +static void lcl_CopyStream( + uno::Reference<io::XInputStream> const& xInStream, + uno::Reference<embed::XStorage> const& xTarget, + OUString const& rPath, const OUString& rMimeType) +{ + ::comphelper::LifecycleProxy proxy; + uno::Reference<io::XStream> const xStream( + ::comphelper::OStorageHelper::GetStreamAtPackageURL(xTarget, rPath, + embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE, proxy)); + uno::Reference<io::XOutputStream> const xOutStream( + (xStream.is()) ? xStream->getOutputStream() : nullptr); + if (!xOutStream.is()) + { + SAL_WARN("xmloff", "no output stream"); + throw uno::Exception("no output stream",nullptr); + } + uno::Reference< beans::XPropertySet > const xStreamProps(xStream, + uno::UNO_QUERY); + if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage + xStreamProps->setPropertyValue("MediaType", + uno::Any(rMimeType)); + xStreamProps->setPropertyValue( // turn off compression + "Compressed", + uno::Any(false)); + } + ::comphelper::OStorageHelper::CopyInputToOutput(xInStream, xOutStream); + xOutStream->closeOutput(); + proxy.commitStorages(); +} + +static OUString +lcl_StoreMediaAndGetURL(SvXMLExport & rExport, + uno::Reference<beans::XPropertySet> const& xPropSet, + OUString const& rURL, const OUString& rMimeType) +{ + OUString urlPath; + if (rURL.startsWithIgnoreAsciiCase("vnd.sun.star.Package:", &urlPath)) + { + try // video is embedded + { + uno::Reference<embed::XStorage> const xTarget( + rExport.GetTargetStorage(), uno::UNO_SET_THROW); + uno::Reference<io::XInputStream> xInStream; + xPropSet->getPropertyValue("PrivateStream") + >>= xInStream; + + if (!xInStream.is()) + { + SAL_WARN("xmloff", "no input stream"); + return OUString(); + } + + lcl_CopyStream(xInStream, xTarget, rURL, rMimeType); + + return urlPath; + } + catch (uno::Exception const&) + { + TOOLS_INFO_EXCEPTION("xmloff", "exception while storing embedded media"); + } + return OUString(); + } + else + { + return rExport.GetRelativeReference(rURL); // linked + } +} + +namespace +{ +void ExportGraphicPreview(const uno::Reference<graphic::XGraphic>& xGraphic, SvXMLExport& rExport, const std::u16string_view& rPrefix, const std::u16string_view& rExtension, const OUString& rMimeType) +{ + const bool bExportEmbedded(rExport.getExportFlags() & SvXMLExportFlags::EMBEDDED); + + if( xGraphic.is() ) try + { + uno::Reference< uno::XComponentContext > xContext = rExport.getComponentContext(); + + uno::Reference< embed::XStorage > xPictureStorage; + uno::Reference< embed::XStorage > xStorage; + uno::Reference< io::XStream > xPictureStream; + + OUString sPictureName; + if( bExportEmbedded ) + { + xPictureStream.set( xContext->getServiceManager()->createInstanceWithContext( "com.sun.star.comp.MemoryStream", xContext), uno::UNO_QUERY_THROW ); + } + else + { + xStorage.set( rExport.GetTargetStorage(), uno::UNO_SET_THROW ); + + xPictureStorage.set( xStorage->openStorageElement( "Pictures" , ::embed::ElementModes::READWRITE ), uno::UNO_SET_THROW ); + + sal_Int32 nIndex = 0; + do + { + sPictureName = rPrefix + OUString::number( ++nIndex ) + rExtension; + } + while( xPictureStorage->hasByName( sPictureName ) ); + + xPictureStream.set( xPictureStorage->openStreamElement( sPictureName, ::embed::ElementModes::READWRITE ), uno::UNO_SET_THROW ); + } + + uno::Reference< graphic::XGraphicProvider > xProvider( graphic::GraphicProvider::create(xContext) ); + uno::Sequence< beans::PropertyValue > aArgs{ + comphelper::makePropertyValue("MimeType", rMimeType ), + comphelper::makePropertyValue("OutputStream", xPictureStream->getOutputStream()) + }; + xProvider->storeGraphic( xGraphic, aArgs ); + + if( xPictureStorage.is() ) + { + uno::Reference< embed::XTransactedObject > xTrans( xPictureStorage, uno::UNO_QUERY ); + if( xTrans.is() ) + xTrans->commit(); + } + + if( !bExportEmbedded ) + { + OUString sURL = "Pictures/" + sPictureName; + rExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sURL ); + rExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + rExport.AddAttribute( XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED ); + rExport.AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD ); + } + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_DRAW, XML_IMAGE, false, true ); + + if( bExportEmbedded ) + { + uno::Reference< io::XSeekableInputStream > xSeekable( xPictureStream, uno::UNO_QUERY_THROW ); + xSeekable->seek(0); + + XMLBase64Export aBase64Exp( rExport ); + aBase64Exp.exportOfficeBinaryDataElement( uno::Reference < io::XInputStream >( xPictureStream, uno::UNO_QUERY_THROW ) ); + } + } + catch( uno::Exception const & ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.draw"); + } +} +} + +void XMLShapeExport::ImpExportMediaShape( + const uno::Reference< drawing::XShape >& xShape, + XmlShapeType eShapeType, XMLShapeExportFlags nFeatures, css::awt::Point* pRefPoint) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + if(eShapeType == XmlShapeType::PresMediaShape) + { + (void)ImpExportPresentationAttributes( xPropSet, GetXMLToken(XML_OBJECT) ); + } + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DRAW, + XML_FRAME, bCreateNewline, true ); + + // export media url + OUString aMediaURL; + xPropSet->getPropertyValue("MediaURL") >>= aMediaURL; + OUString sMimeType; + xPropSet->getPropertyValue("MediaMimeType") >>= sMimeType; + + OUString const persistentURL = + lcl_StoreMediaAndGetURL(GetExport(), xPropSet, aMediaURL, sMimeType); + + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_HREF, persistentURL ); + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED ); + mrExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD ); + + // export mime-type + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_MIME_TYPE, sMimeType ); + + // write plugin + auto pPluginOBJ = std::make_unique<SvXMLElementExport>(mrExport, XML_NAMESPACE_DRAW, XML_PLUGIN, !( nFeatures & XMLShapeExportFlags::NO_WS ), true); + + // export parameters + static constexpr OUString aFalseStr( u"false"_ustr ); + static constexpr OUString aTrueStr( u"true"_ustr ); + + bool bLoop = false; + static constexpr OUString aLoopStr( u"Loop"_ustr ); + xPropSet->getPropertyValue( aLoopStr ) >>= bLoop; + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, aLoopStr ); + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_VALUE, bLoop ? aTrueStr : aFalseStr ); + delete new SvXMLElementExport( mrExport, XML_NAMESPACE_DRAW, XML_PARAM, false, true ); + + bool bMute = false; + static constexpr OUString aMuteStr( u"Mute"_ustr ); + xPropSet->getPropertyValue( aMuteStr ) >>= bMute; + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, aMuteStr ); + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_VALUE, bMute ? aTrueStr : aFalseStr ); + delete new SvXMLElementExport( mrExport, XML_NAMESPACE_DRAW, XML_PARAM, false, true ); + + sal_Int16 nVolumeDB = 0; + xPropSet->getPropertyValue("VolumeDB") >>= nVolumeDB; + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, "VolumeDB" ); + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_VALUE, OUString::number( nVolumeDB ) ); + delete new SvXMLElementExport( mrExport, XML_NAMESPACE_DRAW, XML_PARAM, false, true ); + + media::ZoomLevel eZoom; + OUString aZoomValue; + xPropSet->getPropertyValue("Zoom") >>= eZoom; + switch( eZoom ) + { + case media::ZoomLevel_ZOOM_1_TO_4 : aZoomValue = "25%"; break; + case media::ZoomLevel_ZOOM_1_TO_2 : aZoomValue = "50%"; break; + case media::ZoomLevel_ORIGINAL : aZoomValue = "100%"; break; + case media::ZoomLevel_ZOOM_2_TO_1 : aZoomValue = "200%"; break; + case media::ZoomLevel_ZOOM_4_TO_1 : aZoomValue = "400%"; break; + case media::ZoomLevel_FIT_TO_WINDOW: aZoomValue = "fit"; break; + case media::ZoomLevel_FIT_TO_WINDOW_FIXED_ASPECT: aZoomValue = "fixedfit"; break; + case media::ZoomLevel_FULLSCREEN : aZoomValue = "fullscreen"; break; + + default: + break; + } + + if( !aZoomValue.isEmpty() ) + { + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, "Zoom" ); + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_VALUE, aZoomValue ); + delete new SvXMLElementExport( mrExport, XML_NAMESPACE_DRAW, XML_PARAM, false, true ); + } + + pPluginOBJ.reset(); + + uno::Reference<graphic::XGraphic> xGraphic; + xPropSet->getPropertyValue("Graphic") >>= xGraphic; + Graphic aGraphic(xGraphic); + if (!aGraphic.IsNone()) + { + // The media has a preview, export it. + ExportGraphicPreview(xGraphic, mrExport, u"MediaPreview", u".png", "image/png"); + } + + ImpExportDescription(xShape); +} + +void XMLShapeExport::ImpExport3DSceneShape( const uno::Reference< drawing::XShape >& xShape, XMLShapeExportFlags nFeatures, awt::Point* pRefPoint) +{ + uno::Reference< drawing::XShapes > xShapes(xShape, uno::UNO_QUERY); + if(!(xShapes.is() && xShapes->getCount())) + return; + + uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY ); + SAL_WARN_IF( !xPropSet.is(), "xmloff", "XMLShapeExport::ImpExport3DSceneShape can't export a scene without a propertyset" ); + if( !xPropSet.is() ) + return; + + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + // 3d attributes + export3DSceneAttributes( xPropSet ); + + // write 3DScene shape + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aOBJ( mrExport, XML_NAMESPACE_DR3D, XML_SCENE, bCreateNewline, true); + + ImpExportDescription( xShape ); // #i68101# + ImpExportEvents( xShape ); + + // write 3DSceneLights + export3DLamps( xPropSet ); + + // #89764# if export of position is suppressed for group shape, + // positions of contained objects should be written relative to + // the upper left edge of the group. + awt::Point aUpperLeft; + + if(!(nFeatures & XMLShapeExportFlags::POSITION)) + { + nFeatures |= XMLShapeExportFlags::POSITION; + aUpperLeft = xShape->getPosition(); + pRefPoint = &aUpperLeft; + } + + // write members + exportShapes( xShapes, nFeatures, pRefPoint ); +} + +void XMLShapeExport::ImpExport3DShape( + const uno::Reference< drawing::XShape >& xShape, + XmlShapeType eShapeType) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if(!xPropSet.is()) + return; + + OUString aStr; + OUStringBuffer sStringBuffer; + + // transformation (UNO_NAME_3D_TRANSFORM_MATRIX == "D3DTransformMatrix") + uno::Any aAny = xPropSet->getPropertyValue("D3DTransformMatrix"); + drawing::HomogenMatrix aHomMat; + aAny >>= aHomMat; + SdXMLImExTransform3D aTransform; + aTransform.AddHomogenMatrix(aHomMat); + if(aTransform.NeedsAction()) + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_TRANSFORM, aTransform.GetExportString(mrExport.GetMM100UnitConverter())); + + switch(eShapeType) + { + case XmlShapeType::Draw3DCubeObject: + { + // minEdge + aAny = xPropSet->getPropertyValue("D3DPosition"); + drawing::Position3D aPosition3D; + aAny >>= aPosition3D; + ::basegfx::B3DVector aPos3D(aPosition3D.PositionX, aPosition3D.PositionY, aPosition3D.PositionZ); + + // maxEdge + aAny = xPropSet->getPropertyValue("D3DSize"); + drawing::Direction3D aDirection3D; + aAny >>= aDirection3D; + ::basegfx::B3DVector aDir3D(aDirection3D.DirectionX, aDirection3D.DirectionY, aDirection3D.DirectionZ); + + // transform maxEdge from distance to pos + aDir3D = aPos3D + aDir3D; + + // write minEdge + if(aPos3D != ::basegfx::B3DVector(-2500.0, -2500.0, -2500.0)) // write only when not default + { + SvXMLUnitConverter::convertB3DVector(sStringBuffer, aPos3D); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_MIN_EDGE, aStr); + } + + // write maxEdge + if(aDir3D != ::basegfx::B3DVector(2500.0, 2500.0, 2500.0)) // write only when not default + { + SvXMLUnitConverter::convertB3DVector(sStringBuffer, aDir3D); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_MAX_EDGE, aStr); + } + + // write 3DCube shape + // #i123542# Do this *after* the attributes are added, else these will be lost since opening + // the scope will clear the global attribute list at the exporter + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DR3D, XML_CUBE, true, true); + + break; + } + case XmlShapeType::Draw3DSphereObject: + { + // Center + aAny = xPropSet->getPropertyValue("D3DPosition"); + drawing::Position3D aPosition3D; + aAny >>= aPosition3D; + ::basegfx::B3DVector aPos3D(aPosition3D.PositionX, aPosition3D.PositionY, aPosition3D.PositionZ); + + // Size + aAny = xPropSet->getPropertyValue("D3DSize"); + drawing::Direction3D aDirection3D; + aAny >>= aDirection3D; + ::basegfx::B3DVector aDir3D(aDirection3D.DirectionX, aDirection3D.DirectionY, aDirection3D.DirectionZ); + + // write Center + if(aPos3D != ::basegfx::B3DVector(0.0, 0.0, 0.0)) // write only when not default + { + SvXMLUnitConverter::convertB3DVector(sStringBuffer, aPos3D); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_CENTER, aStr); + } + + // write Size + if(aDir3D != ::basegfx::B3DVector(5000.0, 5000.0, 5000.0)) // write only when not default + { + SvXMLUnitConverter::convertB3DVector(sStringBuffer, aDir3D); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_SIZE, aStr); + } + + // write 3DSphere shape + // #i123542# Do this *after* the attributes are added, else these will be lost since opening + // the scope will clear the global attribute list at the exporter + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DR3D, XML_SPHERE, true, true); + + break; + } + case XmlShapeType::Draw3DLatheObject: + case XmlShapeType::Draw3DExtrudeObject: + { + // write special 3DLathe/3DExtrude attributes, get 3D tools::PolyPolygon as drawing::PolyPolygonShape3D + aAny = xPropSet->getPropertyValue("D3DPolyPolygon3D"); + drawing::PolyPolygonShape3D aUnoPolyPolygon3D; + aAny >>= aUnoPolyPolygon3D; + + // convert to 3D PolyPolygon + const basegfx::B3DPolyPolygon aPolyPolygon3D( + basegfx::utils::UnoPolyPolygonShape3DToB3DPolyPolygon( + aUnoPolyPolygon3D)); + + // convert to 2D tools::PolyPolygon using identity 3D transformation (just grep X and Y) + const basegfx::B3DHomMatrix aB3DHomMatrixFor2DConversion; + const basegfx::B2DPolyPolygon aPolyPolygon( + basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon( + aPolyPolygon3D, + aB3DHomMatrixFor2DConversion)); + + // get 2D range of it + const basegfx::B2DRange aPolyPolygonRange(aPolyPolygon.getB2DRange()); + + // export ViewBox + SdXMLImExViewBox aViewBox( + aPolyPolygonRange.getMinX(), + aPolyPolygonRange.getMinY(), + aPolyPolygonRange.getWidth(), + aPolyPolygonRange.getHeight()); + + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_VIEWBOX, aViewBox.GetExportString()); + + // prepare svg:d string + const OUString aPolygonString( + basegfx::utils::exportToSvgD( + aPolyPolygon, + true, // bUseRelativeCoordinates + false, // bDetectQuadraticBeziers TTTT: not used in old, but maybe activated now + true)); // bHandleRelativeNextPointCompatible + + // write point array + mrExport.AddAttribute(XML_NAMESPACE_SVG, XML_D, aPolygonString); + + if(eShapeType == XmlShapeType::Draw3DLatheObject) + { + // write 3DLathe shape + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DR3D, XML_ROTATE, true, true); + } + else + { + // write 3DExtrude shape + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DR3D, XML_EXTRUDE, true, true); + } + break; + } + default: + break; + } +} + +/** helper for chart that adds all attributes of a 3d scene element to the export */ +void XMLShapeExport::export3DSceneAttributes( const css::uno::Reference< css::beans::XPropertySet >& xPropSet ) +{ + OUString aStr; + OUStringBuffer sStringBuffer; + + // world transformation (UNO_NAME_3D_TRANSFORM_MATRIX == "D3DTransformMatrix") + uno::Any aAny = xPropSet->getPropertyValue("D3DTransformMatrix"); + drawing::HomogenMatrix aHomMat; + aAny >>= aHomMat; + SdXMLImExTransform3D aTransform; + aTransform.AddHomogenMatrix(aHomMat); + if(aTransform.NeedsAction()) + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_TRANSFORM, aTransform.GetExportString(mrExport.GetMM100UnitConverter())); + + // VRP, VPN, VUP + aAny = xPropSet->getPropertyValue("D3DCameraGeometry"); + drawing::CameraGeometry aCamGeo; + aAny >>= aCamGeo; + + ::basegfx::B3DVector aVRP(aCamGeo.vrp.PositionX, aCamGeo.vrp.PositionY, aCamGeo.vrp.PositionZ); + if(aVRP != ::basegfx::B3DVector(0.0, 0.0, 1.0)) // write only when not default + { + SvXMLUnitConverter::convertB3DVector(sStringBuffer, aVRP); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_VRP, aStr); + } + + ::basegfx::B3DVector aVPN(aCamGeo.vpn.DirectionX, aCamGeo.vpn.DirectionY, aCamGeo.vpn.DirectionZ); + if(aVPN != ::basegfx::B3DVector(0.0, 0.0, 1.0)) // write only when not default + { + SvXMLUnitConverter::convertB3DVector(sStringBuffer, aVPN); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_VPN, aStr); + } + + ::basegfx::B3DVector aVUP(aCamGeo.vup.DirectionX, aCamGeo.vup.DirectionY, aCamGeo.vup.DirectionZ); + if(aVUP != ::basegfx::B3DVector(0.0, 1.0, 0.0)) // write only when not default + { + SvXMLUnitConverter::convertB3DVector(sStringBuffer, aVUP); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_VUP, aStr); + } + + // projection "D3DScenePerspective" drawing::ProjectionMode + aAny = xPropSet->getPropertyValue("D3DScenePerspective"); + drawing::ProjectionMode aPrjMode; + aAny >>= aPrjMode; + if(aPrjMode == drawing::ProjectionMode_PARALLEL) + aStr = GetXMLToken(XML_PARALLEL); + else + aStr = GetXMLToken(XML_PERSPECTIVE); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_PROJECTION, aStr); + + // distance + aAny = xPropSet->getPropertyValue("D3DSceneDistance"); + sal_Int32 nDistance = 0; + aAny >>= nDistance; + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + nDistance); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_DISTANCE, aStr); + + // focalLength + aAny = xPropSet->getPropertyValue("D3DSceneFocalLength"); + sal_Int32 nFocalLength = 0; + aAny >>= nFocalLength; + mrExport.GetMM100UnitConverter().convertMeasureToXML(sStringBuffer, + nFocalLength); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_FOCAL_LENGTH, aStr); + + // shadowSlant + aAny = xPropSet->getPropertyValue("D3DSceneShadowSlant"); + sal_Int16 nShadowSlant = 0; + aAny >>= nShadowSlant; + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_SHADOW_SLANT, OUString::number(static_cast<sal_Int32>(nShadowSlant))); + + // shadeMode + aAny = xPropSet->getPropertyValue("D3DSceneShadeMode"); + drawing::ShadeMode aShadeMode; + if(aAny >>= aShadeMode) + { + if(aShadeMode == drawing::ShadeMode_FLAT) + aStr = GetXMLToken(XML_FLAT); + else if(aShadeMode == drawing::ShadeMode_PHONG) + aStr = GetXMLToken(XML_PHONG); + else if(aShadeMode == drawing::ShadeMode_SMOOTH) + aStr = GetXMLToken(XML_GOURAUD); + else + aStr = GetXMLToken(XML_DRAFT); + } + else + { + // ShadeMode enum not there, write default + aStr = GetXMLToken(XML_GOURAUD); + } + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_SHADE_MODE, aStr); + + // ambientColor + aAny = xPropSet->getPropertyValue("D3DSceneAmbientColor"); + sal_Int32 nAmbientColor = 0; + aAny >>= nAmbientColor; + ::sax::Converter::convertColor(sStringBuffer, nAmbientColor); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_AMBIENT_COLOR, aStr); + + // lightingMode + aAny = xPropSet->getPropertyValue("D3DSceneTwoSidedLighting"); + bool bTwoSidedLighting = false; + aAny >>= bTwoSidedLighting; + ::sax::Converter::convertBool(sStringBuffer, bTwoSidedLighting); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_LIGHTING_MODE, aStr); +} + +/** helper for chart that exports all lamps from the propertyset */ +void XMLShapeExport::export3DLamps( const css::uno::Reference< css::beans::XPropertySet >& xPropSet ) +{ + // write lamps 1..8 as content + OUString aStr; + OUStringBuffer sStringBuffer; + + static constexpr OUStringLiteral aColorPropName(u"D3DSceneLightColor"); + static constexpr OUStringLiteral aDirectionPropName(u"D3DSceneLightDirection"); + static constexpr OUStringLiteral aLightOnPropName(u"D3DSceneLightOn"); + + ::basegfx::B3DVector aLightDirection; + drawing::Direction3D aLightDir; + bool bLightOnOff = false; + for(sal_Int32 nLamp = 1; nLamp <= 8; nLamp++) + { + OUString aIndexStr = OUString::number( nLamp ); + + // lightcolor + OUString aPropName = aColorPropName + aIndexStr; + sal_Int32 nLightColor = 0; + xPropSet->getPropertyValue( aPropName ) >>= nLightColor; + ::sax::Converter::convertColor(sStringBuffer, nLightColor); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_DIFFUSE_COLOR, aStr); + + // lightdirection + aPropName = aDirectionPropName + aIndexStr; + xPropSet->getPropertyValue(aPropName) >>= aLightDir; + aLightDirection = ::basegfx::B3DVector(aLightDir.DirectionX, aLightDir.DirectionY, aLightDir.DirectionZ); + SvXMLUnitConverter::convertB3DVector(sStringBuffer, aLightDirection); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_DIRECTION, aStr); + + // lighton + aPropName = aLightOnPropName + aIndexStr; + xPropSet->getPropertyValue(aPropName) >>= bLightOnOff; + ::sax::Converter::convertBool(sStringBuffer, bLightOnOff); + aStr = sStringBuffer.makeStringAndClear(); + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_ENABLED, aStr); + + // specular + mrExport.AddAttribute(XML_NAMESPACE_DR3D, XML_SPECULAR, + nLamp == 1 ? XML_TRUE : XML_FALSE); + + // write light entry + SvXMLElementExport aOBJ(mrExport, XML_NAMESPACE_DR3D, XML_LIGHT, true, true); + } +} + + +// using namespace css::io; +// using namespace ::xmloff::EnhancedCustomShapeToken; + + +static void ExportParameter( OUStringBuffer& rStrBuffer, const css::drawing::EnhancedCustomShapeParameter& rParameter ) +{ + if ( !rStrBuffer.isEmpty() ) + rStrBuffer.append( ' ' ); + if ( rParameter.Value.getValueTypeClass() == uno::TypeClass_DOUBLE ) + { + double fNumber = 0.0; + rParameter.Value >>= fNumber; + ::rtl::math::doubleToUStringBuffer( rStrBuffer, fNumber, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true ); + } + else + { + sal_Int32 nValue = 0; + rParameter.Value >>= nValue; + + switch( rParameter.Type ) + { + case css::drawing::EnhancedCustomShapeParameterType::EQUATION : + { + rStrBuffer.append( "?f" + OUString::number( nValue ) ); + } + break; + + case css::drawing::EnhancedCustomShapeParameterType::ADJUSTMENT : + { + rStrBuffer.append( '$' ); + rStrBuffer.append( nValue ); + } + break; + + case css::drawing::EnhancedCustomShapeParameterType::BOTTOM : + rStrBuffer.append( GetXMLToken( XML_BOTTOM ) ); break; + case css::drawing::EnhancedCustomShapeParameterType::RIGHT : + rStrBuffer.append( GetXMLToken( XML_RIGHT ) ); break; + case css::drawing::EnhancedCustomShapeParameterType::TOP : + rStrBuffer.append( GetXMLToken( XML_TOP ) ); break; + case css::drawing::EnhancedCustomShapeParameterType::LEFT : + rStrBuffer.append( GetXMLToken( XML_LEFT ) ); break; + case css::drawing::EnhancedCustomShapeParameterType::XSTRETCH : + rStrBuffer.append( GetXMLToken( XML_XSTRETCH ) ); break; + case css::drawing::EnhancedCustomShapeParameterType::YSTRETCH : + rStrBuffer.append( GetXMLToken( XML_YSTRETCH ) ); break; + case css::drawing::EnhancedCustomShapeParameterType::HASSTROKE : + rStrBuffer.append( GetXMLToken( XML_HASSTROKE ) ); break; + case css::drawing::EnhancedCustomShapeParameterType::HASFILL : + rStrBuffer.append( GetXMLToken( XML_HASFILL ) ); break; + case css::drawing::EnhancedCustomShapeParameterType::WIDTH : + rStrBuffer.append( GetXMLToken( XML_WIDTH ) ); break; + case css::drawing::EnhancedCustomShapeParameterType::HEIGHT : + rStrBuffer.append( GetXMLToken( XML_HEIGHT ) ); break; + case css::drawing::EnhancedCustomShapeParameterType::LOGWIDTH : + rStrBuffer.append( GetXMLToken( XML_LOGWIDTH ) ); break; + case css::drawing::EnhancedCustomShapeParameterType::LOGHEIGHT : + rStrBuffer.append( GetXMLToken( XML_LOGHEIGHT ) ); break; + default : + rStrBuffer.append( nValue ); + } + } +} + +static void ImpExportEquations( SvXMLExport& rExport, const uno::Sequence< OUString >& rEquations ) +{ + sal_Int32 i; + for ( i = 0; i < rEquations.getLength(); i++ ) + { + OUString aStr= "f" + OUString::number( i ); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, aStr ); + + aStr = rEquations[ i ]; + sal_Int32 nIndex = 0; + do + { + nIndex = aStr.indexOf( '?', nIndex ); + if ( nIndex != -1 ) + { + aStr = OUString::Concat(aStr.subView(0, nIndex + 1)) + "f" + + aStr.subView(nIndex + 1, aStr.getLength() - nIndex - 1); + nIndex++; + } + } while( nIndex != -1 ); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_FORMULA, aStr ); + SvXMLElementExport aOBJ( rExport, XML_NAMESPACE_DRAW, XML_EQUATION, true, true ); + } +} + +static void ImpExportHandles( SvXMLExport& rExport, const uno::Sequence< beans::PropertyValues >& rHandles ) +{ + if ( !rHandles.hasElements() ) + return; + + OUString aStr; + OUStringBuffer aStrBuffer; + + for ( const uno::Sequence< beans::PropertyValue >& rPropSeq : rHandles ) + { + bool bPosition = false; + for ( const beans::PropertyValue& rPropVal : rPropSeq ) + { + switch( EASGet( rPropVal.Name ) ) + { + case EAS_Position : + { + css::drawing::EnhancedCustomShapeParameterPair aPosition; + if ( rPropVal.Value >>= aPosition ) + { + ExportParameter( aStrBuffer, aPosition.First ); + ExportParameter( aStrBuffer, aPosition.Second ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_HANDLE_POSITION, aStr ); + bPosition = true; + } + } + break; + case EAS_MirroredX : + { + bool bMirroredX; + if ( rPropVal.Value >>= bMirroredX ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_HANDLE_MIRROR_HORIZONTAL, + bMirroredX ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_MirroredY : + { + bool bMirroredY; + if ( rPropVal.Value >>= bMirroredY ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_HANDLE_MIRROR_VERTICAL, + bMirroredY ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_Switched : + { + bool bSwitched; + if ( rPropVal.Value >>= bSwitched ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_HANDLE_SWITCHED, + bSwitched ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_Polar : + { + css::drawing::EnhancedCustomShapeParameterPair aPolar; + if ( rPropVal.Value >>= aPolar ) + { + ExportParameter( aStrBuffer, aPolar.First ); + ExportParameter( aStrBuffer, aPolar.Second ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_HANDLE_POLAR, aStr ); + } + } + break; + case EAS_RadiusRangeMinimum : + { + css::drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum; + if ( rPropVal.Value >>= aRadiusRangeMinimum ) + { + ExportParameter( aStrBuffer, aRadiusRangeMinimum ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_HANDLE_RADIUS_RANGE_MINIMUM, aStr ); + } + } + break; + case EAS_RadiusRangeMaximum : + { + css::drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum; + if ( rPropVal.Value >>= aRadiusRangeMaximum ) + { + ExportParameter( aStrBuffer, aRadiusRangeMaximum ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_HANDLE_RADIUS_RANGE_MAXIMUM, aStr ); + } + } + break; + case EAS_RangeXMinimum : + { + css::drawing::EnhancedCustomShapeParameter aXRangeMinimum; + if ( rPropVal.Value >>= aXRangeMinimum ) + { + ExportParameter( aStrBuffer, aXRangeMinimum ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_HANDLE_RANGE_X_MINIMUM, aStr ); + } + } + break; + case EAS_RangeXMaximum : + { + css::drawing::EnhancedCustomShapeParameter aXRangeMaximum; + if ( rPropVal.Value >>= aXRangeMaximum ) + { + ExportParameter( aStrBuffer, aXRangeMaximum ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_HANDLE_RANGE_X_MAXIMUM, aStr ); + } + } + break; + case EAS_RangeYMinimum : + { + css::drawing::EnhancedCustomShapeParameter aYRangeMinimum; + if ( rPropVal.Value >>= aYRangeMinimum ) + { + ExportParameter( aStrBuffer, aYRangeMinimum ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_HANDLE_RANGE_Y_MINIMUM, aStr ); + } + } + break; + case EAS_RangeYMaximum : + { + css::drawing::EnhancedCustomShapeParameter aYRangeMaximum; + if ( rPropVal.Value >>= aYRangeMaximum ) + { + ExportParameter( aStrBuffer, aYRangeMaximum ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_HANDLE_RANGE_Y_MAXIMUM, aStr ); + } + } + break; + default: + break; + } + } + if ( bPosition ) + SvXMLElementExport aOBJ( rExport, XML_NAMESPACE_DRAW, XML_HANDLE, true, true ); + else + rExport.ClearAttrList(); + } +} + +static void ImpExportEnhancedPath( SvXMLExport& rExport, + const uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair >& rCoordinates, + const uno::Sequence< css::drawing::EnhancedCustomShapeSegment >& rSegments, + bool bExtended = false ) +{ + + OUString aStr; + OUStringBuffer aStrBuffer; + bool bNeedExtended = false; + + sal_Int32 i, j, k, l; + + sal_Int32 nCoords = rCoordinates.getLength(); + sal_Int32 nSegments = rSegments.getLength(); + bool bSimpleSegments = nSegments == 0; + if ( bSimpleSegments ) + nSegments = 4; + for ( j = i = 0; j < nSegments; j++ ) + { + css::drawing::EnhancedCustomShapeSegment aSegment; + if ( bSimpleSegments ) + { + // if there are not enough segments we will default them + switch( j ) + { + case 0 : + { + aSegment.Count = 1; + aSegment.Command = css::drawing::EnhancedCustomShapeSegmentCommand::MOVETO; + } + break; + case 1 : + { + aSegment.Count = static_cast<sal_Int16>(std::min( nCoords - 1, sal_Int32(32767) )); + aSegment.Command = css::drawing::EnhancedCustomShapeSegmentCommand::LINETO; + } + break; + case 2 : + { + aSegment.Count = 1; + aSegment.Command = css::drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH; + } + break; + case 3 : + { + aSegment.Count = 1; + aSegment.Command = css::drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH; + } + break; + } + } + else + aSegment = rSegments[ j ]; + + if ( !aStrBuffer.isEmpty() ) + aStrBuffer.append( ' ' ); + + sal_Int32 nParameter = 0; + switch( aSegment.Command ) + { + case css::drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH : + aStrBuffer.append( 'Z' ); break; + case css::drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH : + aStrBuffer.append( 'N' ); break; + case css::drawing::EnhancedCustomShapeSegmentCommand::NOFILL : + aStrBuffer.append( 'F' ); break; + case css::drawing::EnhancedCustomShapeSegmentCommand::NOSTROKE : + aStrBuffer.append( 'S' ); break; + + case css::drawing::EnhancedCustomShapeSegmentCommand::MOVETO : + aStrBuffer.append( 'M' ); nParameter = 1; break; + case css::drawing::EnhancedCustomShapeSegmentCommand::LINETO : + aStrBuffer.append( 'L' ); nParameter = 1; break; + case css::drawing::EnhancedCustomShapeSegmentCommand::CURVETO : + aStrBuffer.append( 'C' ); nParameter = 3; break; + case css::drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO : + aStrBuffer.append( 'T' ); nParameter = 3; break; + case css::drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE : + aStrBuffer.append( 'U' ); nParameter = 3; break; + case css::drawing::EnhancedCustomShapeSegmentCommand::ARCTO : + aStrBuffer.append( 'A' ); nParameter = 4; break; + case css::drawing::EnhancedCustomShapeSegmentCommand::ARC : + aStrBuffer.append( 'B' ); nParameter = 4; break; + case css::drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO : + aStrBuffer.append( 'W' ); nParameter = 4; break; + case css::drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC : + aStrBuffer.append( 'V' ); nParameter = 4; break; + case css::drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX : + aStrBuffer.append( 'X' ); nParameter = 1; break; + case css::drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY : + aStrBuffer.append( 'Y' ); nParameter = 1; break; + case css::drawing::EnhancedCustomShapeSegmentCommand::QUADRATICCURVETO : + aStrBuffer.append( 'Q' ); nParameter = 2; break; + case css::drawing::EnhancedCustomShapeSegmentCommand::ARCANGLETO : + if ( bExtended ) { + aStrBuffer.append( 'G' ); + nParameter = 2; + } else { + aStrBuffer.setLength( aStrBuffer.getLength() - 1); + bNeedExtended = true; + i += 2; + } + break; + case css::drawing::EnhancedCustomShapeSegmentCommand::DARKEN : + if ( bExtended ) + aStrBuffer.append( 'H' ); + else + bNeedExtended = true; + break; + case css::drawing::EnhancedCustomShapeSegmentCommand::DARKENLESS : + if ( bExtended ) + aStrBuffer.append( 'I' ); + else + bNeedExtended = true; + break; + case css::drawing::EnhancedCustomShapeSegmentCommand::LIGHTEN : + if ( bExtended ) + aStrBuffer.append( 'J' ); + else + bNeedExtended = true; + break; + case css::drawing::EnhancedCustomShapeSegmentCommand::LIGHTENLESS : + if ( bExtended ) + aStrBuffer.append( 'K' ); + else + bNeedExtended = true; + break; + default : // ups, seems to be something wrong + { + aSegment.Count = 1; + aSegment.Command = css::drawing::EnhancedCustomShapeSegmentCommand::LINETO; + } + break; + } + if ( nParameter ) + { + for ( k = 0; k < aSegment.Count; k++ ) + { + if ( ( i + nParameter ) <= nCoords ) + { + for ( l = 0; l < nParameter; l++ ) + { + ExportParameter( aStrBuffer, rCoordinates[ i ].First ); + ExportParameter( aStrBuffer, rCoordinates[ i++ ].Second ); + } + } + else + { + j = nSegments; // error -> exiting + break; + } + } + } + } + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( bExtended ? XML_NAMESPACE_DRAW_EXT : XML_NAMESPACE_DRAW, XML_ENHANCED_PATH, aStr ); + if (!bExtended && bNeedExtended && (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)) + ImpExportEnhancedPath( rExport, rCoordinates, rSegments, true ); +} + +static void ImpExportEnhancedGeometry( SvXMLExport& rExport, const uno::Reference< beans::XPropertySet >& xPropSet ) +{ + bool bEquations = false; + uno::Sequence< OUString > aEquations; + + bool bHandles = false; + uno::Sequence< beans::PropertyValues > aHandles; + + uno::Sequence< css::drawing::EnhancedCustomShapeSegment > aSegments; + uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > aCoordinates; + + uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentValues; + + OUString aStr; + OUStringBuffer aStrBuffer; + double fTextRotateAngle(0.0); + double fTextPreRotateAngle(0.0); // will be consolidated with fTextRotateAngle at the end + SvXMLUnitConverter& rUnitConverter = rExport.GetMM100UnitConverter(); + + uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xPropSet->getPropertySetInfo() ); + + // geometry + static constexpr OUString sCustomShapeGeometry( u"CustomShapeGeometry"_ustr ); + if ( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName( sCustomShapeGeometry ) ) + { + uno::Any aGeoPropSet( xPropSet->getPropertyValue( sCustomShapeGeometry ) ); + uno::Sequence< beans::PropertyValue > aGeoPropSeq; + + if ( aGeoPropSet >>= aGeoPropSeq ) + { + bool bCoordinates = false; + OUString aCustomShapeType( "non-primitive" ); + + for ( const beans::PropertyValue& rGeoProp : std::as_const(aGeoPropSeq) ) + { + switch( EASGet( rGeoProp.Name ) ) + { + case EAS_Type : + { + rGeoProp.Value >>= aCustomShapeType; + } + break; + case EAS_MirroredX : + { + bool bMirroredX; + if ( rGeoProp.Value >>= bMirroredX ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_MIRROR_HORIZONTAL, + bMirroredX ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_MirroredY : + { + bool bMirroredY; + if ( rGeoProp.Value >>= bMirroredY ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_MIRROR_VERTICAL, + bMirroredY ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_ViewBox : + { + awt::Rectangle aRect; + if ( rGeoProp.Value >>= aRect ) + { + SdXMLImExViewBox aViewBox( aRect.X, aRect.Y, aRect.Width, aRect.Height ); + rExport.AddAttribute( XML_NAMESPACE_SVG, XML_VIEWBOX, aViewBox.GetExportString() ); + } + } + break; + case EAS_TextPreRotateAngle : + { + rGeoProp.Value >>= fTextPreRotateAngle; + } + break; + case EAS_TextRotateAngle : + { + rGeoProp.Value >>= fTextRotateAngle; + } + break; + case EAS_Extrusion : + { + uno::Sequence< beans::PropertyValue > aExtrusionPropSeq; + if ( rGeoProp.Value >>= aExtrusionPropSeq ) + { + bool bSkewValuesProvided = false; + for ( const beans::PropertyValue& rProp : std::as_const(aExtrusionPropSeq) ) + { + switch( EASGet( rProp.Name ) ) + { + case EAS_Extrusion : + { + bool bExtrusionOn; + if ( rProp.Value >>= bExtrusionOn ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION, + bExtrusionOn ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_Brightness : + { + double fExtrusionBrightness = 0; + if ( rProp.Value >>= fExtrusionBrightness ) + { + ::sax::Converter::convertDouble( + aStrBuffer, + fExtrusionBrightness, + false, + util::MeasureUnit::PERCENT, + util::MeasureUnit::PERCENT); + aStrBuffer.append( '%' ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_BRIGHTNESS, aStr ); + } + } + break; + case EAS_Depth : + { + css::drawing::EnhancedCustomShapeParameterPair aDepthParaPair; + if ( rProp.Value >>= aDepthParaPair ) + { + double fDepth = 0; + if ( aDepthParaPair.First.Value >>= fDepth ) + { + rExport.GetMM100UnitConverter().convertDouble( aStrBuffer, fDepth ); + ExportParameter( aStrBuffer, aDepthParaPair.Second ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_DEPTH, aStr ); + } + } + } + break; + case EAS_Diffusion : + { + double fExtrusionDiffusion = 0; + if ( rProp.Value >>= fExtrusionDiffusion ) + { + ::sax::Converter::convertDouble( + aStrBuffer, + fExtrusionDiffusion, + false, + util::MeasureUnit::PERCENT, + util::MeasureUnit::PERCENT); + aStrBuffer.append( '%' ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_DIFFUSION, aStr ); + } + } + break; + case EAS_NumberOfLineSegments : + { + sal_Int32 nExtrusionNumberOfLineSegments = 0; + if ( rProp.Value >>= nExtrusionNumberOfLineSegments ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_NUMBER_OF_LINE_SEGMENTS, OUString::number( nExtrusionNumberOfLineSegments ) ); + } + break; + case EAS_LightFace : + { + bool bExtrusionLightFace; + if ( rProp.Value >>= bExtrusionLightFace ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_LIGHT_FACE, + bExtrusionLightFace ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_FirstLightHarsh : + { + bool bExtrusionFirstLightHarsh; + if ( rProp.Value >>= bExtrusionFirstLightHarsh ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_FIRST_LIGHT_HARSH, + bExtrusionFirstLightHarsh ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_SecondLightHarsh : + { + bool bExtrusionSecondLightHarsh; + if ( rProp.Value >>= bExtrusionSecondLightHarsh ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_SECOND_LIGHT_HARSH, + bExtrusionSecondLightHarsh ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_FirstLightLevel : + { + double fExtrusionFirstLightLevel = 0; + if ( rProp.Value >>= fExtrusionFirstLightLevel ) + { + ::sax::Converter::convertDouble( + aStrBuffer, + fExtrusionFirstLightLevel, + false, + util::MeasureUnit::PERCENT, + util::MeasureUnit::PERCENT); + aStrBuffer.append( '%' ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_FIRST_LIGHT_LEVEL, aStr ); + } + } + break; + case EAS_SecondLightLevel : + { + double fExtrusionSecondLightLevel = 0; + if ( rProp.Value >>= fExtrusionSecondLightLevel ) + { + ::sax::Converter::convertDouble( + aStrBuffer, + fExtrusionSecondLightLevel, + false, + util::MeasureUnit::PERCENT, + util::MeasureUnit::PERCENT); + aStrBuffer.append( '%' ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_SECOND_LIGHT_LEVEL, aStr ); + } + } + break; + case EAS_FirstLightDirection : + { + drawing::Direction3D aExtrusionFirstLightDirection; + if ( rProp.Value >>= aExtrusionFirstLightDirection ) + { + ::basegfx::B3DVector aVec3D( aExtrusionFirstLightDirection.DirectionX, aExtrusionFirstLightDirection.DirectionY, + aExtrusionFirstLightDirection.DirectionZ ); + SvXMLUnitConverter::convertB3DVector( aStrBuffer, aVec3D ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_FIRST_LIGHT_DIRECTION, aStr ); + } + } + break; + case EAS_SecondLightDirection : + { + drawing::Direction3D aExtrusionSecondLightDirection; + if ( rProp.Value >>= aExtrusionSecondLightDirection ) + { + ::basegfx::B3DVector aVec3D( aExtrusionSecondLightDirection.DirectionX, aExtrusionSecondLightDirection.DirectionY, + aExtrusionSecondLightDirection.DirectionZ ); + SvXMLUnitConverter::convertB3DVector( aStrBuffer, aVec3D ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_SECOND_LIGHT_DIRECTION, aStr ); + } + } + break; + case EAS_Metal : + { + bool bExtrusionMetal; + if ( rProp.Value >>= bExtrusionMetal ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_METAL, + bExtrusionMetal ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_MetalType : + { + // export only if ODF extensions are enabled + sal_Int16 eMetalType; + if (rProp.Value >>= eMetalType) + { + SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion(); + if (eVersion > SvtSaveOptions::ODFSVER_013 + && (eVersion & SvtSaveOptions::ODFSVER_EXTENDED)) + { + if (eMetalType == drawing::EnhancedCustomShapeMetalType::MetalMSCompatible) + aStr = "loext:MetalMSCompatible"; + else + aStr = "draw:MetalODF"; + rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_EXTRUSION_METAL_TYPE, aStr); + } + } + } + break; + case EAS_ShadeMode : + { + // shadeMode + drawing::ShadeMode eShadeMode; + if( rProp.Value >>= eShadeMode ) + { + if( eShadeMode == drawing::ShadeMode_FLAT ) + aStr = GetXMLToken( XML_FLAT ); + else if( eShadeMode == drawing::ShadeMode_PHONG ) + aStr = GetXMLToken( XML_PHONG ); + else if( eShadeMode == drawing::ShadeMode_SMOOTH ) + aStr = GetXMLToken( XML_GOURAUD ); + else + aStr = GetXMLToken( XML_DRAFT ); + } + else + { + // ShadeMode enum not there, write default + aStr = GetXMLToken( XML_FLAT); + } + rExport.AddAttribute( XML_NAMESPACE_DR3D, XML_SHADE_MODE, aStr ); + } + break; + case EAS_RotateAngle : + { + css::drawing::EnhancedCustomShapeParameterPair aRotateAngleParaPair; + if ( rProp.Value >>= aRotateAngleParaPair ) + { + ExportParameter( aStrBuffer, aRotateAngleParaPair.First ); + ExportParameter( aStrBuffer, aRotateAngleParaPair.Second ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_ROTATION_ANGLE, aStr ); + } + } + break; + case EAS_RotationCenter : + { + drawing::Direction3D aExtrusionRotationCenter; + if ( rProp.Value >>= aExtrusionRotationCenter ) + { + ::basegfx::B3DVector aVec3D( aExtrusionRotationCenter.DirectionX, aExtrusionRotationCenter.DirectionY, + aExtrusionRotationCenter.DirectionZ ); + SvXMLUnitConverter::convertB3DVector( aStrBuffer, aVec3D ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_ROTATION_CENTER, aStr ); + } + } + break; + case EAS_Shininess : + { + double fExtrusionShininess = 0; + if ( rProp.Value >>= fExtrusionShininess ) + { + ::sax::Converter::convertDouble( + aStrBuffer, + fExtrusionShininess, + false, + util::MeasureUnit::PERCENT, + util::MeasureUnit::PERCENT); + aStrBuffer.append( '%' ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_SHININESS, aStr ); + } + } + break; + case EAS_Skew : + { + css::drawing::EnhancedCustomShapeParameterPair aSkewParaPair; + if ( rProp.Value >>= aSkewParaPair ) + { + bSkewValuesProvided = true; + ExportParameter( aStrBuffer, aSkewParaPair.First ); + ExportParameter( aStrBuffer, aSkewParaPair.Second ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_SKEW, aStr ); + } + } + break; + case EAS_Specularity : + { + double fExtrusionSpecularity = 0; + if ( rProp.Value >>= fExtrusionSpecularity ) + { + SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion(); + if (fExtrusionSpecularity > 100.0 && eVersion >= SvtSaveOptions::ODFSVER_012 + && (eVersion & SvtSaveOptions::ODFSVER_EXTENDED)) + { + // tdf#147580 write values > 100% in loext + ::sax::Converter::convertDouble( + aStrBuffer, + fExtrusionSpecularity, + false, + util::MeasureUnit::PERCENT, + util::MeasureUnit::PERCENT); + aStrBuffer.append( '%' ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_EXTRUSION_SPECULARITY_LOEXT, aStr ); + } + // tdf#147580 ODF 1 allows arbitrary percent, later versions not + if (eVersion >= SvtSaveOptions::ODFSVER_012) + { + fExtrusionSpecularity = std::clamp<double>(fExtrusionSpecularity, 0.0, 100.0); + } + ::sax::Converter::convertDouble( + aStrBuffer, + fExtrusionSpecularity, + false, + util::MeasureUnit::PERCENT, + util::MeasureUnit::PERCENT); + aStrBuffer.append( '%' ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_SPECULARITY, aStr ); + } + } + break; + case EAS_ProjectionMode : + { + drawing::ProjectionMode eProjectionMode; + if ( rProp.Value >>= eProjectionMode ) + rExport.AddAttribute( XML_NAMESPACE_DR3D, XML_PROJECTION, + eProjectionMode == drawing::ProjectionMode_PARALLEL ? GetXMLToken( XML_PARALLEL ) : GetXMLToken( XML_PERSPECTIVE ) ); + } + break; + case EAS_ViewPoint : + { + drawing::Position3D aExtrusionViewPoint; + if ( rProp.Value >>= aExtrusionViewPoint ) + { + rUnitConverter.convertPosition3D( aStrBuffer, aExtrusionViewPoint ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_VIEWPOINT, aStr ); + } + } + break; + case EAS_Origin : + { + css::drawing::EnhancedCustomShapeParameterPair aOriginParaPair; + if ( rProp.Value >>= aOriginParaPair ) + { + ExportParameter( aStrBuffer, aOriginParaPair.First ); + ExportParameter( aStrBuffer, aOriginParaPair.Second ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_ORIGIN, aStr ); + } + } + break; + case EAS_Color : + { + bool bExtrusionColor; + if ( rProp.Value >>= bExtrusionColor ) + { + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_COLOR, + bExtrusionColor ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + } + break; + default: + break; + } + } + // tdf#141301: no specific skew values provided + if (!bSkewValuesProvided) + { + // so we need to export default values explicitly + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_SKEW, "50 -135"); + } + } + } + break; + case EAS_TextPath : + { + uno::Sequence< beans::PropertyValue > aTextPathPropSeq; + if ( rGeoProp.Value >>= aTextPathPropSeq ) + { + for ( const beans::PropertyValue& rProp : std::as_const(aTextPathPropSeq) ) + { + switch( EASGet( rProp.Name ) ) + { + case EAS_TextPath : + { + bool bTextPathOn; + if ( rProp.Value >>= bTextPathOn ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_TEXT_PATH, + bTextPathOn ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_TextPathMode : + { + css::drawing::EnhancedCustomShapeTextPathMode eTextPathMode; + if ( rProp.Value >>= eTextPathMode ) + { + switch ( eTextPathMode ) + { + case css::drawing::EnhancedCustomShapeTextPathMode_NORMAL: aStr = GetXMLToken( XML_NORMAL ); break; + case css::drawing::EnhancedCustomShapeTextPathMode_PATH : aStr = GetXMLToken( XML_PATH ); break; + case css::drawing::EnhancedCustomShapeTextPathMode_SHAPE : aStr = GetXMLToken( XML_SHAPE ); break; + default: + break; + } + if ( !aStr.isEmpty() ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_TEXT_PATH_MODE, aStr ); + } + } + break; + case EAS_ScaleX : + { + bool bScaleX; + if ( rProp.Value >>= bScaleX ) + { + aStr = bScaleX ? GetXMLToken( XML_SHAPE ) : GetXMLToken( XML_PATH ); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_TEXT_PATH_SCALE, aStr ); + } + } + break; + case EAS_SameLetterHeights : + { + bool bSameLetterHeights; + if ( rProp.Value >>= bSameLetterHeights ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_TEXT_PATH_SAME_LETTER_HEIGHTS, + bSameLetterHeights ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + default: + break; + } + } + } + } + break; + case EAS_Path : + { + uno::Sequence< beans::PropertyValue > aPathPropSeq; + if ( rGeoProp.Value >>= aPathPropSeq ) + { + for ( const beans::PropertyValue& rProp : std::as_const(aPathPropSeq) ) + { + switch( EASGet( rProp.Name ) ) + { + case EAS_SubViewSize: + { + // export draw:sub-view-size (do not export in ODF 1.3 or older) + if ((rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0) + { + continue; + } + uno::Sequence< awt::Size > aSubViewSizes; + rProp.Value >>= aSubViewSizes; + + for ( int nIdx = 0; nIdx < aSubViewSizes.getLength(); nIdx++ ) + { + if ( nIdx ) + aStrBuffer.append(' '); + aStrBuffer.append( aSubViewSizes[nIdx].Width ); + aStrBuffer.append(' '); + aStrBuffer.append( aSubViewSizes[nIdx].Height ); + } + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW_EXT, XML_SUB_VIEW_SIZE, aStr ); + } + break; + case EAS_ExtrusionAllowed : + { + bool bExtrusionAllowed; + if ( rProp.Value >>= bExtrusionAllowed ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_EXTRUSION_ALLOWED, + bExtrusionAllowed ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_ConcentricGradientFillAllowed : + { + bool bConcentricGradientFillAllowed; + if ( rProp.Value >>= bConcentricGradientFillAllowed ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CONCENTRIC_GRADIENT_FILL_ALLOWED, + bConcentricGradientFillAllowed ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_TextPathAllowed : + { + bool bTextPathAllowed; + if ( rProp.Value >>= bTextPathAllowed ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_TEXT_PATH_ALLOWED, + bTextPathAllowed ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) ); + } + break; + case EAS_GluePoints : + { + css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> aGluePoints; + if ( rProp.Value >>= aGluePoints ) + { + if ( aGluePoints.hasElements() ) + { + for( const auto& rGluePoint : std::as_const(aGluePoints) ) + { + ExportParameter( aStrBuffer, rGluePoint.First ); + ExportParameter( aStrBuffer, rGluePoint.Second ); + } + aStr = aStrBuffer.makeStringAndClear(); + } + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_GLUE_POINTS, aStr ); + } + } + break; + case EAS_GluePointType : + { + sal_Int16 nGluePointType = sal_Int16(); + if ( rProp.Value >>= nGluePointType ) + { + switch ( nGluePointType ) + { + case css::drawing::EnhancedCustomShapeGluePointType::NONE : aStr = GetXMLToken( XML_NONE ); break; + case css::drawing::EnhancedCustomShapeGluePointType::SEGMENTS : aStr = GetXMLToken( XML_SEGMENTS ); break; + case css::drawing::EnhancedCustomShapeGluePointType::RECT : aStr = GetXMLToken( XML_RECTANGLE ); break; + } + if ( !aStr.isEmpty() ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_GLUE_POINT_TYPE, aStr ); + } + } + break; + case EAS_Coordinates : + { + bCoordinates = ( rProp.Value >>= aCoordinates ); + } + break; + case EAS_Segments : + { + rProp.Value >>= aSegments; + } + break; + case EAS_StretchX : + { + sal_Int32 nStretchPoint = 0; + if ( rProp.Value >>= nStretchPoint ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_PATH_STRETCHPOINT_X, OUString::number( nStretchPoint ) ); + } + break; + case EAS_StretchY : + { + sal_Int32 nStretchPoint = 0; + if ( rProp.Value >>= nStretchPoint ) + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_PATH_STRETCHPOINT_Y, OUString::number( nStretchPoint ) ); + } + break; + case EAS_TextFrames : + { + css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > aPathTextFrames; + if ( rProp.Value >>= aPathTextFrames ) + { + if ( aPathTextFrames.hasElements() ) + { + for ( const auto& rPathTextFrame : std::as_const(aPathTextFrames) ) + { + ExportParameter( aStrBuffer, rPathTextFrame.TopLeft.First ); + ExportParameter( aStrBuffer, rPathTextFrame.TopLeft.Second ); + ExportParameter( aStrBuffer, rPathTextFrame.BottomRight.First ); + ExportParameter( aStrBuffer, rPathTextFrame.BottomRight.Second ); + } + aStr = aStrBuffer.makeStringAndClear(); + } + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_TEXT_AREAS, aStr ); + } + } + break; + default: + break; + } + } + } + } + break; + case EAS_Equations : + { + bEquations = ( rGeoProp.Value >>= aEquations ); + } + break; + case EAS_Handles : + { + bHandles = ( rGeoProp.Value >>= aHandles ); + } + break; + case EAS_AdjustmentValues : + { + rGeoProp.Value >>= aAdjustmentValues; + } + break; + default: + break; + } + } // for + + // ToDo: Where is TextPreRotateAngle still used? We cannot save it in ODF. + fTextRotateAngle += fTextPreRotateAngle; + // Workaround for writing-mode bt-lr and tb-rl90 in ODF strict, + // otherwise loext:writing-mode is used in style export. + if (!(rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)) + { + if (xPropSetInfo->hasPropertyByName(u"WritingMode"_ustr)) + { + sal_Int16 nDirection = -1; + xPropSet->getPropertyValue(u"WritingMode"_ustr) >>= nDirection; + if (nDirection == text::WritingMode2::TB_RL90) + fTextRotateAngle -= 90; + else if (nDirection == text::WritingMode2::BT_LR) + fTextRotateAngle -= 270; + } + } + if (fTextRotateAngle != 0) + { + ::sax::Converter::convertDouble( aStrBuffer, fTextRotateAngle ); + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_TEXT_ROTATE_ANGLE, aStr ); + } + + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_TYPE, aCustomShapeType ); + + // adjustments + sal_Int32 nAdjustmentValues = aAdjustmentValues.getLength(); + if ( nAdjustmentValues ) + { + sal_Int32 i, nValue = 0; + for ( i = 0; i < nAdjustmentValues; i++ ) + { + if ( i ) + aStrBuffer.append( ' ' ); + + const css::drawing::EnhancedCustomShapeAdjustmentValue& rAdj = aAdjustmentValues[ i ]; + if ( rAdj.State == beans::PropertyState_DIRECT_VALUE ) + { + if ( rAdj.Value.getValueTypeClass() == uno::TypeClass_DOUBLE ) + { + double fValue = 0.0; + rAdj.Value >>= fValue; + ::sax::Converter::convertDouble(aStrBuffer, fValue); + } + else + { + rAdj.Value >>= nValue; + aStrBuffer.append(nValue); + } + } + else + { + // this should not be, but better than setting nothing + aStrBuffer.append("0"); + } + } + aStr = aStrBuffer.makeStringAndClear(); + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_MODIFIERS, aStr ); + } + if ( bCoordinates ) + ImpExportEnhancedPath( rExport, aCoordinates, aSegments ); + } + } + SvXMLElementExport aOBJ( rExport, XML_NAMESPACE_DRAW, XML_ENHANCED_GEOMETRY, true, true ); + if ( bEquations ) + ImpExportEquations( rExport, aEquations ); + if ( bHandles ) + ImpExportHandles( rExport, aHandles ); +} + +void XMLShapeExport::ImpExportCustomShape( + const uno::Reference< drawing::XShape >& xShape, + XMLShapeExportFlags nFeatures, css::awt::Point* pRefPoint ) +{ + const uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + if ( !xPropSet.is() ) + return; + + uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xPropSet->getPropertySetInfo() ); + + // Transformation + ImpExportNewTrans( xPropSet, nFeatures, pRefPoint ); + + if ( xPropSetInfo.is() ) + { + OUString aStr; + if ( xPropSetInfo->hasPropertyByName( "CustomShapeEngine" ) ) + { + uno::Any aEngine( xPropSet->getPropertyValue( "CustomShapeEngine" ) ); + if ( ( aEngine >>= aStr ) && !aStr.isEmpty() ) + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_ENGINE, aStr ); + } + if ( xPropSetInfo->hasPropertyByName( "CustomShapeData" ) ) + { + uno::Any aData( xPropSet->getPropertyValue( "CustomShapeData" ) ); + if ( ( aData >>= aStr ) && !aStr.isEmpty() ) + mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_DATA, aStr ); + } + } + bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); // #86116#/#92210# + SvXMLElementExport aOBJ( mrExport, XML_NAMESPACE_DRAW, XML_CUSTOM_SHAPE, bCreateNewline, true ); + ImpExportDescription( xShape ); // #i68101# + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + ImpExportText( xShape ); + ImpExportEnhancedGeometry( mrExport, xPropSet ); + +} + +void XMLShapeExport::ImpExportTableShape( const uno::Reference< drawing::XShape >& xShape, XmlShapeType eShapeType, XMLShapeExportFlags nFeatures, css::awt::Point* pRefPoint ) +{ + uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY); + uno::Reference< container::XNamed > xNamed(xShape, uno::UNO_QUERY); + + SAL_WARN_IF( !xPropSet.is() || !xNamed.is(), "xmloff", "xmloff::XMLShapeExport::ImpExportTableShape(), table shape is not implementing needed interfaces"); + if(!(xPropSet.is() && xNamed.is())) + return; + + try + { + // Transformation + ImpExportNewTrans(xPropSet, nFeatures, pRefPoint); + + bool bIsEmptyPresObj = false; + + // presentation settings + if(eShapeType == XmlShapeType::PresTableShape) + bIsEmptyPresObj = ImpExportPresentationAttributes( xPropSet, GetXMLToken(XML_TABLE) ); + + const bool bCreateNewline( (nFeatures & XMLShapeExportFlags::NO_WS) == XMLShapeExportFlags::NONE ); + + SvXMLElementExport aElement( mrExport, XML_NAMESPACE_DRAW, XML_FRAME, bCreateNewline, true ); + + // do not export in ODF 1.1 or older + if (mrExport.getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012) + { + if( !bIsEmptyPresObj ) + { + uno::Reference< container::XNamed > xTemplate( xPropSet->getPropertyValue("TableTemplate"), uno::UNO_QUERY ); + if( xTemplate.is() ) + { + const OUString sTemplate( xTemplate->getName() ); + if( !sTemplate.isEmpty() ) + { + mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TEMPLATE_NAME, sTemplate ); + + for( const XMLPropertyMapEntry* pEntry = &aXMLTableShapeAttributes[0]; !pEntry->IsEnd(); pEntry++ ) + { + try + { + bool bBool = false; + xPropSet->getPropertyValue( pEntry->getApiName() ) >>= bBool; + if( bBool ) + mrExport.AddAttribute(pEntry->mnNameSpace, pEntry->meXMLName, XML_TRUE ); + } + catch( uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.draw"); + } + } + } + } + + uno::Reference< table::XColumnRowRange > xRange( xPropSet->getPropertyValue( gsModel ), uno::UNO_QUERY_THROW ); + GetShapeTableExport()->exportTable( xRange ); + } + } + + if( !bIsEmptyPresObj ) + { + uno::Reference< graphic::XGraphic > xGraphic( xPropSet->getPropertyValue("ReplacementGraphic"), uno::UNO_QUERY ); + ExportGraphicPreview(xGraphic, mrExport, u"TablePreview", u".svm", "image/x-vclgraphic"); + } + + ImpExportEvents( xShape ); + ImpExportGluePoints( xShape ); + ImpExportDescription( xShape ); // #i68101# + } + catch( uno::Exception const & ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.draw"); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |