1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "datamodel.hxx"
22 #include <rtl/ustrbuf.hxx>
23 #include <sal/log.hxx>
24 #include <drawingml/fillproperties.hxx>
25 #include <drawingml/textbody.hxx>
26 #include <drawingml/textparagraph.hxx>
27 #include <drawingml/textrun.hxx>
28 #include <oox/drawingml/shape.hxx>
29 #include <com/sun/star/beans/XPropertyState.hpp>
30 #include <com/sun/star/drawing/FillStyle.hpp>
31 #include <com/sun/star/drawing/LineStyle.hpp>
32 #include <editeng/unoprnms.hxx>
34 #include <unordered_set>
36 using namespace ::com::sun::star
;
38 namespace oox::drawingml
{
40 Shape
* DiagramData::getOrCreateAssociatedShape(const svx::diagram::Point
& rPoint
, bool bCreateOnDemand
) const
42 if(maPointShapeMap
.end() == maPointShapeMap
.find(rPoint
.msModelId
))
44 const_cast<DiagramData
*>(this)->maPointShapeMap
[rPoint
.msModelId
] = ShapePtr();
47 const ShapePtr
& rShapePtr
= maPointShapeMap
.find(rPoint
.msModelId
)->second
;
49 if(!rShapePtr
&& bCreateOnDemand
)
51 const_cast<ShapePtr
&>(rShapePtr
) = std::make_shared
<Shape
>();
53 // If we did create a new oox::drawingml::Shape, directly apply
54 // available data from the Diagram ModelData to it as preparation
55 restoreDataFromModelToShapeAfterReCreation(rPoint
, *rShapePtr
);
58 return rShapePtr
.get();
61 void DiagramData::restoreDataFromModelToShapeAfterReCreation(const svx::diagram::Point
& rPoint
, Shape
& rNewShape
)
63 // If we did create a new oox::drawingml::Shape, directly apply
64 // available data from the Diagram ModelData to it as preparation
66 // This is e.g. the Text, but may get more (styles?)
67 if(!rPoint
.msTextBody
->msText
.isEmpty())
69 TextBodyPtr
aNewTextBody(std::make_shared
<TextBody
>());
70 rNewShape
.setTextBody(aNewTextBody
);
71 TextRunPtr pTextRun
= std::make_shared
<TextRun
>();
72 pTextRun
->getText() = rPoint
.msTextBody
->msText
;
73 aNewTextBody
->addParagraph().addRun(pTextRun
);
75 if(!rPoint
.msTextBody
->maTextProps
.empty())
77 oox::PropertyMap
& rTargetMap(aNewTextBody
->getTextProperties().maPropertyMap
);
79 for (auto const& prop
: rPoint
.msTextBody
->maTextProps
)
81 const sal_Int32
nPropId(oox::PropertyMap::getPropertyId(prop
.first
));
83 rTargetMap
.setAnyProperty(nPropId
, prop
.second
);
89 static void addProperty(const OUString
& rName
,
90 const css::uno::Reference
< css::beans::XPropertySetInfo
>& xInfo
,
91 std::vector
< std::pair
< OUString
, css::uno::Any
>>& rTarget
,
92 const css::uno::Reference
< css::beans::XPropertySet
>& xPropSet
)
94 if(xInfo
->hasPropertyByName(rName
))
95 rTarget
.push_back(std::pair(OUString(rName
), xPropSet
->getPropertyValue(rName
)));
98 void DiagramData::secureStyleDataFromShapeToModel(::oox::drawingml::Shape
& rShape
)
100 const std::vector
< ShapePtr
>& rChildren(rShape
.getChildren());
102 if(!rChildren
.empty())
105 for (auto& child
: rChildren
)
107 secureStyleDataFromShapeToModel(*child
);
110 // if group shape we are done. Do not secure properties for group shapes
115 const css::uno::Reference
< css::drawing::XShape
> &rXShape(rShape
.getXShape());
119 // we need a ModelID for association
120 if(rShape
.getDiagramDataModelID().isEmpty())
123 // define target to save to
124 svx::diagram::PointStyle
* pTarget(nullptr);
125 const bool bIsBackgroundShape(rShape
.getDiagramDataModelID() == msBackgroundShapeModelID
);
127 if(bIsBackgroundShape
)
129 // if BackgroundShape, create properties & set as target
130 if(!maBackgroundShapeStyle
)
131 maBackgroundShapeStyle
= std::make_shared
< svx::diagram::PointStyle
>();
132 pTarget
= maBackgroundShapeStyle
.get();
136 // if Shape, seek association
137 for (auto & point
: maPoints
)
139 if(point
.msModelId
== rShape
.getDiagramDataModelID())
141 // found - create properties & set as target
142 pTarget
= point
.msPointStylePtr
.get();
144 // we are done, there is no 2nd shape with the same ModelID by definition
150 // no target -> nothing to do
151 if(nullptr == pTarget
)
155 // to easier decide which additional properties may/should be preserved,
156 // create a full list of set properties to browse/decide (in debugger)
157 const css::uno::Reference
< css::beans::XPropertyState
> xAllPropStates(rXShape
, css::uno::UNO_QUERY
);
158 const css::uno::Reference
< css::beans::XPropertySet
> xAllPropSet( rXShape
, css::uno::UNO_QUERY
);
159 const css::uno::Sequence
< css::beans::Property
> allSequence(xAllPropSet
->getPropertySetInfo()->getProperties());
160 std::vector
< std::pair
< OUString
, css::uno::Any
>> allSetProps
;
161 for (auto& rProp
: allSequence
)
165 if (xAllPropStates
->getPropertyState(rProp
.Name
) == css::beans::PropertyState::PropertyState_DIRECT_VALUE
)
167 css::uno::Any
aValue(xAllPropSet
->getPropertyValue(rProp
.Name
));
168 if(aValue
.hasValue())
169 allSetProps
.push_back(std::pair(rProp
.Name
, aValue
));
178 const css::uno::Reference
< css::beans::XPropertySet
> xPropSet( rXShape
, css::uno::UNO_QUERY
);
182 const css::uno::Reference
< css::lang::XServiceInfo
> xServiceInfo( rXShape
, css::uno::UNO_QUERY
);
186 const css::uno::Reference
< css::beans::XPropertySetInfo
> xInfo(xPropSet
->getPropertySetInfo());
190 // Note: The Text may also be secured here, so it may also be possible to
191 // secure/store it at PointStyle instead of at TextBody, same maybe evaluated
192 // for the text attributes - where when securing here the attributes would be
193 // in our UNO API format already.
194 // if(xServiceInfo->supportsService("com.sun.star.drawing.Text"))
196 // css::uno::Reference< css::text::XText > xText(rXShape, css::uno::UNO_QUERY);
197 // const OUString aText(xText->getString());
199 // if(!aText.isEmpty())
204 // Add all kinds of properties that are needed to re-create the XShape.
205 // For now this is a minimal example-selection, it will need to be extended
206 // over time for all kind of cases/properties
209 if(!bIsBackgroundShape
210 && xServiceInfo
->supportsService(u
"com.sun.star.drawing.TextProperties"_ustr
))
212 addProperty(UNO_NAME_CHAR_COLOR
, xInfo
, pTarget
->maProperties
, xPropSet
);
213 addProperty(UNO_NAME_CHAR_HEIGHT
, xInfo
, pTarget
->maProperties
, xPropSet
);
214 addProperty(UNO_NAME_CHAR_SHADOWED
, xInfo
, pTarget
->maProperties
, xPropSet
);
215 addProperty(UNO_NAME_CHAR_WEIGHT
, xInfo
, pTarget
->maProperties
, xPropSet
);
219 if(xServiceInfo
->supportsService(u
"com.sun.star.drawing.FillProperties"_ustr
))
221 css::drawing::FillStyle
eFillStyle(css::drawing::FillStyle_NONE
);
222 if (xInfo
->hasPropertyByName(UNO_NAME_FILLSTYLE
))
223 xPropSet
->getPropertyValue(UNO_NAME_FILLSTYLE
) >>= eFillStyle
;
225 if(css::drawing::FillStyle_NONE
!= eFillStyle
)
227 addProperty(UNO_NAME_FILLSTYLE
, xInfo
, pTarget
->maProperties
, xPropSet
);
231 case css::drawing::FillStyle_SOLID
:
233 addProperty(UNO_NAME_FILLCOLOR
, xInfo
, pTarget
->maProperties
, xPropSet
);
237 case css::drawing::FillStyle_NONE
:
238 case css::drawing::FillStyle_GRADIENT
:
239 case css::drawing::FillStyle_HATCH
:
240 case css::drawing::FillStyle_BITMAP
:
247 if(!bIsBackgroundShape
248 && xServiceInfo
->supportsService(u
"com.sun.star.drawing.LineProperties"_ustr
))
250 css::drawing::LineStyle
eLineStyle(css::drawing::LineStyle_NONE
);
251 if (xInfo
->hasPropertyByName(UNO_NAME_LINESTYLE
))
252 xPropSet
->getPropertyValue(UNO_NAME_LINESTYLE
) >>= eLineStyle
;
254 if(css::drawing::LineStyle_NONE
!= eLineStyle
)
256 addProperty(UNO_NAME_LINESTYLE
, xInfo
, pTarget
->maProperties
, xPropSet
);
257 addProperty(UNO_NAME_LINECOLOR
, xInfo
, pTarget
->maProperties
, xPropSet
);
258 addProperty(UNO_NAME_LINEWIDTH
, xInfo
, pTarget
->maProperties
, xPropSet
);
262 case css::drawing::LineStyle_SOLID
:
265 case css::drawing::LineStyle_NONE
:
266 case css::drawing::LineStyle_DASH
:
273 void DiagramData::secureDataFromShapeToModelAfterDiagramImport(::oox::drawingml::Shape
& rRootShape
)
275 const std::vector
< ShapePtr
>& rChildren(rRootShape
.getChildren());
277 for (auto& child
: rChildren
)
279 secureStyleDataFromShapeToModel(*child
);
282 // After Diagram import, parts of the Diagram ModelData is at the
283 // oox::drawingml::Shape. Since these objects are temporary helpers,
284 // secure that data at the Diagram ModelData by copying.
286 // This is currently mainly the Text, but may get more (styles?)
287 for (auto & point
: maPoints
)
289 Shape
* pShapeCandidate(getOrCreateAssociatedShape(point
));
291 if(nullptr != pShapeCandidate
)
293 if(pShapeCandidate
->getTextBody() && !pShapeCandidate
->getTextBody()->isEmpty())
295 point
.msTextBody
->msText
= pShapeCandidate
->getTextBody()->toString();
297 const uno::Sequence
< beans::PropertyValue
> aTextProps(
298 pShapeCandidate
->getTextBody()->getTextProperties().maPropertyMap
.makePropertyValueSequence());
300 for (auto const& prop
: aTextProps
)
301 point
.msTextBody
->maTextProps
.push_back(std::pair(prop
.Name
, prop
.Value
));
304 // At this place a mechanism to find missing data should be added:
305 // Create a Shape from so-far secured data & compare it with the
306 // imported one. Report differences to allow extending the mechanism
309 // The original is pShapeCandidate, re-create potential new oox::drawingml::Shape
310 // as aNew to be able to compare these
311 ShapePtr
aNew(std::make_shared
<Shape
>());
312 restoreDataFromModelToShapeAfterReCreation(point
, *aNew
);
314 // Unfortunately oox::drawingml::Shape has no operator==. I tried to add
315 // one, but that is too expensive. I stopped at oox::drawingml::Color.
316 // To compare it is necessary to use the debugger, or for single aspects
317 // of the oox data it might be possible to call local dump() methods at
318 // both instances to compare them/their output
320 // bool bSame(aNew.get() == pShapeCandidate);
326 void DiagramData::restoreStyleDataFromShapeToModel(::oox::drawingml::Shape
& rShape
)
328 const std::vector
< ShapePtr
>& rChildren(rShape
.getChildren());
330 if(!rChildren
.empty())
333 for (auto& child
: rChildren
)
335 restoreStyleDataFromShapeToModel(*child
);
338 // if group shape we are done. Do not restore properties for group shapes
343 const css::uno::Reference
< css::drawing::XShape
> &rXShape(rShape
.getXShape());
347 // we need a ModelID for association
348 if(rShape
.getDiagramDataModelID().isEmpty())
351 // define source to save to
352 svx::diagram::PointStyle
* pSource(nullptr);
354 if(rShape
.getDiagramDataModelID() == msBackgroundShapeModelID
)
356 // if BackgroundShape, set BackgroundShapeStyle as source
357 if(maBackgroundShapeStyle
)
358 pSource
= maBackgroundShapeStyle
.get();
362 // if Shape, seek association
363 for (auto & point
: maPoints
)
365 if(point
.msModelId
== rShape
.getDiagramDataModelID())
367 // found - create properties & set as source
368 pSource
= point
.msPointStylePtr
.get();
370 // we are done, there is no 2nd shape with the same ModelID by definition
376 // no source -> nothing to do
377 if(nullptr == pSource
)
380 // get target PropertySet of new XShape
381 css::uno::Reference
<css::beans::XPropertySet
> xPropSet(rXShape
, css::uno::UNO_QUERY
);
386 for (auto const& prop
: pSource
->maProperties
)
388 xPropSet
->setPropertyValue(prop
.first
, prop
.second
);
392 void DiagramData::restoreDataFromShapeToModelAfterDiagramImport(::oox::drawingml::Shape
& rRootShape
)
394 const std::vector
< ShapePtr
>& rChildren(rRootShape
.getChildren());
396 for (auto& child
: rChildren
)
398 restoreStyleDataFromShapeToModel(*child
);
402 DiagramData::DiagramData()
403 : svx::diagram::DiagramData()
404 , mpBackgroundShapeFillProperties( std::make_shared
<FillProperties
>() )
408 DiagramData::~DiagramData()
412 static void Connection_dump(const svx::diagram::Connection
& rConnection
)
416 "cnx modelId " << rConnection
.msModelId
<< ", srcId " << rConnection
.msSourceId
<< ", dstId "
417 << rConnection
.msDestId
<< ", parTransId " << rConnection
.msParTransId
<< ", presId "
418 << rConnection
.msPresId
<< ", sibTransId " << rConnection
.msSibTransId
<< ", srcOrd "
419 << rConnection
.mnSourceOrder
<< ", dstOrd " << rConnection
.mnDestOrder
);
422 static void Point_dump(const svx::diagram::Point
& rPoint
, const Shape
* pShape
)
426 "pt text " << pShape
<< ", cnxId " << rPoint
.msCnxId
<< ", modelId "
427 << rPoint
.msModelId
<< ", type " << rPoint
.mnXMLType
);
430 void DiagramData::dump() const
432 SAL_INFO("oox.drawingml", "Dgm: DiagramData # of cnx: " << maConnections
.size() );
433 for (const auto& rConnection
: maConnections
)
434 Connection_dump(rConnection
);
436 SAL_INFO("oox.drawingml", "Dgm: DiagramData # of pt: " << maPoints
.size() );
437 for (const auto& rPoint
: maPoints
)
438 Point_dump(rPoint
, getOrCreateAssociatedShape(rPoint
));
441 void DiagramData::buildDiagramDataModel(bool bClearOoxShapes
)
445 // Delete/remove all existing oox::drawingml::Shape
446 maPointShapeMap
.clear();
450 svx::diagram::DiagramData::buildDiagramDataModel(bClearOoxShapes
);
454 // re-create all existing oox::drawingml::Shape
455 svx::diagram::Points
& rPoints
= getPoints();
457 for (auto & point
: rPoints
)
459 // Create/get shape. Re-create here, that may also set needed
460 // and available data from the Diagram ModelData at the Shape
461 getOrCreateAssociatedShape(point
, true);
468 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */