include/svx/diagram/datamodel.hxx | 18 ++ oox/source/drawingml/diagram/datamodel.cxx | 13 + oox/source/drawingml/diagram/diagram.cxx | 39 ++++- oox/source/drawingml/diagram/diagram.hxx | 4 oox/source/drawingml/diagram/diagramhelper.cxx | 49 +++++-- svx/source/diagram/datamodel.cxx | 171 ++++++++++++++++++++++++- 6 files changed, 270 insertions(+), 24 deletions(-)
New commits: commit edadfb564f1334887df473f9bc05dab6b1e7ef9d Author: Armin Le Grand (Allotropia) <armin.le.gr...@me.com> AuthorDate: Wed Apr 13 17:17:35 2022 +0200 Commit: Armin Le Grand <armin.le.gr...@me.com> CommitDate: Thu Apr 14 11:02:19 2022 +0200 Advanced Diagram support: Secure properties at Diagram ModelData Additionally to the Text/Attributes already saved/secured after Diagram import, secure more data that is part of the Diagram ModelData. This is about attributes (e.g. FillStyle/LineStyle, TextAttributes, ...) in UNO API formt that will be secured/ attached to the Diagram ModelData in it's svx::diagram::Point structure. This is done for all those entries for which a XShape will/ would be incarnated, thus associated to entries that will get a visualization, including the BackgroundObject. From that data, at re-creation time, the attributes can be re-applied to the re-created XShape(s), also after changes to the Diagram Model Data (e.g. Add/Remove). This is - besides the already added securing of the Style/ Theme - a 2nd method for lossless re-creation. For the BackgroundObject - if it has FillStyles - it is even the only method to secure that data and thus necessary for that case. The selection of atributes that gets secured is minimal for now and may/need to be extended for existing cases accordingly. Change-Id: Ie9b72b9b9135113cf858d57fe6cd8622d736c4a4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132976 Tested-by: Jenkins Reviewed-by: Armin Le Grand <armin.le.gr...@me.com> diff --git a/include/svx/diagram/datamodel.hxx b/include/svx/diagram/datamodel.hxx index c42a3d26a115..686835ee378f 100644 --- a/include/svx/diagram/datamodel.hxx +++ b/include/svx/diagram/datamodel.hxx @@ -29,8 +29,8 @@ #include <rtl/ustrbuf.hxx> #include <com/sun/star/uno/Sequence.hxx> -#include <com/sun/star/beans/PropertyValue.hpp> #include <com/sun/star/xml/dom/XDocument.hpp> +#include <com/sun/star/drawing/XShape.hpp> namespace svx::diagram { @@ -83,11 +83,21 @@ struct SVXCORE_DLLPUBLIC TextBody OUString msText; // attributes from TextBody::getTextProperties() - css::uno::Sequence< css::beans::PropertyValue > maTextProps; + std::vector< std::pair< OUString, css::uno::Any >> maTextProps; }; typedef std::shared_ptr< TextBody > TextBodyPtr; +/** Styles for a Point (FillStyle/LineStyle/...) + */ +struct SVXCORE_DLLPUBLIC PointStyle +{ + // attributes (LineStyle/FillStyle/...) + std::vector< std::pair< OUString, css::uno::Any >> maProperties; +}; + +typedef std::shared_ptr< PointStyle > PointStylePtr; + /** A point */ struct SVXCORE_DLLPUBLIC Point @@ -95,6 +105,7 @@ struct SVXCORE_DLLPUBLIC Point Point(); TextBodyPtr msTextBody; + PointStylePtr msPointStylePtr; OUString msCnxId; OUString msModelId; @@ -136,6 +147,9 @@ struct SVXCORE_DLLPUBLIC Point bool mbCustomVerticalFlip; bool mbCustomText; bool mbIsPlaceholder; + + void securePropertiesFromXShape(const css::uno::Reference< css::drawing::XShape >& rXShape); + void restorePropertiesToXShape(const css::uno::Reference< css::drawing::XShape >& rXShape) const; }; typedef std::vector< Point > Points; diff --git a/oox/source/drawingml/diagram/datamodel.cxx b/oox/source/drawingml/diagram/datamodel.cxx index cc8924228e23..f0a3cef66903 100644 --- a/oox/source/drawingml/diagram/datamodel.cxx +++ b/oox/source/drawingml/diagram/datamodel.cxx @@ -68,15 +68,15 @@ void DiagramData::restoreDataFromModelToShapeAfterReCreation(const svx::diagram: pTextRun->getText() = rPoint.msTextBody->msText; aNewTextBody->addParagraph().addRun(pTextRun); - if(rPoint.msTextBody->maTextProps.hasElements()) + if(!rPoint.msTextBody->maTextProps.empty()) { oox::PropertyMap& rTargetMap(aNewTextBody->getTextProperties().maPropertyMap); for (auto const& prop : rPoint.msTextBody->maTextProps) { - sal_Int32 nPropId(oox::PropertyMap::getPropertyId(prop.Name)); + const sal_Int32 nPropId(oox::PropertyMap::getPropertyId(prop.first)); if(nPropId > 0) - rTargetMap.setAnyProperty(nPropId, prop.Value); + rTargetMap.setAnyProperty(nPropId, prop.second); } } } @@ -99,7 +99,12 @@ void DiagramData::secureDataFromShapeToModelAfterDiagramImport() { point.msTextBody = std::make_shared<svx::diagram::TextBody>(); point.msTextBody->msText = pShapeCandidate->getTextBody()->toString(); - point.msTextBody->maTextProps = pShapeCandidate->getTextBody()->getTextProperties().maPropertyMap.makePropertyValueSequence(); + + const uno::Sequence< beans::PropertyValue > aTextProps( + pShapeCandidate->getTextBody()->getTextProperties().maPropertyMap.makePropertyValueSequence()); + + for (auto const& prop : aTextProps) + point.msTextBody->maTextProps.push_back(std::pair(prop.Name, prop.Value)); } // At this place a mechanism to find missing data should be added: diff --git a/oox/source/drawingml/diagram/diagram.cxx b/oox/source/drawingml/diagram/diagram.cxx index 484e7af9fd13..56b9b791e022 100644 --- a/oox/source/drawingml/diagram/diagram.cxx +++ b/oox/source/drawingml/diagram/diagram.cxx @@ -103,6 +103,40 @@ static void removeUnneededGroupShapes(const ShapePtr& pShape) } } +void DiagramLayout::secureDataFromXShapeToModelAfterDiagramImport() +{ + // maPresPointShapeMap types: < const svx::diagram::Point*, ShapePtr > + for (const auto& pEntry : maPresPointShapeMap) + { + if(nullptr != pEntry.first && pEntry.second) + { + const css::uno::Reference< css::drawing::XShape >& rXShape(pEntry.second->getXShape()); + + if(rXShape) + { + const_cast<svx::diagram::Point*>(pEntry.first)->securePropertiesFromXShape(rXShape); + } + } + } +} + +void DiagramLayout::restoreDataFromModelToXShapeAfterDiagramReCreate() +{ + // maPresPointShapeMap types: < const svx::diagram::Point*, ShapePtr > + for (const auto& pEntry : maPresPointShapeMap) + { + if(nullptr != pEntry.first && pEntry.second) + { + const css::uno::Reference< css::drawing::XShape >& rXShape(pEntry.second->getXShape()); + + if(rXShape) + { + pEntry.first->restorePropertiesToXShape(rXShape); + } + } + } +} + void Diagram::addTo( const ShapePtr & pParentShape ) { if (pParentShape->getSize().Width == 0 || pParentShape->getSize().Height == 0) @@ -403,11 +437,6 @@ void loadDiagram( ShapePtr const & pShape, } } - // After Diagram import, parts of the Diagram ModelData is at the - // oox::drawingml::Shape. Since these objects are temporary helpers, - // secure that data at the Diagram ModelData by copying. - pData->secureDataFromShapeToModelAfterDiagramImport(); - // collect data, init maps // for Diagram import, do - for now - NOT clear all oox::drawingml::Shape pData->buildDiagramDataModel(false); diff --git a/oox/source/drawingml/diagram/diagram.hxx b/oox/source/drawingml/diagram/diagram.hxx index f58c762f6a1a..43516d4a270d 100644 --- a/oox/source/drawingml/diagram/diagram.hxx +++ b/oox/source/drawingml/diagram/diagram.hxx @@ -79,6 +79,10 @@ public: PresPointShapeMap & getPresPointShapeMap() { return maPresPointShapeMap; } + // get/set data between Diagram DataModel and oox::drawingml::Shape + void secureDataFromXShapeToModelAfterDiagramImport(); + void restoreDataFromModelToXShapeAfterDiagramReCreate(); + private: Diagram& mrDgm; OUString msDefStyle; diff --git a/oox/source/drawingml/diagram/diagramhelper.cxx b/oox/source/drawingml/diagram/diagramhelper.cxx index cc6efd9ba50e..6dc991149b01 100644 --- a/oox/source/drawingml/diagram/diagramhelper.cxx +++ b/oox/source/drawingml/diagram/diagramhelper.cxx @@ -103,7 +103,9 @@ void AdvancedDiagramHelper::reLayout(SdrObjGroup& rTarget) // set oox::Theme at Filter. All LineStyle/FillStyle/Colors/Attributes // will be taken from there - xFilter->setCurrentTheme(getOrCreateThemePtr(xFilter)); + static bool bUseDiagramThemeData(false); + if(bUseDiagramThemeData) + xFilter->setCurrentTheme(getOrCreateThemePtr(xFilter)); css::uno::Reference< css::lang::XComponent > aComponentModel( rUnoModel, uno::UNO_QUERY ); xFilter->setTargetDocument(aComponentModel); @@ -126,11 +128,16 @@ void AdvancedDiagramHelper::reLayout(SdrObjGroup& rTarget) pShapePtr->getFillProperties()); } - // Re-apply remembered geometry - rTarget.TRSetBaseGeometry(aTransformation, aPolyPolygon); - // sync FontHeights mpDiagramPtr->syncDiagramFontHeights(); + + // re-apply secured data from ModelData + static bool bUseDiagramModelData(true); + if(bUseDiagramModelData) + mpDiagramPtr->getLayout()->restoreDataFromModelToXShapeAfterDiagramReCreate(); + + // Re-apply remembered geometry + rTarget.TRSetBaseGeometry(aTransformation, aPolyPolygon); } OUString AdvancedDiagramHelper::getString() const @@ -155,22 +162,42 @@ std::vector<std::pair<OUString, OUString>> AdvancedDiagramHelper::getChildren(co OUString AdvancedDiagramHelper::addNode(const OUString& rText) { + OUString aRetval; + if(hasDiagramData()) { - return mpDiagramPtr->getData()->addNode(rText); + aRetval = mpDiagramPtr->getData()->addNode(rText); + + // reset temporary buffered ModelData association lists & rebuild them + // and the Diagram DataModel + mpDiagramPtr->getData()->buildDiagramDataModel(true); + + // also reset temporary buffered layout data - that might + // still refer to changed oox::Shape data + mpDiagramPtr->getLayout()->getPresPointShapeMap().clear(); } - return OUString(); + return aRetval; } bool AdvancedDiagramHelper::removeNode(const OUString& rNodeId) { + bool bRetval(false); + if(hasDiagramData()) { - return mpDiagramPtr->getData()->removeNode(rNodeId); + bRetval = mpDiagramPtr->getData()->removeNode(rNodeId); + + // reset temporary buffered ModelData association lists & rebuild them + // and the Diagram DataModel + mpDiagramPtr->getData()->buildDiagramDataModel(true); + + // also reset temporary buffered layout data - that might + // still refer to changed oox::Shape data + mpDiagramPtr->getLayout()->getPresPointShapeMap().clear(); } - return false; + return bRetval; } void AdvancedDiagramHelper::doAnchor(SdrObjGroup& rTarget) @@ -182,6 +209,12 @@ void AdvancedDiagramHelper::doAnchor(SdrObjGroup& rTarget) mpDiagramPtr->syncDiagramFontHeights(); + // After Diagram import, parts of the Diagram ModelData is at the + // oox::drawingml::Shape. Since these objects are temporary helpers, + // secure that data at the Diagram ModelData by copying. + mpDiagramPtr->getData()->secureDataFromShapeToModelAfterDiagramImport(); + mpDiagramPtr->getLayout()->secureDataFromXShapeToModelAfterDiagramImport(); + anchorToSdrObjGroup(rTarget); } diff --git a/svx/source/diagram/datamodel.cxx b/svx/source/diagram/datamodel.cxx index a586f0556d31..44e9977326a0 100644 --- a/svx/source/diagram/datamodel.cxx +++ b/svx/source/diagram/datamodel.cxx @@ -23,6 +23,15 @@ #include <svx/diagram/datamodel.hxx> #include <comphelper/xmltools.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> +#include <com/sun/star/text/XText.hpp> + +#include <editeng/unoprnms.hxx> #include <sal/log.hxx> namespace svx::diagram { @@ -63,6 +72,163 @@ Point::Point() { } +static void addProperty(const OUString& rName, + const css::uno::Reference< css::beans::XPropertySetInfo >& xInfo, + std::vector< std::pair< OUString, css::uno::Any >>& rTarget, + const css::uno::Reference< css::beans::XPropertySet >& xPropSet ) +{ + if(xInfo->hasPropertyByName(rName)) + rTarget.push_back(std::pair(OUString(rName), xPropSet->getPropertyValue(rName))); +} + +void Point::securePropertiesFromXShape(const css::uno::Reference< css::drawing::XShape >& rXShape) +{ + if(!rXShape) + return; + +#ifdef DBG_UTIL + // to easier decide which additional properties may/should be preserved, + // create a full list of set properties to browse/decide (in debugger) + const css::uno::Reference< css::beans::XPropertyState > xAllPropStates(rXShape, css::uno::UNO_QUERY); + const css::uno::Reference< css::beans::XPropertySet > xAllPropSet( rXShape, css::uno::UNO_QUERY ); + const css::uno::Sequence< css::beans::Property > allSequence(xAllPropSet->getPropertySetInfo()->getProperties()); + std::vector< std::pair< OUString, css::uno::Any >> allSetProps; + for (auto& rProp : allSequence) + { + try + { + if (xAllPropStates->getPropertyState(rProp.Name) == css::beans::PropertyState::PropertyState_DIRECT_VALUE) + { + css::uno::Any aValue(xAllPropSet->getPropertyValue(rProp.Name)); + if(aValue.hasValue()) + allSetProps.push_back(std::pair(rProp.Name, aValue)); + } + } + catch (...) + { + } + } +#endif + + const css::uno::Reference< css::beans::XPropertySet > xPropSet( rXShape, css::uno::UNO_QUERY ); + if(!xPropSet) + return; + + const css::uno::Reference< css::lang::XServiceInfo > xServiceInfo( rXShape, css::uno::UNO_QUERY ); + if(!xServiceInfo) + return; + + const css::uno::Reference< css::beans::XPropertySetInfo > xInfo(xPropSet->getPropertySetInfo()); + if (!xInfo.is()) + return; + + if(!msPointStylePtr) + msPointStylePtr = std::make_shared< svx::diagram::PointStyle >(); + + // shortcut to target + std::vector< std::pair< OUString, css::uno::Any >>& rTarget(msPointStylePtr->maProperties); + + // Note: The Text may also be secured here, so it may also be possible to + // secure/store it at PointStyle instead of at TextBody, same maybe evaluated + // for the text attributes - where when securing here the attributes would be + // in our UNO API format already. + // if(xServiceInfo->supportsService("com.sun.star.drawing.Text")) + // { + // css::uno::Reference< css::text::XText > xText(rXShape, css::uno::UNO_QUERY); + // const OUString aText(xText->getString()); + // + // if(!aText.isEmpty()) + // { + // } + // } + + // Add all kinds of properties that are needed to re-create the XShape. + // For now this is a minimal example-selection, it will need to be extended + // over time for all kind of cases/properties + + // text properties + if(xServiceInfo->supportsService("com.sun.star.drawing.TextProperties")) + { + addProperty(UNO_NAME_CHAR_COLOR, xInfo, rTarget, xPropSet); + addProperty(UNO_NAME_CHAR_HEIGHT, xInfo, rTarget, xPropSet); + addProperty(UNO_NAME_CHAR_SHADOWED, xInfo, rTarget, xPropSet); + addProperty(UNO_NAME_CHAR_WEIGHT, xInfo, rTarget, xPropSet); + } + + // fill properties + if(xServiceInfo->supportsService("com.sun.star.drawing.FillProperties")) + { + css::drawing::FillStyle eFillStyle(css::drawing::FillStyle_NONE); + if (xInfo->hasPropertyByName(UNO_NAME_FILLSTYLE)) + xPropSet->getPropertyValue(UNO_NAME_FILLSTYLE) >>= eFillStyle; + + if(css::drawing::FillStyle_NONE != eFillStyle) + { + addProperty(UNO_NAME_FILLSTYLE, xInfo, rTarget, xPropSet); + + switch(eFillStyle) + { + case css::drawing::FillStyle_SOLID: + { + addProperty(UNO_NAME_FILLCOLOR, xInfo, rTarget, xPropSet); + break; + } + default: + case css::drawing::FillStyle_NONE: + case css::drawing::FillStyle_GRADIENT: + case css::drawing::FillStyle_HATCH: + case css::drawing::FillStyle_BITMAP: + break; + } + } + } + + // line properties + if(xServiceInfo->supportsService("com.sun.star.drawing.LineProperties")) + { + css::drawing::LineStyle eLineStyle(css::drawing::LineStyle_NONE); + if (xInfo->hasPropertyByName(UNO_NAME_LINESTYLE)) + xPropSet->getPropertyValue(UNO_NAME_LINESTYLE) >>= eLineStyle; + + if(css::drawing::LineStyle_NONE != eLineStyle) + { + addProperty(UNO_NAME_LINESTYLE, xInfo, rTarget, xPropSet); + addProperty(UNO_NAME_LINECOLOR, xInfo, rTarget, xPropSet); + addProperty(UNO_NAME_LINEWIDTH, xInfo, rTarget, xPropSet); + + switch(eLineStyle) + { + case css::drawing::LineStyle_SOLID: + break; + default: + case css::drawing::LineStyle_NONE: + case css::drawing::LineStyle_DASH: + break; + } + } + } +} + +void Point::restorePropertiesToXShape(const css::uno::Reference< css::drawing::XShape >& rXShape) const +{ + if(!msPointStylePtr) + return; + + if(!rXShape) + return; + + css::uno::Reference<css::beans::XPropertySet> xPropSet(rXShape, css::uno::UNO_QUERY); + if(!xPropSet) + return; + + std::vector< std::pair< OUString, css::uno::Any >>& rSource(msPointStylePtr->maProperties); + + for (auto const& prop : rSource) + { + xPropSet->setPropertyValue(prop.first, prop.second); + } +} + DiagramData::DiagramData() { } @@ -136,9 +302,6 @@ bool DiagramData::removeNode(const OUString& rNodeId) maPoints.end()); // TODO: fix source/dest order - - buildDiagramDataModel(true); - return true; } @@ -254,8 +417,6 @@ OUString DiagramData::addNode(const OUString& rText) maPoints.push_back(aDataPoint); maPoints.push_back(aPresPoint); - buildDiagramDataModel(true); - return sNewNodeId; }