summaryrefslogtreecommitdiffstats
path: root/oox/source/shape
diff options
context:
space:
mode:
Diffstat (limited to 'oox/source/shape')
-rw-r--r--oox/source/shape/LockedCanvasContext.cxx97
-rw-r--r--oox/source/shape/LockedCanvasContext.hxx37
-rw-r--r--oox/source/shape/ShapeContextHandler.cxx689
-rw-r--r--oox/source/shape/ShapeDrawingFragmentHandler.cxx49
-rw-r--r--oox/source/shape/ShapeFilterBase.cxx137
-rw-r--r--oox/source/shape/WordprocessingCanvasContext.cxx110
-rw-r--r--oox/source/shape/WordprocessingCanvasContext.hxx39
-rw-r--r--oox/source/shape/WpgContext.cxx95
-rw-r--r--oox/source/shape/WpgContext.hxx43
-rw-r--r--oox/source/shape/WpsContext.cxx982
-rw-r--r--oox/source/shape/WpsContext.hxx46
11 files changed, 2324 insertions, 0 deletions
diff --git a/oox/source/shape/LockedCanvasContext.cxx b/oox/source/shape/LockedCanvasContext.cxx
new file mode 100644
index 0000000000..ab74515827
--- /dev/null
+++ b/oox/source/shape/LockedCanvasContext.cxx
@@ -0,0 +1,97 @@
+/* -*- 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/.
+ */
+
+#include "LockedCanvasContext.hxx"
+#include <sal/log.hxx>
+#include <drawingml/shapepropertiescontext.hxx>
+#include <oox/drawingml/connectorshapecontext.hxx>
+#include <oox/drawingml/graphicshapecontext.hxx>
+#include <oox/drawingml/shape.hxx>
+#include <oox/drawingml/shapecontext.hxx>
+#include <oox/drawingml/shapegroupcontext.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+using namespace com::sun::star;
+
+namespace oox::shape
+{
+LockedCanvasContext::LockedCanvasContext(FragmentHandler2 const& rParent)
+ : FragmentHandler2(rParent)
+{
+ mpShapePtr = std::make_shared<oox::drawingml::Shape>("com.sun.star.drawing.GroupShape");
+ mpShapePtr->setLockedCanvas(true); // will be "LockedCanvas" in InteropGrabBag
+}
+
+LockedCanvasContext::~LockedCanvasContext() = default;
+
+::oox::core::ContextHandlerRef
+LockedCanvasContext::onCreateContext(sal_Int32 nElementToken, const ::oox::AttributeList& rAttribs)
+{
+ switch (getBaseToken(nElementToken))
+ {
+ case XML_nvGrpSpPr: // CT_GvmlGroupShapeNonVisual, child see at end
+ return this;
+ case XML_grpSpPr: // CT_GroupShapeProperties
+ return new oox::drawingml::ShapePropertiesContext(*this, *mpShapePtr);
+ case XML_txSp: // CT_GvmlTextShape
+ break;
+ case XML_sp: // CT_GvmlShape
+ {
+ return new oox::drawingml::ShapeContext(
+ *this, mpShapePtr,
+ std::make_shared<oox::drawingml::Shape>("com.sun.star.drawing.CustomShape", true));
+ }
+ case XML_cxnSp: // CT_GvmlConnector
+ {
+ oox::drawingml::ShapePtr pShape
+ = std::make_shared<oox::drawingml::Shape>("com.sun.star.drawing.ConnectorShape");
+ return new oox::drawingml::ConnectorShapeContext(*this, mpShapePtr, pShape,
+ pShape->getConnectorShapeProperties());
+ }
+ case XML_pic: // CT_GvmlPicture
+ {
+ return new oox::drawingml::GraphicShapeContext(
+ *this, mpShapePtr,
+ std::make_shared<oox::drawingml::Shape>("com.sun.star.drawing.GraphicObjectShape"));
+ }
+ case XML_graphicFrame: // CT_GvmlGraphicObjectFrame
+ {
+ return new oox::drawingml::GraphicalObjectFrameContext(
+ *this, mpShapePtr,
+ std::make_shared<oox::drawingml::Shape>("com.sun.star.drawing.GraphicObjectShape"),
+ true);
+ }
+ case XML_grpSp: // CT_GvmlGroupShape
+ {
+ return new oox::drawingml::ShapeGroupContext(
+ *this, mpShapePtr,
+ std::make_shared<oox::drawingml::Shape>("com.sun.star.drawing.GroupShape"));
+ }
+ // mandatory child elements of CT_GvmlGroupShapeNonVisual
+ case XML_cNvPr: // CT_NonVisualDrawingProps
+ {
+ mpShapePtr->setHidden(rAttribs.getBool(XML_hidden, false));
+ mpShapePtr->setId(rAttribs.getStringDefaulted(XML_id));
+ mpShapePtr->setName(rAttribs.getStringDefaulted(XML_name));
+ break;
+ }
+ case XML_cNvGrpSpPr: // CT_NonVisualGroupDrawingShapeProps
+ break;
+ default:
+ SAL_WARN("oox", "LockedCanvasContext::createFastChildContext: unhandled element:"
+ << getBaseToken(nElementToken));
+ break;
+ }
+ return nullptr;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/shape/LockedCanvasContext.hxx b/oox/source/shape/LockedCanvasContext.hxx
new file mode 100644
index 0000000000..076931d092
--- /dev/null
+++ b/oox/source/shape/LockedCanvasContext.hxx
@@ -0,0 +1,37 @@
+/* -*- 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/.
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_SHAPE_LOCKEDCANVASCONTEXT_HXX
+#define INCLUDED_OOX_SOURCE_SHAPE_LOCKEDCANVASCONTEXT_HXX
+
+#include <oox/core/fragmenthandler2.hxx>
+#include <oox/drawingml/drawingmltypes.hxx>
+
+namespace oox::shape
+{
+/// Locked canvas is kind of a container for drawingml shapes: it can even contain group shapes.
+class LockedCanvasContext final : public oox::core::FragmentHandler2
+{
+public:
+ explicit LockedCanvasContext(oox::core::FragmentHandler2 const& rParent);
+ ~LockedCanvasContext() override;
+
+ oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElementToken,
+ const ::oox::AttributeList& rAttribs) override;
+
+ const oox::drawingml::ShapePtr& getShape() const { return mpShapePtr; }
+
+private:
+ oox::drawingml::ShapePtr mpShapePtr;
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/shape/ShapeContextHandler.cxx b/oox/source/shape/ShapeContextHandler.cxx
new file mode 100644
index 0000000000..c012097004
--- /dev/null
+++ b/oox/source/shape/ShapeContextHandler.cxx
@@ -0,0 +1,689 @@
+/* -*- 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 <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XGluePointsSupplier.hpp>
+#include <com/sun/star/container/XIdentifierContainer.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
+
+#include <oox/shape/ShapeContextHandler.hxx>
+#include <oox/shape/ShapeDrawingFragmentHandler.hxx>
+#include "LockedCanvasContext.hxx"
+#include "WordprocessingCanvasContext.hxx"
+#include "WpsContext.hxx"
+#include "WpgContext.hxx"
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <oox/vml/vmldrawingfragment.hxx>
+#include <oox/vml/vmlshape.hxx>
+#include <oox/vml/vmlshapecontainer.hxx>
+#include <oox/shape/ShapeFilterBase.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/drawingml/theme.hxx>
+#include <oox/drawingml/themefragmenthandler.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdobj.hxx>
+
+#include <drawingml/connectorhelper.hxx>
+
+#include <memory>
+#include <utility>
+
+using namespace ::com::sun::star;
+
+namespace oox::shape {
+using namespace core;
+using namespace drawingml;
+
+ShapeContextHandler::ShapeContextHandler(rtl::Reference<ShapeFilterBase> xFilterBase) :
+ m_bFullWPGSUpport(false),
+ mxShapeFilterBase(std::move(xFilterBase))
+
+{
+}
+
+ShapeContextHandler::~ShapeContextHandler()
+{
+}
+
+uno::Reference<xml::sax::XFastContextHandler> ShapeContextHandler::getLockedCanvasContext(sal_Int32 nElement)
+{
+ if (!mxLockedCanvasContext.is())
+ {
+ FragmentHandler2Ref rFragmentHandler(new ShapeFragmentHandler(*mxShapeFilterBase, msRelationFragmentPath));
+
+ switch (nElement & 0xffff)
+ {
+ case XML_lockedCanvas:
+ mxLockedCanvasContext.set(new LockedCanvasContext(*rFragmentHandler));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return static_cast<ContextHandler *>(mxLockedCanvasContext.get());
+}
+
+/*
+ * This method creates new ChartGraphicDataContext Object.
+ */
+uno::Reference<xml::sax::XFastContextHandler> ShapeContextHandler::getChartShapeContext(sal_Int32 nElement)
+{
+ if (!mxChartShapeContext.is())
+ {
+ switch (nElement & 0xffff)
+ {
+ case XML_chart:
+ {
+ std::unique_ptr<ContextHandler2Helper> pFragmentHandler(
+ new ShapeFragmentHandler(*mxShapeFilterBase, msRelationFragmentPath));
+ mpShape = std::make_shared<Shape>("com.sun.star.drawing.OLE2Shape" );
+ mxChartShapeContext.set(new ChartGraphicDataContext(*pFragmentHandler, mpShape, true));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return mxChartShapeContext;
+}
+
+uno::Reference<xml::sax::XFastContextHandler> ShapeContextHandler::getWpsContext(sal_Int32 nStartElement, sal_Int32 nElement)
+{
+ if (!mxWpsContext.is())
+ {
+ FragmentHandler2Ref rFragmentHandler(new ShapeFragmentHandler(*mxShapeFilterBase, msRelationFragmentPath));
+ ShapePtr pMasterShape;
+
+ uno::Reference<drawing::XShape> xShape;
+ // No element happens in case of pretty-printed XML, bodyPr is the case when we are called again after <wps:txbx>.
+ if (!nElement || nElement == WPS_TOKEN(bodyPr))
+ // Assume that this is just a continuation of the previous shape.
+ xShape = mxSavedShape;
+
+ switch (getBaseToken(nStartElement))
+ {
+ case XML_wsp:
+ mxWpsContext.set(new WpsContext(
+ *rFragmentHandler,
+ xShape,
+ pMasterShape,
+ std::make_shared<oox::drawingml::Shape>(
+ "com.sun.star.drawing.CustomShape")));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return mxWpsContext;
+}
+
+uno::Reference<xml::sax::XFastContextHandler> ShapeContextHandler::getWpgContext(sal_Int32 nElement)
+{
+ if (!mxWpgContext.is())
+ {
+ FragmentHandler2Ref rFragmentHandler(new ShapeFragmentHandler(*mxShapeFilterBase, msRelationFragmentPath));
+
+ switch (getBaseToken(nElement))
+ {
+ case XML_wgp:
+ {
+ mxWpgContext.set(new WpgContext(*rFragmentHandler, oox::drawingml::ShapePtr()));
+ mxWpgContext->setFullWPGSupport(m_bFullWPGSUpport);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return static_cast<ContextHandler *>(mxWpgContext.get());
+}
+
+uno::Reference<xml::sax::XFastContextHandler> const &
+ShapeContextHandler::getGraphicShapeContext(::sal_Int32 Element )
+{
+ if (! mxGraphicShapeContext.is())
+ {
+ auto pFragmentHandler = std::make_shared<ShapeFragmentHandler>(*mxShapeFilterBase, msRelationFragmentPath);
+ ShapePtr pMasterShape;
+
+ switch (Element & 0xffff)
+ {
+ case XML_graphic:
+ mpShape = std::make_shared<Shape>("com.sun.star.drawing.GraphicObjectShape" );
+ mxGraphicShapeContext.set
+ (new GraphicalObjectFrameContext(*pFragmentHandler, pMasterShape, mpShape, true));
+ break;
+ case XML_pic:
+ mpShape = std::make_shared<Shape>("com.sun.star.drawing.GraphicObjectShape" );
+ mxGraphicShapeContext.set
+ (new GraphicShapeContext(*pFragmentHandler, pMasterShape, mpShape));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return mxGraphicShapeContext;
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ShapeContextHandler::getDrawingShapeContext()
+{
+ if (!mxDrawingFragmentHandler.is())
+ {
+ mpDrawing = std::make_shared<oox::vml::Drawing>( *mxShapeFilterBase, mxDrawPage, oox::vml::VMLDRAWING_WORD );
+ mxDrawingFragmentHandler.set
+ (new oox::vml::DrawingFragment
+ ( *mxShapeFilterBase, msRelationFragmentPath, *mpDrawing ));
+ }
+ else
+ {
+ // Reset the handler if fragment path has changed
+ OUString sHandlerFragmentPath = mxDrawingFragmentHandler->getFragmentPath();
+ if ( msRelationFragmentPath != sHandlerFragmentPath )
+ {
+ mxDrawingFragmentHandler.clear();
+ mxDrawingFragmentHandler.set
+ (new oox::vml::DrawingFragment
+ ( *mxShapeFilterBase, msRelationFragmentPath, *mpDrawing ));
+ }
+ }
+ return static_cast<ContextHandler *>(mxDrawingFragmentHandler.get());
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ShapeContextHandler::getDiagramShapeContext()
+{
+ if (!mxDiagramShapeContext.is())
+ {
+ auto pFragmentHandler = std::make_shared<ShapeFragmentHandler>(*mxShapeFilterBase, msRelationFragmentPath);
+ mpShape = std::make_shared<Shape>();
+ mpShape->setSize(maSize);
+ mxDiagramShapeContext.set(new DiagramGraphicDataContext(*pFragmentHandler, mpShape));
+ }
+
+ return mxDiagramShapeContext;
+}
+
+uno::Reference<xml::sax::XFastContextHandler> ShapeContextHandler::getWordprocessingCanvasContext(sal_Int32 nElement)
+{
+ if (!mxWordprocessingCanvasContext.is())
+ {
+ FragmentHandler2Ref rFragmentHandler(new ShapeFragmentHandler(*mxShapeFilterBase, msRelationFragmentPath));
+
+ switch (getBaseToken(nElement))
+ {
+ case XML_wpc:
+ mxWordprocessingCanvasContext.set(new WordprocessingCanvasContext(*rFragmentHandler, maSize));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return static_cast<ContextHandler *>(mxWordprocessingCanvasContext.get());
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ShapeContextHandler::getContextHandler(sal_Int32 nElement)
+{
+ uno::Reference<xml::sax::XFastContextHandler> xResult;
+ const sal_uInt32 nStartToken = getStartToken();
+
+ switch (getNamespace( nStartToken ))
+ {
+ case NMSP_doc:
+ case NMSP_vml:
+ xResult.set(getDrawingShapeContext());
+ break;
+ case NMSP_dmlDiagram:
+ xResult.set(getDiagramShapeContext());
+ break;
+ case NMSP_dmlLockedCanvas:
+ xResult.set(getLockedCanvasContext(nStartToken));
+ break;
+ case NMSP_dmlChart:
+ xResult.set(getChartShapeContext(nStartToken));
+ break;
+ case NMSP_wps:
+ xResult.set(getWpsContext(nStartToken, nElement));
+ break;
+ case NMSP_wpg:
+ xResult.set(getWpgContext(nStartToken));
+ break;
+ case NMSP_wpc:
+ xResult.set(getWordprocessingCanvasContext(nStartToken));
+ break;
+ default:
+ xResult.set(getGraphicShapeContext(nStartToken));
+ break;
+ }
+
+ return xResult;
+}
+
+// css::xml::sax::XFastContextHandler:
+void SAL_CALL ShapeContextHandler::startFastElement
+(::sal_Int32 Element,
+ const uno::Reference< xml::sax::XFastAttributeList > & Attribs)
+{
+ mxShapeFilterBase->filter(maMediaDescriptor);
+
+ if (Element == DGM_TOKEN(relIds) || Element == LC_TOKEN(lockedCanvas) || Element == C_TOKEN(chart) ||
+ Element == WPS_TOKEN(wsp) || Element == WPG_TOKEN(wgp) || Element == OOX_TOKEN(dmlPicture, pic)
+ || Element == WPC_TOKEN(wpc))
+ {
+ // Parse the theme relation, if available; the diagram won't have colors without it.
+ if (!mpThemePtr && !msRelationFragmentPath.isEmpty())
+ {
+ // Get Target for Type = "officeDocument" from _rels/.rels file
+ // aOfficeDocumentFragmentPath is pointing to "word/document.xml" for docx & to "ppt/presentation.xml" for pptx
+ FragmentHandlerRef rFragmentHandlerRef(new ShapeFragmentHandler(*mxShapeFilterBase, "/"));
+ OUString aOfficeDocumentFragmentPath = rFragmentHandlerRef->getFragmentPathFromFirstTypeFromOfficeDoc( u"officeDocument" );
+
+ // Get the theme DO NOT use msRelationFragmentPath for getting theme as for a document there is a single theme in document.xml.rels
+ // and the same is used by header and footer as well.
+ FragmentHandlerRef rFragmentHandler(new ShapeFragmentHandler(*mxShapeFilterBase, aOfficeDocumentFragmentPath));
+ OUString aThemeFragmentPath = rFragmentHandler->getFragmentPathFromFirstTypeFromOfficeDoc( u"theme" );
+
+ if (!aThemeFragmentPath.isEmpty())
+ {
+ mpThemePtr = std::make_shared<Theme>();
+ auto pTheme = std::make_shared<model::Theme>();
+ mpThemePtr->setTheme(pTheme);
+ uno::Reference<xml::sax::XFastSAXSerializable> xDoc(mxShapeFilterBase->importFragment(aThemeFragmentPath), uno::UNO_QUERY_THROW);
+ mxShapeFilterBase->importFragment(new ThemeFragmentHandler(*mxShapeFilterBase, aThemeFragmentPath, *mpThemePtr, *pTheme), xDoc);
+ mxShapeFilterBase->setCurrentTheme(mpThemePtr);
+ }
+ }
+ else if (mpThemePtr && !mxShapeFilterBase->getCurrentTheme())
+ {
+ mxShapeFilterBase->setCurrentTheme(mpThemePtr);
+ }
+
+ createFastChildContext(Element, Attribs);
+ }
+
+ // Entering VML block (startFastElement() is called for the outermost tag),
+ // handle possible recursion.
+ if ( getContextHandler() == getDrawingShapeContext() )
+ mpDrawing->getShapes().pushMark();
+
+ uno::Reference<XFastContextHandler> xContextHandler(getContextHandler());
+
+ if (xContextHandler.is())
+ xContextHandler->startFastElement(Element, Attribs);
+}
+
+void SAL_CALL ShapeContextHandler::startUnknownElement
+(const OUString & Namespace, const OUString & Name,
+ const uno::Reference< xml::sax::XFastAttributeList > & Attribs)
+{
+ if ( getContextHandler() == getDrawingShapeContext() )
+ mpDrawing->getShapes().pushMark();
+
+ uno::Reference<XFastContextHandler> xContextHandler(getContextHandler());
+
+ if (xContextHandler.is())
+ xContextHandler->startUnknownElement(Namespace, Name, Attribs);
+}
+
+void SAL_CALL ShapeContextHandler::endFastElement(::sal_Int32 Element)
+{
+ uno::Reference<XFastContextHandler> xContextHandler(getContextHandler());
+
+ if (xContextHandler.is())
+ xContextHandler->endFastElement(Element);
+ // In case a textbox is sent, and later we get additional properties for
+ // the textbox, then the wps context is not cleared, so do that here.
+ if (Element != (NMSP_wps | XML_wsp))
+ return;
+
+ uno::Reference<lang::XServiceInfo> xServiceInfo(mxSavedShape, uno::UNO_QUERY);
+ bool bTextFrame = xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.text.TextFrame");
+ bool bTextBox = false;
+ if (!bTextFrame)
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(mxSavedShape, uno::UNO_QUERY);
+ if (xPropertySet.is())
+ xPropertySet->getPropertyValue("TextBox") >>= bTextBox;
+ }
+ if (bTextFrame || bTextBox)
+ mxWpsContext.clear();
+ mxSavedShape.clear();
+}
+
+void SAL_CALL ShapeContextHandler::endUnknownElement
+(const OUString & Namespace,
+ const OUString & Name)
+{
+ uno::Reference<XFastContextHandler> xContextHandler(getContextHandler());
+
+ if (xContextHandler.is())
+ xContextHandler->endUnknownElement(Namespace, Name);
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ShapeContextHandler::createFastChildContext
+(::sal_Int32 Element,
+ const uno::Reference< xml::sax::XFastAttributeList > & Attribs)
+{
+ uno::Reference< xml::sax::XFastContextHandler > xResult;
+ uno::Reference< xml::sax::XFastContextHandler > xContextHandler(getContextHandler(Element));
+
+ if (xContextHandler.is())
+ xResult.set(xContextHandler->createFastChildContext
+ (Element, Attribs));
+
+ return xResult;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ShapeContextHandler::createUnknownChildContext
+(const OUString & Namespace,
+ const OUString & Name,
+ const uno::Reference< xml::sax::XFastAttributeList > & Attribs)
+{
+ uno::Reference<XFastContextHandler> xContextHandler(getContextHandler());
+
+ if (xContextHandler.is())
+ return xContextHandler->createUnknownChildContext
+ (Namespace, Name, Attribs);
+
+ return uno::Reference< xml::sax::XFastContextHandler >();
+}
+
+void SAL_CALL ShapeContextHandler::characters(const OUString & aChars)
+{
+ uno::Reference<XFastContextHandler> xContextHandler(getContextHandler());
+
+ if (xContextHandler.is())
+ xContextHandler->characters(aChars);
+}
+
+namespace // helpers for case mxWordprocessingCanvasContext
+{
+ void lcl_createShapeMap(oox::drawingml::ShapePtr rShapePtr,
+ oox::drawingml::ShapeIdMap& rShapeMap)
+{
+ std::vector< ShapePtr >& rChildren = rShapePtr->getChildren();
+ if (rChildren.empty())
+ return;
+ for (auto& pIt : rChildren)
+ {
+ rShapeMap[pIt->getId()] = pIt; // add child itself
+ lcl_createShapeMap(pIt, rShapeMap); // and all its descendants
+ }
+}
+
+} // end anonymous namespace
+
+uno::Reference< drawing::XShape >
+ShapeContextHandler::getShape()
+{
+ uno::Reference< drawing::XShape > xResult;
+ uno::Reference< drawing::XShapes > xShapes = mxDrawPage;
+
+ if (mxShapeFilterBase && xShapes.is())
+ {
+ if ( getContextHandler() == getDrawingShapeContext() )
+ {
+ mpDrawing->finalizeFragmentImport();
+ if( std::shared_ptr< vml::ShapeBase > pShape = mpDrawing->getShapes().takeLastShape() )
+ xResult = pShape->convertAndInsert( xShapes );
+ // Only now remove the recursion mark, because getShape() is called in writerfilter
+ // after endFastElement().
+ mpDrawing->getShapes().popMark();
+ }
+ else if (mxDiagramShapeContext.is())
+ {
+ basegfx::B2DHomMatrix aMatrix;
+ if (mpShape->getExtDrawings().empty())
+ {
+ mpShape->addShape( *mxShapeFilterBase, mpThemePtr.get(), xShapes, aMatrix, mpShape->getFillProperties() );
+ xResult = mpShape->getXShape();
+ }
+ else
+ {
+ // Prerendered diagram output is available, then use that, and throw away the original result.
+ for (auto const& extDrawing : mpShape->getExtDrawings())
+ {
+ OUString aFragmentPath(mxDiagramShapeContext->getFragmentPathFromRelId(extDrawing));
+ oox::drawingml::ShapePtr pShapePtr = std::make_shared<Shape>( "com.sun.star.drawing.GroupShape" );
+ pShapePtr->setDiagramType();
+ mxShapeFilterBase->importFragment(new ShapeDrawingFragmentHandler(*mxShapeFilterBase, aFragmentPath, pShapePtr));
+ pShapePtr->setDiagramDoms(mpShape->getDiagramDoms());
+ pShapePtr->keepDiagramDrawing(*mxShapeFilterBase, aFragmentPath);
+
+ if (mpShape->getFontRefColorForNodes().isUsed())
+ applyFontRefColor(pShapePtr, mpShape->getFontRefColorForNodes());
+
+ // migrate IDiagramHelper to new oox::Shape (from mpShape which was loaded
+ // to pShapePtr where the geometry is now constructed)
+ mpShape->migrateDiagramHelperToNewShape(pShapePtr);
+
+ if (!mpShape->getChildren().empty())
+ {
+ // first child is diagram background - we want to keep it, as drawingML fallback doesn't contain it
+ auto& aChildren = pShapePtr->getChildren();
+ ShapePtr pBackground = mpShape->getChildren().front();
+ aChildren.insert(aChildren.begin(), pBackground);
+ }
+
+ pShapePtr->addShape( *mxShapeFilterBase, mpThemePtr.get(), xShapes, aMatrix, pShapePtr->getFillProperties() );
+ xResult = pShapePtr->getXShape();
+ }
+ mpShape.reset();
+ }
+ mxDiagramShapeContext.clear();
+ }
+ else if (mxLockedCanvasContext.is())
+ {
+ ShapePtr pShape = mxLockedCanvasContext->getShape();
+ if (pShape)
+ {
+ basegfx::B2DHomMatrix aMatrix;
+ pShape->addShape(*mxShapeFilterBase, mpThemePtr.get(), xShapes, aMatrix, pShape->getFillProperties());
+ xResult = pShape->getXShape();
+ mxLockedCanvasContext.clear();
+ }
+ }
+ else if (mxWordprocessingCanvasContext.is())
+ {
+ // group which represents the drawing canvas
+ ShapePtr pShape = mxWordprocessingCanvasContext->getShape();
+ if (pShape)
+ {
+ basegfx::B2DHomMatrix aMatrix;
+ pShape->addShape(*mxShapeFilterBase, mpThemePtr.get(), xShapes, aMatrix, pShape->getFillProperties());
+
+ // create a flat map of all shapes in the drawing canvas group.
+ oox::drawingml::ShapeIdMap aShapeMap;
+ lcl_createShapeMap(pShape, aShapeMap);
+
+ // Traverse aShapeMap and generate edge related properties.
+ for (auto& rIt : aShapeMap)
+ {
+ if ((rIt.second)->getServiceName() == "com.sun.star.drawing.ConnectorShape")
+ {
+ ConnectorHelper::applyConnections(rIt.second, aShapeMap);
+
+ if (rIt.second->getConnectorName() == u"bentConnector3"_ustr
+ || rIt.second->getConnectorName() == u"bentConnector4"_ustr
+ || rIt.second->getConnectorName() == u"bentConnector5"_ustr)
+ {
+ ConnectorHelper::applyBentHandleAdjustments(rIt.second);
+ }
+ else if (rIt.second->getConnectorName() == u"curvedConnector3"_ustr
+ || rIt.second->getConnectorName() == u"curvedConnector4"_ustr
+ || rIt.second->getConnectorName() == u"curvedConnector5"_ustr)
+ {
+ ConnectorHelper::applyCurvedHandleAdjustments(rIt.second);
+ }
+ // else use the default path of LibreOffice.
+ // curveConnector2 and bentConnector2 do not have handles.
+ }
+ }
+ xResult = pShape->getXShape();
+ mxWordprocessingCanvasContext.clear();
+ }
+ }
+ //NMSP_dmlChart == getNamespace( mnStartToken ) check is introduced to make sure that
+ //mnStartToken is set as NMSP_dmlChart in setStartToken.
+ //Only in case it is set then only the below block of code for ChartShapeContext should be executed.
+ else if (mxChartShapeContext.is() && (NMSP_dmlChart == getNamespace( getStartToken() )))
+ {
+ basegfx::B2DHomMatrix aMatrix;
+ oox::drawingml::ShapePtr xShapePtr( mxChartShapeContext->getShape());
+ // See SwXTextDocument::createInstance(), ODF import uses the same hack.
+ xShapePtr->setServiceName("com.sun.star.drawing.temporaryForXMLImportOLE2Shape");
+ xShapePtr->addShape( *mxShapeFilterBase, mpThemePtr.get(), xShapes, aMatrix, xShapePtr->getFillProperties() );
+ xResult = xShapePtr->getXShape();
+ mxChartShapeContext.clear();
+ }
+ else if (mxWpsContext.is())
+ {
+ ShapePtr pShape = mxWpsContext->getShape();
+ if (pShape)
+ {
+ basegfx::B2DHomMatrix aMatrix;
+ pShape->setPosition(maPosition);
+ pShape->addShape(*mxShapeFilterBase, mpThemePtr.get(), xShapes, aMatrix, pShape->getFillProperties());
+ xResult = pShape->getXShape();
+ mxSavedShape = xResult;
+ mxWpsContext.clear();
+ }
+ }
+ else if (mxWpgContext.is())
+ {
+ ShapePtr pShape = mxWpgContext->getShape();
+ if (pShape)
+ {
+ basegfx::B2DHomMatrix aMatrix;
+ pShape->setPosition(maPosition);
+ pShape->addShape(*mxShapeFilterBase, mpThemePtr.get(), xShapes, aMatrix, pShape->getFillProperties());
+ xResult = pShape->getXShape();
+ mxSavedShape = xResult;
+ mxWpgContext.clear();
+ }
+ }
+ else if (mpShape)
+ {
+ basegfx::B2DHomMatrix aTransformation;
+
+ if (maPosition.X != 0 || maPosition.Y != 0)
+ {
+ // We got a position from writerfilter/, store that in the shape, otherwise the
+ // position won't be set.
+ mpShape->setWps(true);
+ mpShape->setPosition(maPosition);
+ }
+
+ mpShape->addShape(*mxShapeFilterBase, mpThemePtr.get(), xShapes, aTransformation, mpShape->getFillProperties() );
+ xResult.set(mpShape->getXShape());
+ mxGraphicShapeContext.clear( );
+ }
+ }
+
+ if (xResult)
+ popStartToken();
+ return xResult;
+}
+
+void ShapeContextHandler::setDrawPage(const css::uno::Reference< css::drawing::XDrawPage > & the_value)
+{
+ mxDrawPage = the_value;
+}
+
+void ShapeContextHandler::setModel(const css::uno::Reference< css::frame::XModel > & the_value)
+{
+ if( !mxShapeFilterBase.is() )
+ throw uno::RuntimeException();
+ uno::Reference<lang::XComponent> xComp(the_value, uno::UNO_QUERY_THROW);
+ mxShapeFilterBase->setTargetDocument(xComp);
+}
+
+void ShapeContextHandler::setRelationFragmentPath(const OUString & the_value)
+{
+ msRelationFragmentPath = the_value;
+}
+
+sal_Int32 ShapeContextHandler::getStartToken() const
+{
+ assert(mnStartTokenStack.size() && "This stack must not be empty!");
+ return mnStartTokenStack.top();
+}
+
+void ShapeContextHandler::popStartToken()
+{
+ if (mnStartTokenStack.size() > 1)
+ mnStartTokenStack.pop();
+}
+
+void ShapeContextHandler::pushStartToken( sal_Int32 _starttoken )
+{
+ mnStartTokenStack.push(_starttoken);
+}
+
+void ShapeContextHandler::setPosition(const awt::Point& rPosition)
+{
+ maPosition = rPosition;
+}
+
+void ShapeContextHandler::setSize(const awt::Size& rSize)
+{
+ maSize = rSize;
+}
+
+void ShapeContextHandler::setDocumentProperties(const uno::Reference<document::XDocumentProperties>& xDocProps)
+{
+ mxDocumentProperties = xDocProps;
+ mxShapeFilterBase->checkDocumentProperties(mxDocumentProperties);
+}
+
+void ShapeContextHandler::setMediaDescriptor(const uno::Sequence<beans::PropertyValue>& rMediaDescriptor)
+{
+ maMediaDescriptor = rMediaDescriptor;
+}
+
+void ShapeContextHandler::setGraphicMapper(css::uno::Reference<css::graphic::XGraphicMapper> const & rxGraphicMapper)
+{
+ mxShapeFilterBase->setGraphicMapper(rxGraphicMapper);
+}
+
+void ShapeContextHandler::applyFontRefColor(const oox::drawingml::ShapePtr& pShape,
+ const oox::drawingml::Color& rFontRefColor)
+{
+ pShape->getShapeStyleRefs()[XML_fontRef].maPhClr = rFontRefColor;
+ std::vector<oox::drawingml::ShapePtr>& vChildren = pShape->getChildren();
+ for (auto const& child : vChildren)
+ {
+ applyFontRefColor(child, rFontRefColor);
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/shape/ShapeDrawingFragmentHandler.cxx b/oox/source/shape/ShapeDrawingFragmentHandler.cxx
new file mode 100644
index 0000000000..456f7df6c8
--- /dev/null
+++ b/oox/source/shape/ShapeDrawingFragmentHandler.cxx
@@ -0,0 +1,49 @@
+/* -*- 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/.
+ */
+
+#include <oox/shape/ShapeDrawingFragmentHandler.hxx>
+
+#include <oox/drawingml/shapegroupcontext.hxx>
+#include <oox/token/namespaces.hxx>
+#include <utility>
+
+using namespace com::sun::star;
+
+namespace oox::shape {
+
+ShapeDrawingFragmentHandler::ShapeDrawingFragmentHandler(oox::core::XmlFilterBase& rFilter, const OUString& rFragmentPath, oox::drawingml::ShapePtr pGroupShapePtr)
+ : FragmentHandler2(rFilter, rFragmentPath)
+ , mpGroupShapePtr(std::move(pGroupShapePtr))
+{
+}
+
+ShapeDrawingFragmentHandler::~ShapeDrawingFragmentHandler() noexcept
+{
+}
+
+void SAL_CALL ShapeDrawingFragmentHandler::endDocument()
+{
+}
+
+::oox::core::ContextHandlerRef ShapeDrawingFragmentHandler::onCreateContext(sal_Int32 Element, const AttributeList& /*Attribs*/ )
+{
+ switch( Element )
+ {
+ case DSP_TOKEN( spTree ):
+ return new oox::drawingml::ShapeGroupContext(*this, oox::drawingml::ShapePtr(nullptr), mpGroupShapePtr);
+ default:
+ break;
+ }
+
+ return this;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/shape/ShapeFilterBase.cxx b/oox/source/shape/ShapeFilterBase.cxx
new file mode 100644
index 0000000000..ad10c4fe67
--- /dev/null
+++ b/oox/source/shape/ShapeFilterBase.cxx
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <oox/shape/ShapeFilterBase.hxx>
+#include <oox/drawingml/chart/chartconverter.hxx>
+#include <oox/drawingml/themefragmenthandler.hxx>
+#include <oox/helper/graphichelper.hxx>
+#include <oox/ole/vbaproject.hxx>
+#include <oox/drawingml/theme.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
+
+namespace oox::shape {
+
+using namespace ::com::sun::star;
+
+ShapeFilterBase::ShapeFilterBase( const uno::Reference< uno::XComponentContext >& rxContext ) :
+ XmlFilterBase( rxContext ),
+#if ENABLE_WASM_STRIP_CHART
+ // WASM_CHART change
+ mxChartConv( )
+#else
+ mxChartConv( std::make_shared<::oox::drawingml::chart::ChartConverter>() )
+#endif
+{
+}
+
+ShapeFilterBase::~ShapeFilterBase()
+{
+}
+
+const ::oox::drawingml::Theme* ShapeFilterBase::getCurrentTheme() const
+{
+ return mpTheme.get();
+}
+
+std::shared_ptr<::oox::drawingml::Theme> ShapeFilterBase::getCurrentThemePtr() const
+{
+ return mpTheme;
+}
+
+void ShapeFilterBase::setCurrentTheme(const ::oox::drawingml::ThemePtr& pTheme)
+{
+ mpTheme = pTheme;
+}
+
+::oox::vml::Drawing* ShapeFilterBase::getVmlDrawing()
+{
+ return nullptr;
+}
+
+::oox::drawingml::table::TableStyleListPtr ShapeFilterBase::getTableStyles()
+{
+ return ::oox::drawingml::table::TableStyleListPtr();
+}
+
+::oox::drawingml::chart::ChartConverter* ShapeFilterBase::getChartConverter()
+{
+ return mxChartConv.get();
+}
+
+::oox::ole::VbaProject* ShapeFilterBase::implCreateVbaProject() const
+{
+ return new ::oox::ole::VbaProject( getComponentContext(), getModel(), u"Writer" );
+}
+
+OUString ShapeFilterBase::getImplementationName()
+{
+ return OUString();
+}
+
+namespace {
+
+/// Graphic helper for shapes, that can manage color schemes.
+class ShapeGraphicHelper : public GraphicHelper
+{
+public:
+ explicit ShapeGraphicHelper( const ShapeFilterBase& rFilter );
+ virtual ::Color getSchemeColor( sal_Int32 nToken ) const override;
+private:
+ const ShapeFilterBase& mrFilter;
+};
+
+}
+
+ShapeGraphicHelper::ShapeGraphicHelper( const ShapeFilterBase& rFilter ) :
+ GraphicHelper( rFilter.getComponentContext(), rFilter.getTargetFrame(), rFilter.getStorage() ),
+ mrFilter( rFilter )
+{
+}
+
+::Color ShapeGraphicHelper::getSchemeColor( sal_Int32 nToken ) const
+{
+ return mrFilter.getSchemeColor( nToken );
+}
+
+GraphicHelper* ShapeFilterBase::implCreateGraphicHelper() const
+{
+ GraphicHelper* pGraphicHelper = new ShapeGraphicHelper(*this);
+ if (mxGraphicMapper.is())
+ pGraphicHelper->setGraphicMapper(mxGraphicMapper);
+ return pGraphicHelper;
+}
+
+::Color ShapeFilterBase::getSchemeColor( sal_Int32 nToken ) const
+{
+ ::Color nColor;
+
+ if (mpTheme)
+ mpTheme->getClrScheme().getColor( nToken, nColor );
+
+ return nColor;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/shape/WordprocessingCanvasContext.cxx b/oox/source/shape/WordprocessingCanvasContext.cxx
new file mode 100644
index 0000000000..9365e387f5
--- /dev/null
+++ b/oox/source/shape/WordprocessingCanvasContext.cxx
@@ -0,0 +1,110 @@
+/* -*- 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/.
+ */
+
+#include "WordprocessingCanvasContext.hxx"
+#include "WpsContext.hxx"
+#include "WpgContext.hxx"
+#include <drawingml/customshapeproperties.hxx>
+#include <drawingml/effectpropertiescontext.hxx>
+#include <drawingml/fillproperties.hxx>
+#include <drawingml/shapepropertiescontext.hxx>
+#include <oox/drawingml/connectorshapecontext.hxx>
+#include <oox/drawingml/drawingmltypes.hxx>
+#include <oox/drawingml/graphicshapecontext.hxx>
+#include <oox/drawingml/shape.hxx>
+#include <oox/drawingml/shapecontext.hxx>
+#include <oox/drawingml/shapegroupcontext.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <sal/log.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdobj.hxx>
+
+using namespace com::sun::star;
+
+namespace oox::shape
+{
+WordprocessingCanvasContext::WordprocessingCanvasContext(FragmentHandler2 const& rParent,
+ css::awt::Size& rSize)
+ : FragmentHandler2(rParent)
+ , m_bFullWPGSupport(true)
+{
+ mpShapePtr = std::make_shared<oox::drawingml::Shape>("com.sun.star.drawing.GroupShape");
+ mpShapePtr->setSize(rSize);
+ mpShapePtr->setWordprocessingCanvas(true); // will be "WordprocessingCanvas" in InteropGrabBag
+ mpShapePtr->setWps(true);
+ oox::drawingml::ShapePtr pBackground
+ = std::make_shared<oox::drawingml::Shape>("com.sun.star.drawing.CustomShape");
+ pBackground->getCustomShapeProperties()->setShapePresetType(XML_rect);
+ pBackground->setSize(rSize);
+ pBackground->setWordprocessingCanvas(true);
+ pBackground->setWPGChild(true);
+ pBackground->setWps(true);
+ // Fill and Line properties will follow in wpc:bg and wpc:whole child elements of wpc element
+ mpShapePtr->addChild(pBackground);
+ mpShapePtr->setChildSize(rSize);
+}
+
+WordprocessingCanvasContext::~WordprocessingCanvasContext() = default;
+
+::oox::core::ContextHandlerRef
+WordprocessingCanvasContext::onCreateContext(sal_Int32 nElementToken,
+ const ::oox::AttributeList& /*rAttribs*/)
+{
+ switch (getBaseToken(nElementToken))
+ {
+ case XML_wpc:
+ SAL_INFO("oox", "WordprocessingCanvasContext::createFastChildContext: wpc: "
+ << getBaseToken(nElementToken));
+ break;
+ case XML_bg: //CT_BackgroundFormatting
+ return new oox::drawingml::ShapePropertiesContext(*this,
+ *(getShape()->getChildren().front()));
+ case XML_whole: // CT_WholeE2oFormatting
+ return new oox::drawingml::ShapePropertiesContext(*this,
+ *(getShape()->getChildren().front()));
+ case XML_wsp: // CT_WordprocessingShape
+ {
+ oox::drawingml::ShapePtr pShape = std::make_shared<oox::drawingml::Shape>(
+ "com.sun.star.drawing.CustomShape", /*bDefaultHeight=*/false);
+ return new oox::shape::WpsContext(*this, uno::Reference<drawing::XShape>(), mpShapePtr,
+ pShape);
+ }
+ case XML_pic: // CT_Picture
+ return new oox::drawingml::GraphicShapeContext(
+ *this, mpShapePtr,
+ std::make_shared<oox::drawingml::Shape>("com.sun.star.drawing.GraphicObjectShape"));
+ break;
+ case XML_graphicFrame: // CT_GraphicFrame
+ SAL_INFO("oox",
+ "WordprocessingCanvasContext::createFastChildContext: ToDo: graphicFrame: "
+ << getBaseToken(nElementToken));
+ break;
+ case XML_wgp: // CT_WordprocessingGroup
+ {
+ rtl::Reference<WpgContext> pWPGContext = new oox::shape::WpgContext(*this, mpShapePtr);
+ pWPGContext->setFullWPGSupport(m_bFullWPGSupport);
+ return pWPGContext;
+ }
+ default:
+ // includes case XML_contentPart
+ // Word uses this for Ink, as <w14:contentPart r:id="rId4"> for example. Thereby rId4 is
+ // a reference into the 'ink' folder in the docx package. Import of Ink is not
+ // implemented yet. In general it refers to arbitrary XML source.
+ SAL_WARN("oox",
+ "WordprocessingCanvasContext::createFastChildContext: unhandled element:"
+ << getBaseToken(nElementToken));
+ break;
+ }
+ return nullptr;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/shape/WordprocessingCanvasContext.hxx b/oox/source/shape/WordprocessingCanvasContext.hxx
new file mode 100644
index 0000000000..d4cc67f6a9
--- /dev/null
+++ b/oox/source/shape/WordprocessingCanvasContext.hxx
@@ -0,0 +1,39 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <oox/core/fragmenthandler2.hxx>
+#include <oox/drawingml/drawingmltypes.hxx>
+
+namespace oox::shape
+{
+/// Handles CT_WordprocessingCanvas, used for wpc element, which is a drawing canvas for Word.
+class WordprocessingCanvasContext final : public oox::core::FragmentHandler2
+{
+public:
+ // mpShapePtr points to the root of the group. rSize is the size of the background shape.
+ explicit WordprocessingCanvasContext(oox::core::FragmentHandler2 const& rParent,
+ css::awt::Size& rSize);
+ ~WordprocessingCanvasContext() override;
+
+ oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElementToken,
+ const ::oox::AttributeList& rAttribs) override;
+
+ oox::drawingml::ShapePtr getShape() { return mpShapePtr; }
+ const bool& isFullWPGSupport() const { return m_bFullWPGSupport; };
+ void setFullWPGSupport(bool bUse) { m_bFullWPGSupport = bUse; };
+
+private:
+ oox::drawingml::ShapePtr mpShapePtr;
+ bool m_bFullWPGSupport;
+};
+} // end namespace oox::shape
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/shape/WpgContext.cxx b/oox/source/shape/WpgContext.cxx
new file mode 100644
index 0000000000..339e7a70ea
--- /dev/null
+++ b/oox/source/shape/WpgContext.cxx
@@ -0,0 +1,95 @@
+/* -*- 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/.
+ */
+
+#include "WpgContext.hxx"
+#include "WpsContext.hxx"
+#include <sal/log.hxx>
+#include <drawingml/shapepropertiescontext.hxx>
+#include <oox/drawingml/shapegroupcontext.hxx>
+#include <oox/drawingml/graphicshapecontext.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+using namespace com::sun::star;
+
+namespace oox::shape
+{
+WpgContext::WpgContext(FragmentHandler2 const& rParent, const oox::drawingml::ShapePtr& pMaster)
+ : FragmentHandler2(rParent)
+ , m_bFullWPGSupport(false)
+{
+ mpShape = std::make_shared<oox::drawingml::Shape>("com.sun.star.drawing.GroupShape");
+ mpShape->setWps(true);
+ if (pMaster)
+ pMaster->addChild(mpShape);
+}
+
+WpgContext::~WpgContext() = default;
+
+oox::core::ContextHandlerRef WpgContext::onCreateContext(sal_Int32 nElementToken,
+ const oox::AttributeList& /*rAttribs*/)
+{
+ switch (getBaseToken(nElementToken))
+ {
+ case XML_wgp:
+ case XML_cNvGrpSpPr:
+ case XML_grpSpPr:
+ return new oox::drawingml::ShapePropertiesContext(*this, *mpShape);
+ case XML_wsp:
+ {
+ if (m_bFullWPGSupport)
+ {
+ oox::drawingml::ShapePtr pShape = std::make_shared<oox::drawingml::Shape>(
+ "com.sun.star.drawing.CustomShape", /*bDefaultHeight=*/false);
+ return new oox::shape::WpsContext(*this, uno::Reference<drawing::XShape>(), mpShape,
+ pShape);
+ }
+
+ // Don't set default character height, Writer has its own way to set
+ // the default, and if we don't set it here, editeng properly inherits
+ // it.
+ oox::drawingml::ShapePtr pShape = std::make_shared<oox::drawingml::Shape>(
+ "com.sun.star.drawing.CustomShape", /*bDefaultHeight=*/false);
+ return new oox::drawingml::ShapeContext(*this, mpShape, pShape);
+ }
+ case XML_pic:
+ return new oox::drawingml::GraphicShapeContext(
+ *this, mpShape,
+ std::make_shared<oox::drawingml::Shape>("com.sun.star.drawing.GraphicObjectShape"));
+ case XML_grpSp:
+ {
+ if (m_bFullWPGSupport)
+ {
+ rtl::Reference<WpgContext> pWPGShape = new oox::shape::WpgContext(*this, mpShape);
+ pWPGShape->setFullWPGSupport(m_bFullWPGSupport);
+ return pWPGShape;
+ }
+
+ return new oox::drawingml::ShapeGroupContext(
+ *this, mpShape,
+ std::make_shared<oox::drawingml::Shape>("com.sun.star.drawing.GroupShape"));
+ }
+ case XML_graphicFrame:
+ {
+ auto pShape = std::make_shared<oox::drawingml::Shape>(
+ "com.sun.star.drawing.GraphicObjectShape");
+ pShape->setWps(true);
+ return new oox::drawingml::GraphicalObjectFrameContext(*this, mpShape, pShape,
+ /*bEmbedShapesInChart=*/true);
+ }
+ default:
+ SAL_WARN("oox", "WpgContext::createFastChildContext: unhandled element: "
+ << getBaseToken(nElementToken));
+ break;
+ }
+ return nullptr;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/shape/WpgContext.hxx b/oox/source/shape/WpgContext.hxx
new file mode 100644
index 0000000000..edd11644b2
--- /dev/null
+++ b/oox/source/shape/WpgContext.hxx
@@ -0,0 +1,43 @@
+/* -*- 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/.
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_SHAPE_WPGCONTEXT_HXX
+#define INCLUDED_OOX_SOURCE_SHAPE_WPGCONTEXT_HXX
+
+#include <oox/core/fragmenthandler2.hxx>
+#include <oox/drawingml/drawingmltypes.hxx>
+
+namespace oox::shape
+{
+/// Wpg is the drawingML equivalent of v:group.
+class WpgContext final : public oox::core::FragmentHandler2
+{
+public:
+ explicit WpgContext(oox::core::FragmentHandler2 const& rParent,
+ const oox::drawingml::ShapePtr& pMaster);
+ ~WpgContext() override;
+
+ oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElementToken,
+ const oox::AttributeList& rAttribs) override;
+
+ const oox::drawingml::ShapePtr& getShape() const { return mpShape; }
+
+ const bool& isFullWPGSupport() const { return m_bFullWPGSupport; };
+ void setFullWPGSupport(bool bUse) { m_bFullWPGSupport = bUse; };
+
+private:
+ oox::drawingml::ShapePtr mpShape;
+
+ bool m_bFullWPGSupport;
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/shape/WpsContext.cxx b/oox/source/shape/WpsContext.cxx
new file mode 100644
index 0000000000..fae7048563
--- /dev/null
+++ b/oox/source/shape/WpsContext.cxx
@@ -0,0 +1,982 @@
+/* -*- 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/.
+ */
+
+#include "WpsContext.hxx"
+#include "WpgContext.hxx"
+#include "WordprocessingCanvasContext.hxx"
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <drawingml/customshapegeometry.hxx>
+#include <drawingml/customshapeproperties.hxx>
+#include <drawingml/fontworkhelpers.hxx>
+#include <drawingml/textbody.hxx>
+#include <drawingml/textbodyproperties.hxx>
+#include <oox/drawingml/color.hxx>
+#include <oox/drawingml/connectorshapecontext.hxx>
+#include <oox/drawingml/drawingmltypes.hxx>
+#include <oox/drawingml/shape.hxx>
+#include <oox/drawingml/shapepropertymap.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <svx/svdoashp.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/drawing/HomogenMatrix3.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/geometry/IntegerRectangle2D.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/text/XTextCursor.hpp>
+#include <com/sun/star/text/WritingMode.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+
+#include <optional>
+
+using namespace com::sun::star;
+
+namespace
+{
+bool lcl_getTextPropsFromFrameText(const uno::Reference<text::XText>& xText,
+ std::vector<beans::PropertyValue>& rTextPropVec)
+{
+ if (!xText.is())
+ return false;
+ uno::Reference<text::XTextCursor> xTextCursor = xText->createTextCursor();
+ xTextCursor->gotoStart(false);
+ xTextCursor->gotoEnd(true);
+ uno::Reference<container::XEnumerationAccess> paraEnumAccess(xText, uno::UNO_QUERY);
+ if (!paraEnumAccess.is())
+ return false;
+ uno::Reference<container::XEnumeration> paraEnum(paraEnumAccess->createEnumeration());
+ while (paraEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xParagraph(paraEnum->nextElement(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> runEnumAccess(xParagraph, uno::UNO_QUERY);
+ if (!runEnumAccess.is())
+ continue;
+ uno::Reference<container::XEnumeration> runEnum = runEnumAccess->createEnumeration();
+ while (runEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xRun(runEnum->nextElement(), uno::UNO_QUERY);
+ if (xRun->getString().isEmpty())
+ continue;
+ uno::Reference<beans::XPropertySet> xRunPropSet(xRun, uno::UNO_QUERY);
+ if (!xRunPropSet.is())
+ continue;
+ auto xRunPropSetInfo = xRunPropSet->getPropertySetInfo();
+ if (!xRunPropSetInfo.is())
+ continue;
+
+ // We have found a non-empty run. Collect its properties.
+ auto aRunPropInfoSequence = xRunPropSetInfo->getProperties();
+ for (const beans::Property& aProp : aRunPropInfoSequence)
+ {
+ rTextPropVec.push_back(comphelper::makePropertyValue(
+ aProp.Name, xRunPropSet->getPropertyValue(aProp.Name)));
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// CharInteropGrabBag puts all attributes of an element into a property with Name="attributes" and
+// Value being a sequence of the attributes. This methods finds the value of an individual rName
+// attribute and puts it into rValue parameter. If it does not find it, rValue is unchanged and
+// the method returns false, otherwise it returns true.
+bool lcl_getAttributeAsString(const uno::Sequence<beans::PropertyValue>& aPropertyValueAsSeq,
+ const OUString& rName, OUString& rValue)
+{
+ comphelper::SequenceAsHashMap aPropertyValueAsMap(aPropertyValueAsSeq);
+ uno::Sequence<beans::PropertyValue> aAttributesSeq;
+ if (!((aPropertyValueAsMap.getValue("attributes") >>= aAttributesSeq)
+ && aAttributesSeq.hasElements()))
+ return false;
+ comphelper::SequenceAsHashMap aAttributesMap(aAttributesSeq);
+ OUString sRet;
+ if (!(aAttributesMap.getValue(rName) >>= sRet))
+ return false;
+ rValue = sRet;
+ return true;
+}
+
+// Same as above for a number as attribute value
+bool lcl_getAttributeAsNumber(const uno::Sequence<beans::PropertyValue>& rPropertyValueAsSeq,
+ const OUString& rName, sal_Int32& rValue)
+{
+ comphelper::SequenceAsHashMap aPropertyValueAsMap(rPropertyValueAsSeq);
+ uno::Sequence<beans::PropertyValue> aAttributesSeq;
+ if (!((aPropertyValueAsMap.getValue("attributes") >>= aAttributesSeq)
+ && aAttributesSeq.hasElements()))
+ return false;
+ comphelper::SequenceAsHashMap aAttributesMap(aAttributesSeq);
+ sal_Int32 nRet;
+ if (!(aAttributesMap.getValue(rName) >>= nRet))
+ return false;
+ rValue = nRet;
+ return true;
+}
+
+void lcl_getColorTransformationsFromPropSeq(const uno::Sequence<beans::PropertyValue>& rPropSeq,
+ oox::drawingml::Color& rColor)
+{
+ auto isValidPropName = [](const OUString& rName) -> bool {
+ return rName == u"tint" || rName == u"shade" || rName == u"alpha" || rName == u"hueMod"
+ || rName == u"sat" || rName == u"satOff" || rName == u"satMod" || rName == u"lum"
+ || rName == u"lumOff" || rName == u"lumMod";
+ };
+ for (auto it = rPropSeq.begin(); it < rPropSeq.end(); ++it)
+ {
+ if (isValidPropName((*it).Name))
+ {
+ uno::Sequence<beans::PropertyValue> aValueSeq;
+ sal_Int32 nNumber(0); // dummy value to make compiler happy, "val" should exist
+ if (((*it).Value >>= aValueSeq)
+ && lcl_getAttributeAsNumber(aValueSeq, u"val"_ustr, nNumber))
+ {
+ // char w14:alpha contains transparency, whereas shape fill a:alpha contains opacity.
+ if ((*it).Name == u"alpha")
+ rColor.addTransformation(
+ oox::NMSP_dml | oox::AttributeConversion::decodeToken((*it).Name),
+ oox::drawingml::MAX_PERCENT - nNumber);
+ else
+ rColor.addTransformation(
+ oox::NMSP_w14 | oox::AttributeConversion::decodeToken((*it).Name), nNumber);
+ }
+ }
+ }
+}
+
+// Expected: rPropSeq contains a property "schemeClr" or a property "srgbClr".
+bool lcl_getColorFromPropSeq(const uno::Sequence<beans::PropertyValue>& rPropSeq,
+ oox::drawingml::Color& rColor)
+{
+ bool bColorFound = false;
+ comphelper::SequenceAsHashMap aPropMap(rPropSeq);
+ uno::Sequence<beans::PropertyValue> aColorDetailSeq;
+ if (aPropMap.getValue(u"schemeClr"_ustr) >>= aColorDetailSeq)
+ {
+ OUString sColorString;
+ bColorFound = lcl_getAttributeAsString(aColorDetailSeq, u"val"_ustr, sColorString);
+ if (bColorFound)
+ {
+ sal_Int32 nColorToken = oox::AttributeConversion::decodeToken(sColorString);
+ rColor.setSchemeClr(nColorToken);
+ rColor.setSchemeName(sColorString);
+ }
+ }
+ if (!bColorFound && (aPropMap.getValue(u"srgbClr"_ustr) >>= aColorDetailSeq))
+ {
+ OUString sColorString;
+ bColorFound = lcl_getAttributeAsString(aColorDetailSeq, u"val"_ustr, sColorString);
+ if (bColorFound)
+ {
+ sal_Int32 nColor = oox::AttributeConversion::decodeIntegerHex(sColorString);
+ rColor.setSrgbClr(nColor);
+ }
+ }
+ // Without color, color transformations are pointless.
+ if (bColorFound)
+ lcl_getColorTransformationsFromPropSeq(aColorDetailSeq, rColor);
+ return bColorFound;
+}
+
+void lcl_getFillDetailsFromPropSeq(const uno::Sequence<beans::PropertyValue>& rTextFillSeq,
+ oox::drawingml::FillProperties& rFillProperties)
+{
+ // rTextFillSeq should have an item containing either "noFill" or "solidFill" or "gradFill"
+ // property.
+ if (!rTextFillSeq.hasElements())
+ return;
+ comphelper::SequenceAsHashMap aTextFillMap(rTextFillSeq);
+ if (aTextFillMap.find(u"noFill"_ustr) != aTextFillMap.end())
+ {
+ rFillProperties.moFillType = oox::XML_noFill;
+ return;
+ }
+
+ uno::Sequence<beans::PropertyValue> aPropSeq;
+ if ((aTextFillMap.getValue(u"solidFill"_ustr) >>= aPropSeq) && aPropSeq.hasElements())
+ {
+ rFillProperties.moFillType = oox::XML_solidFill;
+ lcl_getColorFromPropSeq(aPropSeq, rFillProperties.maFillColor);
+ return;
+ }
+
+ if ((aTextFillMap.getValue(u"gradFill"_ustr) >>= aPropSeq) && aPropSeq.hasElements())
+ {
+ rFillProperties.moFillType = oox::XML_gradFill;
+ // aPropSeq should have two items. One is "gsLst" for the stop colors, the other is
+ // either "lin" or "path" for the kind of gradient.
+ // First get stop colors
+ comphelper::SequenceAsHashMap aPropMap(aPropSeq);
+ uno::Sequence<beans::PropertyValue> aGsLstSeq;
+ if (aPropMap.getValue("gsLst") >>= aGsLstSeq)
+ {
+ for (auto it = aGsLstSeq.begin(); it < aGsLstSeq.end(); ++it)
+ {
+ // (*it) is a bean::PropertyValue with Name="gs". Its Value is a property sequence.
+ uno::Sequence<beans::PropertyValue> aColorStopSeq;
+ if ((*it).Value >>= aColorStopSeq)
+ {
+ // aColorStopSeq should have an item for the color and an item for the position
+ sal_Int32 nPos;
+ oox::drawingml::Color aColor;
+ if (lcl_getAttributeAsNumber(aColorStopSeq, u"pos"_ustr, nPos)
+ && lcl_getColorFromPropSeq(aColorStopSeq, aColor))
+ {
+ // The position in maGradientStops is relative, thus in range [0.0;1.0].
+ double fPos = nPos / 100000.0;
+ rFillProperties.maGradientProps.maGradientStops.insert({ fPos, aColor });
+ }
+ }
+ }
+ }
+ // Now determine kind of gradient.
+ uno::Sequence<beans::PropertyValue> aKindSeq;
+ if (aPropMap.getValue("lin") >>= aKindSeq)
+ {
+ // aKindSeq contains the attributes "ang" and "scaled"
+ sal_Int32 nAngle; // in 1/60000 deg
+ if (lcl_getAttributeAsNumber(aKindSeq, "ang", nAngle))
+ rFillProperties.maGradientProps.moShadeAngle = nAngle;
+ OUString sScaledString;
+ if (lcl_getAttributeAsString(aKindSeq, "scaled", sScaledString))
+ rFillProperties.maGradientProps.moShadeScaled
+ = sScaledString == u"1" || sScaledString == u"true";
+ return;
+ }
+ if (aPropMap.getValue("path") >>= aKindSeq)
+ {
+ // aKindSeq contains the attribute "path" for the kind of path and a property "fillToRect"
+ // which defines the center rectangle of the gradient. The property "a:tileRect" known from
+ // fill of shapes does not exist in w14 namespace.
+ OUString sKind;
+ if (lcl_getAttributeAsString(aKindSeq, "path", sKind))
+ rFillProperties.maGradientProps.moGradientPath
+ = oox::AttributeConversion::decodeToken(sKind);
+ comphelper::SequenceAsHashMap aKindMap(aKindSeq);
+ uno::Sequence<beans::PropertyValue> aFillToRectSeq;
+ if (aKindMap.getValue("fillToRect") >>= aFillToRectSeq)
+ {
+ // The values l, t, r and b are not coordinates, but determine an offset from the
+ // edge of the bounding box of the shape. This unusual meaning of X1, Y1, X2 and
+ // Y2 is needed for method pushToPropMap() of FillProperties.
+ geometry::IntegerRectangle2D aRect;
+ if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"l"_ustr, aRect.X1))
+ aRect.X1 = 0;
+ if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"t"_ustr, aRect.Y1))
+ aRect.Y1 = 0;
+ if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"r"_ustr, aRect.X2))
+ aRect.X2 = 0;
+ if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"b"_ustr, aRect.Y2))
+ aRect.Y2 = 0;
+ rFillProperties.maGradientProps.moFillToRect = aRect;
+ }
+ }
+ return;
+ }
+}
+
+void lcl_getLineDetailsFromPropSeq(const uno::Sequence<beans::PropertyValue>& rTextOutlineSeq,
+ oox::drawingml::LineProperties& rLineProperties)
+{
+ if (!rTextOutlineSeq.hasElements())
+ {
+ rLineProperties.maLineFill.moFillType = oox::XML_noFill; // MS Office default
+ return;
+ }
+ // aTextOulineSeq contains e.g. "attributes" {w, cap, cmpd, ctr}, either
+ // "solidFill" or "gradFill or "noFill", and "prstDash" and "lineJoint" properties.
+
+ // Fill
+ lcl_getFillDetailsFromPropSeq(rTextOutlineSeq, rLineProperties.maLineFill);
+
+ // LineJoint
+ comphelper::SequenceAsHashMap aTextOutlineMap(rTextOutlineSeq);
+ if (aTextOutlineMap.find(u"bevel"_ustr) != aTextOutlineMap.end())
+ rLineProperties.moLineJoint = oox::XML_bevel;
+ else if (aTextOutlineMap.find(u"round"_ustr) != aTextOutlineMap.end())
+ rLineProperties.moLineJoint = oox::XML_round;
+ else if (aTextOutlineMap.find(u"miter"_ustr) != aTextOutlineMap.end())
+ {
+ // LineProperties has no member to store a miter limit. Therefore some heuristic is
+ // added here. 0 is default for attribute "lim" in MS Office. It is rendered same as bevel.
+ sal_Int32 nMiterLimit = aTextOutlineMap.getUnpackedValueOrDefault("lim", sal_Int32(0));
+ if (nMiterLimit == 0)
+ rLineProperties.moLineJoint = oox::XML_bevel;
+ else
+ rLineProperties.moLineJoint = oox::XML_miter;
+ }
+
+ // Dash
+ uno::Sequence<beans::PropertyValue> aDashSeq;
+ if (aTextOutlineMap.getValue(u"prstDash"_ustr) >>= aDashSeq)
+ {
+ // aDashSeq contains the attribute "val" with the kind of dash, e.g. "sysDot"
+ OUString sDashKind;
+ if (lcl_getAttributeAsString(aDashSeq, u"val"_ustr, sDashKind))
+ rLineProperties.moPresetDash = oox::AttributeConversion::decodeToken(sDashKind);
+ }
+ OUString sCapKind;
+ if (lcl_getAttributeAsString(rTextOutlineSeq, u"cap"_ustr, sCapKind))
+ rLineProperties.moLineCap = oox::AttributeConversion::decodeToken(sCapKind);
+
+ // Width
+ sal_Int32 nWidth; // EMU
+ if (lcl_getAttributeAsNumber(rTextOutlineSeq, u"w"_ustr, nWidth))
+ rLineProperties.moLineWidth = nWidth;
+
+ // Compound. LineProperties has a member for it, however Fontwork can currently only render "sng".
+ OUString sCompoundKind;
+ if (lcl_getAttributeAsString(rTextOutlineSeq, u"cmpd"_ustr, sCompoundKind))
+ rLineProperties.moLineCompound = oox::AttributeConversion::decodeToken(sCompoundKind);
+
+ // Align. LineProperties has no member for attribute "algn".
+
+ return;
+}
+
+oox::drawingml::LineProperties
+lcl_generateLinePropertiesFromTextProps(const comphelper::SequenceAsHashMap& aTextPropMap)
+{
+ oox::drawingml::LineProperties aLineProperties;
+ aLineProperties.maLineFill.moFillType = oox::XML_noFill; // default
+
+ // Get property "textOutline" from aTextPropMap
+ uno::Sequence<beans::PropertyValue> aCharInteropGrabBagSeq;
+ if (!(aTextPropMap.getValue(u"CharInteropGrabBag"_ustr) >>= aCharInteropGrabBagSeq))
+ return aLineProperties;
+ if (!aCharInteropGrabBagSeq.hasElements())
+ return aLineProperties;
+ comphelper::SequenceAsHashMap aCharInteropGrabBagMap(aCharInteropGrabBagSeq);
+ beans::PropertyValue aProp;
+ if (!(aCharInteropGrabBagMap.getValue(u"CharTextOutlineTextEffect"_ustr) >>= aProp))
+ return aLineProperties;
+ uno::Sequence<beans::PropertyValue> aTextOutlineSeq;
+ if (!(aProp.Name == "textOutline" && (aProp.Value >>= aTextOutlineSeq)
+ && aTextOutlineSeq.hasElements()))
+ return aLineProperties;
+
+ // Copy line properties from aTextOutlineSeq to aLineProperties
+ lcl_getLineDetailsFromPropSeq(aTextOutlineSeq, aLineProperties);
+ return aLineProperties;
+}
+
+oox::drawingml::FillProperties
+lcl_generateFillPropertiesFromTextProps(const comphelper::SequenceAsHashMap& rTextPropMap)
+{
+ oox::drawingml::FillProperties aFillProperties;
+ aFillProperties.moFillType = oox::XML_solidFill; // default
+ // Theme color supersedes direct color. textFill supersedes theme color. Theme color and textFill
+ // are in CharInteropGrabBag.
+ uno::Sequence<beans::PropertyValue> aCharInteropGrabBagSeq;
+ if ((rTextPropMap.getValue(u"CharInteropGrabBag"_ustr) >>= aCharInteropGrabBagSeq)
+ && aCharInteropGrabBagSeq.hasElements())
+ {
+ // Handle case textFill
+ comphelper::SequenceAsHashMap aCharInteropGrabBagMap(aCharInteropGrabBagSeq);
+ beans::PropertyValue aProp;
+ if (aCharInteropGrabBagMap.getValue(u"CharTextFillTextEffect"_ustr) >>= aProp)
+ {
+ uno::Sequence<beans::PropertyValue> aTextFillSeq;
+ if (aProp.Name == "textFill" && (aProp.Value >>= aTextFillSeq)
+ && aTextFillSeq.hasElements())
+ {
+ // Copy fill properties from aTextFillSeq to aFillProperties
+ lcl_getFillDetailsFromPropSeq(aTextFillSeq, aFillProperties);
+ return aFillProperties;
+ }
+ }
+
+ // no textFill, look for theme color, tint and shade
+ bool bColorFound(false);
+ OUString sColorString;
+ if (aCharInteropGrabBagMap.getValue("CharThemeOriginalColor") >>= sColorString)
+ {
+ sal_Int32 nThemeOrigColor = oox::AttributeConversion::decodeIntegerHex(sColorString);
+ aFillProperties.maFillColor.setSrgbClr(nThemeOrigColor);
+ bColorFound = true;
+ }
+ if (aCharInteropGrabBagMap.getValue("CharThemeColor") >>= sColorString)
+ {
+ sal_Int32 nColorToken = oox::AttributeConversion::decodeToken(sColorString);
+ aFillProperties.maFillColor.setSchemeClr(nColorToken);
+ aFillProperties.maFillColor.setSchemeName(sColorString);
+ bColorFound = true;
+ // A character color has shade or tint, a shape color has lumMod and lumOff.
+ OUString sTransformString;
+ if (aCharInteropGrabBagMap.getValue("CharThemeColorTint") >>= sTransformString)
+ {
+ double fTint = oox::AttributeConversion::decodeIntegerHex(sTransformString);
+ fTint = fTint / 255.0 * oox::drawingml::MAX_PERCENT;
+ aFillProperties.maFillColor.addTransformation(OOX_TOKEN(w14, lumMod),
+ static_cast<sal_Int32>(fTint + 0.5));
+ double fOff = oox::drawingml::MAX_PERCENT - fTint;
+ aFillProperties.maFillColor.addTransformation(OOX_TOKEN(w14, lumOff),
+ static_cast<sal_Int32>(fOff + 0.5));
+ }
+ else if (aCharInteropGrabBagMap.getValue("CharThemeColorShade") >>= sTransformString)
+ {
+ double fShade = oox::AttributeConversion::decodeIntegerHex(sTransformString);
+ fShade = fShade / 255.0 * oox::drawingml::MAX_PERCENT;
+ aFillProperties.maFillColor.addTransformation(OOX_TOKEN(w14, lumMod),
+ static_cast<sal_Int32>(fShade + 0.5));
+ }
+ }
+ if (bColorFound)
+ return aFillProperties;
+ }
+
+ // Neither textFill nor theme color. Look for direct color.
+ sal_Int32 aCharColor = 0;
+ if (rTextPropMap.getValue(u"CharColor"_ustr) >>= aCharColor)
+ aFillProperties.maFillColor.setSrgbClr(aCharColor);
+ else
+ aFillProperties.maFillColor.setUnused();
+ return aFillProperties;
+}
+
+void lcl_applyShapePropsToShape(const uno::Reference<beans::XPropertySet>& xShapePropertySet,
+ const oox::drawingml::ShapePropertyMap& rShapeProps)
+{
+ for (const auto& rProp : rShapeProps.makePropertyValueSequence())
+ {
+ xShapePropertySet->setPropertyValue(rProp.Name, rProp.Value);
+ }
+}
+
+void lcl_setTextAnchorFromTextProps(const uno::Reference<beans::XPropertySet>& xShapePropertySet,
+ const comphelper::SequenceAsHashMap& aTextPropMap)
+{
+ // Fontwork does not evaluate paragraph alignment but uses text anchor instead
+ auto eHorzAdjust(drawing::TextHorizontalAdjust_CENTER);
+ sal_Int16 nParaAlign = sal_Int16(drawing::TextHorizontalAdjust_CENTER);
+ aTextPropMap.getValue("ParaAdjust") >>= nParaAlign;
+ switch (nParaAlign)
+ {
+ case sal_Int16(style::ParagraphAdjust_LEFT):
+ eHorzAdjust = drawing::TextHorizontalAdjust_LEFT;
+ break;
+ case sal_Int16(style::ParagraphAdjust_RIGHT):
+ eHorzAdjust = drawing::TextHorizontalAdjust_RIGHT;
+ break;
+ default:
+ eHorzAdjust = drawing::TextHorizontalAdjust_CENTER;
+ }
+ xShapePropertySet->setPropertyValue("TextHorizontalAdjust", uno::Any(eHorzAdjust));
+ xShapePropertySet->setPropertyValue("TextVerticalAdjust",
+ uno::Any(drawing::TextVerticalAdjust_TOP));
+}
+
+void lcl_setTextPropsToShape(const uno::Reference<beans::XPropertySet>& xShapePropertySet,
+ std::vector<beans::PropertyValue>& aTextPropVec)
+{
+ auto xShapePropertySetInfo = xShapePropertySet->getPropertySetInfo();
+ if (!xShapePropertySetInfo.is())
+ return;
+ for (size_t i = 0; i < aTextPropVec.size(); ++i)
+ {
+ if (xShapePropertySetInfo->hasPropertyByName(aTextPropVec[i].Name)
+ && !(xShapePropertySetInfo->getPropertyByName(aTextPropVec[i].Name).Attributes
+ & beans::PropertyAttribute::READONLY)
+ && aTextPropVec[i].Name != u"CharInteropGrabBag")
+ {
+ xShapePropertySet->setPropertyValue(aTextPropVec[i].Name, aTextPropVec[i].Value);
+ }
+ }
+}
+
+void lcl_applyUsedTextPropsToAllTextRuns(uno::Reference<text::XText>& xText,
+ const std::vector<beans::PropertyValue>& aTextPropVec)
+{
+ if (!xText.is())
+ return;
+ uno::Reference<text::XTextCursor> xTextCursor = xText->createTextCursor();
+ xTextCursor->gotoStart(false);
+ xTextCursor->gotoEnd(true);
+ uno::Reference<container::XEnumerationAccess> paraEnumAccess(xText, uno::UNO_QUERY);
+ if (!paraEnumAccess.is())
+ return;
+ uno::Reference<container::XEnumeration> paraEnum(paraEnumAccess->createEnumeration());
+ while (paraEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xParagraph(paraEnum->nextElement(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> runEnumAccess(xParagraph, uno::UNO_QUERY);
+ if (!runEnumAccess.is())
+ continue;
+ uno::Reference<container::XEnumeration> runEnum = runEnumAccess->createEnumeration();
+ while (runEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xRun(runEnum->nextElement(), uno::UNO_QUERY);
+ if (xRun->getString().isEmpty())
+ continue;
+ uno::Reference<beans::XPropertySet> xRunPropSet(xRun, uno::UNO_QUERY);
+ if (!xRunPropSet.is())
+ continue;
+ auto xRunPropSetInfo = xRunPropSet->getPropertySetInfo();
+ if (!xRunPropSetInfo.is())
+ continue;
+
+ for (size_t i = 0; i < aTextPropVec.size(); ++i)
+ {
+ if (xRunPropSetInfo->hasPropertyByName(aTextPropVec[i].Name)
+ && !(xRunPropSetInfo->getPropertyByName(aTextPropVec[i].Name).Attributes
+ & beans::PropertyAttribute::READONLY))
+ xRunPropSet->setPropertyValue(aTextPropVec[i].Name, aTextPropVec[i].Value);
+ }
+ }
+ }
+}
+} // anonymous namespace
+
+namespace oox::shape
+{
+WpsContext::WpsContext(ContextHandler2Helper const& rParent, uno::Reference<drawing::XShape> xShape,
+ const drawingml::ShapePtr& pMasterShapePtr,
+ const drawingml::ShapePtr& pShapePtr)
+ : ShapeContext(rParent, pMasterShapePtr, pShapePtr)
+ , mxShape(std::move(xShape))
+{
+ if (mpShapePtr)
+ mpShapePtr->setWps(true);
+
+ if (const auto pParent = dynamic_cast<const WpgContext*>(&rParent))
+ m_bHasWPGParent = pParent->isFullWPGSupport();
+ else if (dynamic_cast<const WordprocessingCanvasContext*>(&rParent))
+ m_bHasWPGParent = true;
+ else
+ m_bHasWPGParent = false;
+
+ if ((pMasterShapePtr && pMasterShapePtr->isInWordprocessingCanvas())
+ || dynamic_cast<const WordprocessingCanvasContext*>(&rParent) != nullptr)
+ pShapePtr->setWordprocessingCanvas(true);
+}
+
+WpsContext::~WpsContext() = default;
+
+oox::core::ContextHandlerRef WpsContext::onCreateContext(sal_Int32 nElementToken,
+ const oox::AttributeList& rAttribs)
+{
+ switch (getBaseToken(nElementToken))
+ {
+ case XML_wsp:
+ break;
+ case XML_cNvCnPr:
+ {
+ // It might be a connector shape in a wordprocessing canvas
+ // Replace the custom shape with a connector shape.
+ if (!mpShapePtr || !mpShapePtr->isInWordprocessingCanvas() || !mpMasterShapePtr)
+ break;
+ // Generate new shape
+ oox::drawingml::ShapePtr pShape = std::make_shared<oox::drawingml::Shape>(
+ "com.sun.star.drawing.ConnectorShape", false);
+ pShape->setConnectorShape(true);
+ pShape->setWps(true);
+ pShape->setWordprocessingCanvas(true);
+ // ToDo: Can only copy infos from mpShapePtr to pShape for which getter available.
+ pShape->setName(mpShapePtr->getName());
+ pShape->setId(mpShapePtr->getId());
+ pShape->setWPGChild(mpShapePtr->isWPGChild());
+ // And actually replace the shape.
+ mpShapePtr = pShape;
+ mpMasterShapePtr->getChildren().pop_back();
+ mpMasterShapePtr->getChildren().push_back(pShape);
+ return new oox::drawingml::ConnectorShapePropertiesContext(
+ *this, mpShapePtr, mpShapePtr->getConnectorShapeProperties());
+ }
+ case XML_bodyPr:
+ if (mxShape.is())
+ {
+ // no evaluation of attribute XML_rot, because Word ignores it, as of 2022-07.
+
+ uno::Reference<lang::XServiceInfo> xServiceInfo(mxShape, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
+ sal_Int32 nVert = rAttribs.getToken(XML_vert, XML_horz);
+ // Values 'wordArtVert' and 'wordArtVertRtl' are not implemented.
+ // Map them to other vert values.
+ if (nVert == XML_eaVert || nVert == XML_wordArtVertRtl)
+ {
+ xPropertySet->setPropertyValue("TextWritingMode",
+ uno::Any(text::WritingMode_TB_RL));
+ xPropertySet->setPropertyValue("WritingMode",
+ uno::Any(text::WritingMode2::TB_RL));
+ }
+ else if (nVert == XML_mongolianVert || nVert == XML_wordArtVert)
+ {
+ xPropertySet->setPropertyValue("WritingMode",
+ uno::Any(text::WritingMode2::TB_LR));
+ }
+ else if (nVert != XML_horz) // cases XML_vert and XML_vert270
+ {
+ // Hack to get same rendering as after the fix for tdf#87924. If shape rotation
+ // plus text direction results in upright text, use horizontal text direction.
+ // Remove hack when frame is able to rotate.
+
+ // Need transformation matrix since RotateAngle does not contain flip.
+ drawing::HomogenMatrix3 aMatrix;
+ xPropertySet->getPropertyValue("Transformation") >>= aMatrix;
+ basegfx::B2DHomMatrix aTransformation;
+ aTransformation.set(0, 0, aMatrix.Line1.Column1);
+ aTransformation.set(0, 1, aMatrix.Line1.Column2);
+ aTransformation.set(0, 2, aMatrix.Line1.Column3);
+ aTransformation.set(1, 0, aMatrix.Line2.Column1);
+ aTransformation.set(1, 1, aMatrix.Line2.Column2);
+ aTransformation.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);
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate = 0;
+ double fShearX = 0;
+ aTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
+ auto nRotate(static_cast<sal_uInt16>(NormAngle360(basegfx::rad2deg(fRotate))));
+ if ((nVert == XML_vert && nRotate == 270)
+ || (nVert == XML_vert270 && nRotate == 90))
+ {
+ xPropertySet->setPropertyValue("WritingMode",
+ uno::Any(text::WritingMode2::LR_TB));
+ // ToDo: Remember original vert value and remove hack on export.
+ }
+ else if (nVert == XML_vert)
+ xPropertySet->setPropertyValue("WritingMode",
+ uno::Any(text::WritingMode2::TB_RL90));
+ else // nVert == XML_vert270
+ xPropertySet->setPropertyValue("WritingMode",
+ uno::Any(text::WritingMode2::BT_LR));
+ }
+
+ if (bool bUpright = rAttribs.getBool(XML_upright, false))
+ {
+ uno::Sequence<beans::PropertyValue> aGrabBag;
+ xPropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
+ sal_Int32 length = aGrabBag.getLength();
+ aGrabBag.realloc(length + 1);
+ auto pGrabBag = aGrabBag.getArray();
+ pGrabBag[length].Name = "Upright";
+ pGrabBag[length].Value <<= bUpright;
+ xPropertySet->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag));
+ }
+
+ if (xServiceInfo.is())
+ {
+ // Handle inset attributes for Writer textframes.
+ sal_Int32 aInsets[] = { XML_lIns, XML_tIns, XML_rIns, XML_bIns };
+ std::optional<sal_Int32> oInsets[4];
+ for (std::size_t i = 0; i < SAL_N_ELEMENTS(aInsets); ++i)
+ {
+ std::optional<OUString> oValue = rAttribs.getString(aInsets[i]);
+ if (oValue.has_value())
+ oInsets[i] = oox::drawingml::GetCoordinate(oValue.value());
+ else
+ // Defaults from the spec: left/right: 91440 EMU, top/bottom: 45720 EMU
+ oInsets[i]
+ = (aInsets[i] == XML_lIns || aInsets[i] == XML_rIns) ? 254 : 127;
+ }
+ const OUString aShapeProps[]
+ = { OUString("TextLeftDistance"), OUString("TextUpperDistance"),
+ OUString("TextRightDistance"), OUString("TextLowerDistance") };
+ for (std::size_t i = 0; i < SAL_N_ELEMENTS(aShapeProps); ++i)
+ if (oInsets[i])
+ xPropertySet->setPropertyValue(aShapeProps[i], uno::Any(*oInsets[i]));
+ }
+
+ // Handle text vertical adjustment inside a text frame
+ if (rAttribs.hasAttribute(XML_anchor))
+ {
+ drawing::TextVerticalAdjust eAdjust
+ = drawingml::GetTextVerticalAdjust(rAttribs.getToken(XML_anchor, XML_t));
+ xPropertySet->setPropertyValue("TextVerticalAdjust", uno::Any(eAdjust));
+ }
+
+ // Apply character color of the shape to the shape's textbox.
+ uno::Reference<text::XText> xText(mxShape, uno::UNO_QUERY);
+ uno::Any xCharColor = xPropertySet->getPropertyValue("CharColor");
+ Color aColor = COL_AUTO;
+ if ((xCharColor >>= aColor) && aColor != COL_AUTO)
+ {
+ // tdf#135923 Apply character color of the shape to the textrun
+ // when the character color of the textrun is default.
+ // tdf#153791 But only if the run has no background color (shd element in OOXML)
+ if (uno::Reference<container::XEnumerationAccess> paraEnumAccess{
+ xText, uno::UNO_QUERY })
+ {
+ uno::Reference<container::XEnumeration> paraEnum(
+ paraEnumAccess->createEnumeration());
+
+ while (paraEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xParagraph(paraEnum->nextElement(),
+ uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> runEnumAccess(
+ xParagraph, uno::UNO_QUERY);
+ if (!runEnumAccess.is())
+ continue;
+ if (uno::Reference<beans::XPropertySet> xParaPropSet{ xParagraph,
+ uno::UNO_QUERY })
+ if ((xParaPropSet->getPropertyValue("ParaBackColor") >>= aColor)
+ && aColor != COL_AUTO)
+ continue;
+
+ uno::Reference<container::XEnumeration> runEnum
+ = runEnumAccess->createEnumeration();
+
+ while (runEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xRun(runEnum->nextElement(),
+ uno::UNO_QUERY);
+ const uno::Reference<beans::XPropertyState> xRunState(
+ xRun, uno::UNO_QUERY);
+ if (!xRunState
+ || xRunState->getPropertyState("CharColor")
+ == beans::PropertyState_DEFAULT_VALUE)
+ {
+ uno::Reference<beans::XPropertySet> xRunPropSet(xRun,
+ uno::UNO_QUERY);
+ if (!xRunPropSet)
+ continue;
+ if ((xRunPropSet->getPropertyValue("CharBackColor") >>= aColor)
+ && aColor != COL_AUTO)
+ continue;
+ if (!(xRunPropSet->getPropertyValue("CharColor") >>= aColor)
+ || aColor == COL_AUTO)
+ xRunPropSet->setPropertyValue("CharColor", xCharColor);
+ }
+ }
+ }
+ }
+ }
+
+ auto nWrappingType = rAttribs.getToken(XML_wrap, XML_square);
+ xPropertySet->setPropertyValue("TextWordWrap",
+ uno::Any(nWrappingType == XML_square));
+
+ return this;
+ }
+ else if (m_bHasWPGParent && mpShapePtr)
+ {
+ // this WPS context has to be inside a WPG shape, so the <BodyPr> element
+ // cannot be applied to mxShape member, use mpShape instead, and after the
+ // the parent shape finished, apply it for its children.
+ mpShapePtr->setWPGChild(true);
+ oox::drawingml::TextBodyPtr pTextBody;
+ pTextBody.reset(new oox::drawingml::TextBody());
+
+ if (rAttribs.hasAttribute(XML_anchor))
+ {
+ drawing::TextVerticalAdjust eAdjust
+ = drawingml::GetTextVerticalAdjust(rAttribs.getToken(XML_anchor, XML_t));
+ pTextBody->getTextProperties().meVA = eAdjust;
+ }
+
+ sal_Int32 aInsets[] = { XML_lIns, XML_tIns, XML_rIns, XML_bIns };
+ for (int i = 0; i < 4; ++i)
+ {
+ if (rAttribs.hasAttribute(XML_lIns))
+ {
+ std::optional<OUString> oValue = rAttribs.getString(aInsets[i]);
+ if (oValue.has_value())
+ pTextBody->getTextProperties().moInsets[i]
+ = oox::drawingml::GetCoordinate(oValue.value());
+ else
+ // Defaults from the spec: left/right: 91440 EMU, top/bottom: 45720 EMU
+ pTextBody->getTextProperties().moInsets[i]
+ = (aInsets[i] == XML_lIns || aInsets[i] == XML_rIns) ? 254 : 127;
+ }
+ }
+
+ mpShapePtr->setTextBody(pTextBody);
+ }
+ break;
+ case XML_noAutofit:
+ case XML_spAutoFit:
+ {
+ uno::Reference<lang::XServiceInfo> xServiceInfo(mxShape, uno::UNO_QUERY);
+ // We can't use oox::drawingml::TextBodyPropertiesContext here, as this
+ // is a child context of bodyPr, so the shape is already sent: we need
+ // to alter the XShape directly.
+ uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
+ if (xPropertySet.is())
+ {
+ if (xServiceInfo->supportsService("com.sun.star.text.TextFrame"))
+ xPropertySet->setPropertyValue(
+ "FrameIsAutomaticHeight",
+ uno::Any(getBaseToken(nElementToken) == XML_spAutoFit));
+ else
+ xPropertySet->setPropertyValue(
+ "TextAutoGrowHeight",
+ uno::Any(getBaseToken(nElementToken) == XML_spAutoFit));
+ }
+ }
+ break;
+ case XML_prstTxWarp:
+ if (rAttribs.hasAttribute(XML_prst))
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
+ if (xPropertySet.is())
+ {
+ std::optional<OUString> presetShapeName = rAttribs.getString(XML_prst);
+ const OUString& preset = presetShapeName.value();
+ comphelper::SequenceAsHashMap aCustomShapeGeometry(
+ xPropertySet->getPropertyValue("CustomShapeGeometry"));
+ aCustomShapeGeometry["PresetTextWarp"] <<= preset;
+ xPropertySet->setPropertyValue(
+ "CustomShapeGeometry",
+ uno::Any(aCustomShapeGeometry.getAsConstPropertyValueList()));
+ }
+ }
+ return new oox::drawingml::PresetTextShapeContext(
+ *this, rAttribs, *(getShape()->getCustomShapeProperties()));
+ case XML_txbx:
+ {
+ mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
+ mpShapePtr->setTextBox(true);
+ //in case if the textbox is linked, save the attributes
+ //for further processing.
+ if (rAttribs.hasAttribute(XML_id))
+ {
+ std::optional<OUString> id = rAttribs.getString(XML_id);
+ if (id.has_value())
+ {
+ oox::drawingml::LinkedTxbxAttr linkedTxtBoxAttr;
+ linkedTxtBoxAttr.id = id.value().toInt32();
+ mpShapePtr->setTxbxHasLinkedTxtBox(true);
+ mpShapePtr->setLinkedTxbxAttributes(linkedTxtBoxAttr);
+ }
+ }
+ return this;
+ }
+ break;
+ case XML_linkedTxbx:
+ {
+ //in case if the textbox is linked, save the attributes
+ //for further processing.
+ mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
+ mpShapePtr->setTextBox(true);
+ std::optional<OUString> id = rAttribs.getString(XML_id);
+ std::optional<OUString> seq = rAttribs.getString(XML_seq);
+ if (id.has_value() && seq.has_value())
+ {
+ oox::drawingml::LinkedTxbxAttr linkedTxtBoxAttr;
+ linkedTxtBoxAttr.id = id.value().toInt32();
+ linkedTxtBoxAttr.seq = seq.value().toInt32();
+ mpShapePtr->setTxbxHasLinkedTxtBox(true);
+ mpShapePtr->setLinkedTxbxAttributes(linkedTxtBoxAttr);
+ }
+ }
+ break;
+ default:
+ return ShapeContext::onCreateContext(nElementToken, rAttribs);
+ }
+ return nullptr;
+}
+
+void WpsContext::onEndElement()
+{
+ // Convert shape to Fontwork shape if necessary and meaningful.
+ // Only at end of bodyPr all needed info is available.
+
+ if (getBaseToken(getCurrentElement()) != XML_bodyPr)
+ return;
+
+ // Make sure all needed parts are available
+ auto* pCustomShape
+ = dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(mxShape));
+ if (!pCustomShape || !mpShapePtr || !mxShape.is())
+ return;
+ uno::Reference<beans::XPropertySet> xShapePropertySet(mxShape, uno::UNO_QUERY);
+ if (!xShapePropertySet.is())
+ return;
+ // This is the text in the frame, associated with the shape
+ uno::Reference<text::XText> xText(mxShape, uno::UNO_QUERY);
+ if (!xText.is())
+ return;
+
+ OUString sMSPresetType;
+ comphelper::SequenceAsHashMap aCustomShapeGeometry(
+ xShapePropertySet->getPropertyValue("CustomShapeGeometry"));
+ aCustomShapeGeometry["PresetTextWarp"] >>= sMSPresetType;
+ if (sMSPresetType.isEmpty() || sMSPresetType == u"textNoShape")
+ return;
+
+ // Word can combine its "abc Transform" with a lot of shape types. LibreOffice can only render
+ // the old kind WordArt, which is based on a rectangle. In case of non rectangular shape we keep
+ // the shape and do not convert the text to Fontwork.
+ OUString sType;
+ aCustomShapeGeometry["Type"] >>= sType;
+ if (sType != u"ooxml-rect")
+ return;
+
+ // Copy properties from frame text to have them available after the frame is removed.
+ std::vector<beans::PropertyValue> aTextPropVec;
+ if (!lcl_getTextPropsFromFrameText(xText, aTextPropVec))
+ return;
+ comphelper::SequenceAsHashMap aTextPropMap(comphelper::containerToSequence(aTextPropVec));
+
+ // Copy text content from frame to shape. Since Fontwork uses simple text anyway, we can use
+ // a string.
+ OUString sFrameContent(xText->getString());
+ pCustomShape->NbcSetText(sFrameContent);
+
+ // Setting the property "TextBox" to false includes removing the attached frame from the shape.
+ xShapePropertySet->setPropertyValue("TextBox", uno::Any(false));
+
+ // Set the shape into text path mode, so that the text is drawn as Fontwork. Word renders a legacy
+ // "text on path" without the legacy stretching, therefore use false for bFromWordArt.
+ mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
+ FontworkHelpers::putCustomShapeIntoTextPathMode(mxShape, getShape()->getCustomShapeProperties(),
+ sMSPresetType, /*bFromWordArt*/ false);
+
+ // Apply the text props to the fontwork shape
+ lcl_setTextPropsToShape(xShapePropertySet, aTextPropVec); // includes e.g. FontName
+ lcl_setTextAnchorFromTextProps(xShapePropertySet, aTextPropMap);
+
+ // Fontwork in LO uses fill and stroke of the shape and cannot style text portions individually.
+ // "abc Transform" in Word uses fill and outline of the characters.
+ // We need to copy the properties from a run to the shape.
+ oox::drawingml::ShapePropertyMap aStrokeShapeProps(getFilter().getModelObjectHelper());
+ oox::drawingml::LineProperties aCreatedLineProperties
+ = lcl_generateLinePropertiesFromTextProps(aTextPropMap);
+ aCreatedLineProperties.pushToPropMap(aStrokeShapeProps, getFilter().getGraphicHelper());
+ lcl_applyShapePropsToShape(xShapePropertySet, aStrokeShapeProps);
+
+ oox::drawingml::ShapePropertyMap aFillShapeProps(getFilter().getModelObjectHelper());
+ oox::drawingml::FillProperties aCreatedFillProperties
+ = lcl_generateFillPropertiesFromTextProps(aTextPropMap);
+ aCreatedFillProperties.pushToPropMap(aFillShapeProps, getFilter().getGraphicHelper(),
+ /*nShapeRotation*/ 0,
+ /*nPhClr*/ API_RGB_TRANSPARENT,
+ /*aShapeSize*/ css::awt::Size(0, 0), /*nPhClrTheme*/ -1,
+ pCustomShape->IsMirroredX(), pCustomShape->IsMirroredY(),
+ /*bIsCustomShape*/ true);
+ lcl_applyShapePropsToShape(xShapePropertySet, aFillShapeProps);
+
+ // Copying the text content from frame to shape as string has lost the styles. Apply the used text
+ // properties back to all runs in the text.
+ uno::Reference<text::XText> xNewText(pCustomShape->getUnoShape(), uno::UNO_QUERY);
+ if (xNewText.is())
+ lcl_applyUsedTextPropsToAllTextRuns(xNewText, aTextPropVec);
+
+ // Fontwork stretches the text to the given path. So adapt shape size to text is nonsensical.
+ xShapePropertySet->setPropertyValue("TextAutoGrowHeight", uno::Any(false));
+ xShapePropertySet->setPropertyValue("TextAutoGrowWidth", uno::Any(false));
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/shape/WpsContext.hxx b/oox/source/shape/WpsContext.hxx
new file mode 100644
index 0000000000..16108b3733
--- /dev/null
+++ b/oox/source/shape/WpsContext.hxx
@@ -0,0 +1,46 @@
+/* -*- 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/.
+ */
+
+#ifndef INCLUDED_OOX_SOURCE_SHAPE_WPSCONTEXT_HXX
+#define INCLUDED_OOX_SOURCE_SHAPE_WPSCONTEXT_HXX
+
+#include <oox/core/contexthandler2.hxx>
+#include <oox/drawingml/shapecontext.hxx>
+#include <oox/drawingml/drawingmltypes.hxx>
+
+namespace com::sun::star::drawing
+{
+class XShape;
+}
+
+namespace oox::shape
+{
+/// Wps is the drawingML equivalent of v:shape.
+class WpsContext final : public oox::drawingml::ShapeContext
+{
+public:
+ WpsContext(oox::core::ContextHandler2Helper const& rParent,
+ css::uno::Reference<css::drawing::XShape> xShape,
+ oox::drawingml::ShapePtr const& pMasterShapePtr,
+ oox::drawingml::ShapePtr const& pShapePtr);
+ ~WpsContext() override;
+
+ oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElementToken,
+ const oox::AttributeList& rAttribs) override;
+ virtual void onEndElement() override;
+
+private:
+ css::uno::Reference<css::drawing::XShape> mxShape;
+ bool m_bHasWPGParent;
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */