diff options
Diffstat (limited to 'oox/source/shape')
-rw-r--r-- | oox/source/shape/LockedCanvasContext.cxx | 97 | ||||
-rw-r--r-- | oox/source/shape/LockedCanvasContext.hxx | 37 | ||||
-rw-r--r-- | oox/source/shape/ShapeContextHandler.cxx | 689 | ||||
-rw-r--r-- | oox/source/shape/ShapeDrawingFragmentHandler.cxx | 49 | ||||
-rw-r--r-- | oox/source/shape/ShapeFilterBase.cxx | 137 | ||||
-rw-r--r-- | oox/source/shape/WordprocessingCanvasContext.cxx | 110 | ||||
-rw-r--r-- | oox/source/shape/WordprocessingCanvasContext.hxx | 39 | ||||
-rw-r--r-- | oox/source/shape/WpgContext.cxx | 95 | ||||
-rw-r--r-- | oox/source/shape/WpgContext.hxx | 43 | ||||
-rw-r--r-- | oox/source/shape/WpsContext.cxx | 982 | ||||
-rw-r--r-- | oox/source/shape/WpsContext.hxx | 46 |
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: */ |