/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include "LockedCanvasContext.hxx" #include "WordprocessingCanvasContext.hxx" #include "WpsContext.hxx" #include "WpgContext.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; namespace oox::shape { using namespace core; using namespace drawingml; ShapeContextHandler::ShapeContextHandler(rtl::Reference xFilterBase) : m_bFullWPGSUpport(false), mxShapeFilterBase(std::move(xFilterBase)) { } ShapeContextHandler::~ShapeContextHandler() { } uno::Reference 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(mxLockedCanvasContext.get()); } /* * This method creates new ChartGraphicDataContext Object. */ uno::Reference ShapeContextHandler::getChartShapeContext(sal_Int32 nElement) { if (!mxChartShapeContext.is()) { switch (nElement & 0xffff) { case XML_chart: { std::unique_ptr pFragmentHandler( new ShapeFragmentHandler(*mxShapeFilterBase, msRelationFragmentPath)); mpShape = std::make_shared("com.sun.star.drawing.OLE2Shape" ); mxChartShapeContext.set(new ChartGraphicDataContext(*pFragmentHandler, mpShape, true)); break; } default: break; } } return mxChartShapeContext; } uno::Reference ShapeContextHandler::getWpsContext(sal_Int32 nStartElement, sal_Int32 nElement) { if (!mxWpsContext.is()) { FragmentHandler2Ref rFragmentHandler(new ShapeFragmentHandler(*mxShapeFilterBase, msRelationFragmentPath)); uno::Reference xShape; // No element happens in case of pretty-printed XML, bodyPr is the case when we are called again after . 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, nullptr, std::make_shared( "com.sun.star.drawing.CustomShape"))); break; default: break; } } return mxWpsContext; } uno::Reference 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(mxWpgContext.get()); } uno::Reference const & ShapeContextHandler::getGraphicShapeContext(::sal_Int32 Element ) { if (! mxGraphicShapeContext.is()) { auto pFragmentHandler = std::make_shared(*mxShapeFilterBase, msRelationFragmentPath); switch (Element & 0xffff) { case XML_graphic: mpShape = std::make_shared("com.sun.star.drawing.GraphicObjectShape" ); mxGraphicShapeContext.set (new GraphicalObjectFrameContext(*pFragmentHandler, nullptr, mpShape, true)); break; case XML_pic: mpShape = std::make_shared("com.sun.star.drawing.GraphicObjectShape" ); mxGraphicShapeContext.set (new GraphicShapeContext(*pFragmentHandler, nullptr, mpShape)); break; default: break; } } return mxGraphicShapeContext; } uno::Reference ShapeContextHandler::getDrawingShapeContext() { if (!mxDrawingFragmentHandler.is()) { mpDrawing = std::make_shared( *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(mxDrawingFragmentHandler.get()); } uno::Reference ShapeContextHandler::getDiagramShapeContext() { if (!mxDiagramShapeContext.is()) { auto pFragmentHandler = std::make_shared(*mxShapeFilterBase, msRelationFragmentPath); mpShape = std::make_shared(); mpShape->setSize(maSize); mxDiagramShapeContext.set(new DiagramGraphicDataContext(*pFragmentHandler, mpShape)); } return mxDiagramShapeContext; } uno::Reference 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(mxWordprocessingCanvasContext.get()); } uno::Reference ShapeContextHandler::getContextHandler(sal_Int32 nElement) { uno::Reference 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, u"/"_ustr)); 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(); auto pTheme = std::make_shared(); mpThemePtr->setTheme(pTheme); uno::Reference 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 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 xContextHandler(getContextHandler()); if (xContextHandler.is()) xContextHandler->startUnknownElement(Namespace, Name, Attribs); } void SAL_CALL ShapeContextHandler::endFastElement(::sal_Int32 Element) { uno::Reference 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 xServiceInfo(mxSavedShape, uno::UNO_QUERY); bool bTextFrame = xServiceInfo.is() && xServiceInfo->supportsService(u"com.sun.star.text.TextFrame"_ustr); bool bTextBox = false; if (!bTextFrame) { uno::Reference xPropertySet(mxSavedShape, uno::UNO_QUERY); if (xPropertySet.is()) xPropertySet->getPropertyValue(u"TextBox"_ustr) >>= bTextBox; } if (bTextFrame || bTextBox) mxWpsContext.clear(); mxSavedShape.clear(); } void SAL_CALL ShapeContextHandler::endUnknownElement (const OUString & Namespace, const OUString & Name) { uno::Reference 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 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 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( "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(u"com.sun.star.drawing.temporaryForXMLImportOLE2Shape"_ustr); 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 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& xDocProps) { mxDocumentProperties = xDocProps; mxShapeFilterBase->checkDocumentProperties(mxDocumentProperties); } void ShapeContextHandler::setMediaDescriptor(const uno::Sequence& rMediaDescriptor) { maMediaDescriptor = rMediaDescriptor; } void ShapeContextHandler::setGraphicMapper(css::uno::Reference 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& vChildren = pShape->getChildren(); for (auto const& child : vChildren) { applyFontRefColor(child, rFontRefColor); } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */