LibreOffice Module oox (master)  1
datamodel.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "datamodel.hxx"
21 
22 #include <rtl/ustrbuf.hxx>
23 #include <sal/log.hxx>
25 #include <drawingml/textbody.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>
33 
34 #include <unordered_set>
35 
36 using namespace ::com::sun::star;
37 
38 namespace oox::drawingml {
39 
40 Shape* DiagramData::getOrCreateAssociatedShape(const svx::diagram::Point& rPoint, bool bCreateOnDemand) const
41 {
42  if(maPointShapeMap.end() == maPointShapeMap.find(rPoint.msModelId))
43  {
44  const_cast<DiagramData*>(this)->maPointShapeMap[rPoint.msModelId] = ShapePtr();
45  }
46 
47  const ShapePtr& rShapePtr = maPointShapeMap.find(rPoint.msModelId)->second;
48 
49  if(!rShapePtr && bCreateOnDemand)
50  {
51  const_cast<ShapePtr&>(rShapePtr) = std::make_shared<Shape>();
52 
53  // If we did create a new oox::drawingml::Shape, directly apply
54  // available data from the Diagram ModelData to it as preparation
56  }
57 
58  return rShapePtr.get();
59 }
60 
62 {
63  // If we did create a new oox::drawingml::Shape, directly apply
64  // available data from the Diagram ModelData to it as preparation
65 
66  // This is e.g. the Text, but may get more (styles?)
67  if(!rPoint.msTextBody->msText.isEmpty())
68  {
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);
74 
75  if(!rPoint.msTextBody->maTextProps.empty())
76  {
77  oox::PropertyMap& rTargetMap(aNewTextBody->getTextProperties().maPropertyMap);
78 
79  for (auto const& prop : rPoint.msTextBody->maTextProps)
80  {
81  const sal_Int32 nPropId(oox::PropertyMap::getPropertyId(prop.first));
82  if(nPropId > 0)
83  rTargetMap.setAnyProperty(nPropId, prop.second);
84  }
85  }
86  }
87 }
88 
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 )
93 {
94  if(xInfo->hasPropertyByName(rName))
95  rTarget.push_back(std::pair(OUString(rName), xPropSet->getPropertyValue(rName)));
96 }
97 
99 {
100  const std::vector< ShapePtr >& rChildren(rShape.getChildren());
101 
102  if(!rChildren.empty())
103  {
104  // group shape
105  for (auto& child : rChildren)
106  {
108  }
109 
110  // if group shape we are done. Do not secure properties for group shapes
111  return;
112  }
113 
114  // we need a XShape
115  const css::uno::Reference< css::drawing::XShape > &rXShape(rShape.getXShape());
116  if(!rXShape)
117  return;
118 
119  // we need a ModelID for association
120  if(rShape.getDiagramDataModelID().isEmpty())
121  return;
122 
123  // define target to save to
124  svx::diagram::PointStyle* pTarget(nullptr);
125  const bool bIsBackgroundShape(rShape.getDiagramDataModelID() == msBackgroundShapeModelID);
126 
127  if(bIsBackgroundShape)
128  {
129  // if BackgroundShape, create properties & set as target
131  maBackgroundShapeStyle = std::make_shared< svx::diagram::PointStyle >();
132  pTarget = maBackgroundShapeStyle.get();
133  }
134  else
135  {
136  // if Shape, seek association
137  for (auto & point : maPoints)
138  {
139  if(point.msModelId == rShape.getDiagramDataModelID())
140  {
141  // found - create properties & set as target
142  pTarget = point.msPointStylePtr.get();
143 
144  // we are done, there is no 2nd shape with the same ModelID by definition
145  break;
146  }
147  }
148  }
149 
150  // no target -> nothing to do
151  if(nullptr == pTarget)
152  return;
153 
154 #ifdef DBG_UTIL
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)
162  {
163  try
164  {
165  if (xAllPropStates->getPropertyState(rProp.Name) == css::beans::PropertyState::PropertyState_DIRECT_VALUE)
166  {
167  css::uno::Any aValue(xAllPropSet->getPropertyValue(rProp.Name));
168  if(aValue.hasValue())
169  allSetProps.push_back(std::pair(rProp.Name, aValue));
170  }
171  }
172  catch (...)
173  {
174  }
175  }
176 #endif
177 
178  const css::uno::Reference< css::beans::XPropertySet > xPropSet( rXShape, css::uno::UNO_QUERY );
179  if(!xPropSet)
180  return;
181 
182  const css::uno::Reference< css::lang::XServiceInfo > xServiceInfo( rXShape, css::uno::UNO_QUERY );
183  if(!xServiceInfo)
184  return;
185 
186  const css::uno::Reference< css::beans::XPropertySetInfo > xInfo(xPropSet->getPropertySetInfo());
187  if (!xInfo.is())
188  return;
189 
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"))
195  // {
196  // css::uno::Reference< css::text::XText > xText(rXShape, css::uno::UNO_QUERY);
197  // const OUString aText(xText->getString());
198  //
199  // if(!aText.isEmpty())
200  // {
201  // }
202  // }
203 
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
207 
208  // text properties
209  if(!bIsBackgroundShape
210  && xServiceInfo->supportsService("com.sun.star.drawing.TextProperties"))
211  {
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);
216  }
217 
218  // fill properties
219  if(xServiceInfo->supportsService("com.sun.star.drawing.FillProperties"))
220  {
221  css::drawing::FillStyle eFillStyle(css::drawing::FillStyle_NONE);
222  if (xInfo->hasPropertyByName(UNO_NAME_FILLSTYLE))
223  xPropSet->getPropertyValue(UNO_NAME_FILLSTYLE) >>= eFillStyle;
224 
225  if(css::drawing::FillStyle_NONE != eFillStyle)
226  {
227  addProperty(UNO_NAME_FILLSTYLE, xInfo, pTarget->maProperties, xPropSet);
228 
229  switch(eFillStyle)
230  {
231  case css::drawing::FillStyle_SOLID:
232  {
233  addProperty(UNO_NAME_FILLCOLOR, xInfo, pTarget->maProperties, xPropSet);
234  break;
235  }
236  default:
237  case css::drawing::FillStyle_NONE:
238  case css::drawing::FillStyle_GRADIENT:
239  case css::drawing::FillStyle_HATCH:
240  case css::drawing::FillStyle_BITMAP:
241  break;
242  }
243  }
244  }
245 
246  // line properties
247  if(!bIsBackgroundShape
248  && xServiceInfo->supportsService("com.sun.star.drawing.LineProperties"))
249  {
250  css::drawing::LineStyle eLineStyle(css::drawing::LineStyle_NONE);
251  if (xInfo->hasPropertyByName(UNO_NAME_LINESTYLE))
252  xPropSet->getPropertyValue(UNO_NAME_LINESTYLE) >>= eLineStyle;
253 
254  if(css::drawing::LineStyle_NONE != eLineStyle)
255  {
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);
259 
260  switch(eLineStyle)
261  {
262  case css::drawing::LineStyle_SOLID:
263  break;
264  default:
265  case css::drawing::LineStyle_NONE:
266  case css::drawing::LineStyle_DASH:
267  break;
268  }
269  }
270  }
271 }
272 
274 {
275  const std::vector< ShapePtr >& rChildren(rRootShape.getChildren());
276 
277  for (auto& child : rChildren)
278  {
280  }
281 
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.
285 
286  // This is currently mainly the Text, but may get more (styles?)
287  for (auto & point : maPoints)
288  {
289  Shape* pShapeCandidate(getOrCreateAssociatedShape(point));
290 
291  if(nullptr != pShapeCandidate)
292  {
293  if(pShapeCandidate->getTextBody() && !pShapeCandidate->getTextBody()->isEmpty())
294  {
295  point.msTextBody->msText = pShapeCandidate->getTextBody()->toString();
296 
297  const uno::Sequence< beans::PropertyValue > aTextProps(
298  pShapeCandidate->getTextBody()->getTextProperties().maPropertyMap.makePropertyValueSequence());
299 
300  for (auto const& prop : aTextProps)
301  point.msTextBody->maTextProps.push_back(std::pair(prop.Name, prop.Value));
302  }
303 
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
307  // more easily.
308 #ifdef DBG_UTIL
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>());
313 
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
319 
320  // bool bSame(aNew.get() == pShapeCandidate);
321 #endif
322  }
323  }
324 }
325 
327 {
328  const std::vector< ShapePtr >& rChildren(rShape.getChildren());
329 
330  if(!rChildren.empty())
331  {
332  // group shape
333  for (auto& child : rChildren)
334  {
336  }
337 
338  // if group shape we are done. Do not restore properties for group shapes
339  return;
340  }
341 
342  // we need a XShape
343  const css::uno::Reference< css::drawing::XShape > &rXShape(rShape.getXShape());
344  if(!rXShape)
345  return;
346 
347  // we need a ModelID for association
348  if(rShape.getDiagramDataModelID().isEmpty())
349  return;
350 
351  // define source to save to
352  svx::diagram::PointStyle* pSource(nullptr);
353 
355  {
356  // if BackgroundShape, set BackgroundShapeStyle as source
358  pSource = maBackgroundShapeStyle.get();
359  }
360  else
361  {
362  // if Shape, seek association
363  for (auto & point : maPoints)
364  {
365  if(point.msModelId == rShape.getDiagramDataModelID())
366  {
367  // found - create properties & set as source
368  pSource = point.msPointStylePtr.get();
369 
370  // we are done, there is no 2nd shape with the same ModelID by definition
371  break;
372  }
373  }
374  }
375 
376  // no source -> nothing to do
377  if(nullptr == pSource)
378  return;
379 
380  // get target PropertySet of new XShape
381  css::uno::Reference<css::beans::XPropertySet> xPropSet(rXShape, css::uno::UNO_QUERY);
382  if(!xPropSet)
383  return;
384 
385  // apply properties
386  for (auto const& prop : pSource->maProperties)
387  {
388  xPropSet->setPropertyValue(prop.first, prop.second);
389  }
390 }
391 
393 {
394  const std::vector< ShapePtr >& rChildren(rRootShape.getChildren());
395 
396  for (auto& child : rChildren)
397  {
399  }
400 }
401 
403 : svx::diagram::DiagramData()
404 , mpBackgroundShapeFillProperties( std::make_shared<FillProperties>() )
405 {
406 }
407 
409 {
410 }
411 
412 static void Connection_dump(const svx::diagram::Connection& rConnection)
413 {
414  SAL_INFO(
415  "oox.drawingml",
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);
420 }
421 
422 static void Point_dump(const svx::diagram::Point& rPoint, const Shape* pShape)
423 {
424  SAL_INFO(
425  "oox.drawingml",
426  "pt text " << pShape << ", cnxId " << rPoint.msCnxId << ", modelId "
427  << rPoint.msModelId << ", type " << rPoint.mnXMLType);
428 }
429 
430 void DiagramData::dump() const
431 {
432  SAL_INFO("oox.drawingml", "Dgm: DiagramData # of cnx: " << maConnections.size() );
433  for (const auto& rConnection : maConnections)
434  Connection_dump(rConnection);
435 
436  SAL_INFO("oox.drawingml", "Dgm: DiagramData # of pt: " << maPoints.size() );
437  for (const auto& rPoint : maPoints)
438  Point_dump(rPoint, getOrCreateAssociatedShape(rPoint));
439 }
440 
441 void DiagramData::buildDiagramDataModel(bool bClearOoxShapes)
442 {
443  if(bClearOoxShapes)
444  {
445  // Delete/remove all existing oox::drawingml::Shape
446  maPointShapeMap.clear();
447  }
448 
449  // call parent
451 
452  if(bClearOoxShapes)
453  {
454  // re-create all existing oox::drawingml::Shape
455  svx::diagram::Points& rPoints = getPoints();
456 
457  for (auto & point : rPoints)
458  {
459  // Create/get shape. Re-create here, that may also set needed
460  // and available data from the Diagram ModelData at the Shape
462  }
463  }
464 }
465 
466 }
467 
468 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::vector< Point > Points
A helper that maps property identifiers to property values.
Definition: propertymap.hxx:51
constexpr OUStringLiteral UNO_NAME_LINEWIDTH
PointShapeMap maPointShapeMap
Definition: datamodel.hxx:69
const OUString & getDiagramDataModelID() const
Definition: shape.hxx:121
FilterGroup & rTarget
def point()
std::shared_ptr< T > make_shared(Args &&...args)
std::vector< std::pair< OUString, css::uno::Any > > maProperties
std::shared_ptr< TextRun > TextRunPtr
Definition: textrun.hxx:65
constexpr OUStringLiteral UNO_NAME_FILLSTYLE
constexpr OUStringLiteral UNO_NAME_CHAR_HEIGHT
static sal_Int32 getPropertyId(std::u16string_view sPropName)
Returns the property identifier of the passed name.
void setTextBody(const TextBodyPtr &pTextBody)
Definition: shape.cxx:1973
TypeConstant mnXMLType
static void Connection_dump(const svx::diagram::Connection &rConnection)
Definition: datamodel.cxx:412
void secureStyleDataFromShapeToModel(::oox::drawingml::Shape &rShape)
Definition: datamodel.cxx:98
void secureDataFromShapeToModelAfterDiagramImport(::oox::drawingml::Shape &rRootShape)
Definition: datamodel.cxx:273
virtual void buildDiagramDataModel(bool bClearOoxShapes)
Definition: datamodel.cxx:441
constexpr OUStringLiteral UNO_NAME_LINECOLOR
Shape * getOrCreateAssociatedShape(const svx::diagram::Point &rPoint, bool bCreateOnDemand=false) const
Definition: datamodel.cxx:40
virtual void dump() const
Definition: datamodel.cxx:430
static void Point_dump(const svx::diagram::Point &rPoint, const Shape *pShape)
Definition: datamodel.cxx:422
static void restoreDataFromModelToShapeAfterReCreation(const svx::diagram::Point &rPoint, Shape &rNewShape)
Definition: datamodel.cxx:61
std::vector< ShapePtr > & getChildren()
Definition: shape.hxx:162
constexpr OUStringLiteral UNO_NAME_LINESTYLE
std::shared_ptr< TextBody > TextBodyPtr
virtual void buildDiagramDataModel(bool bClearOoxShapes)
void restoreDataFromShapeToModelAfterDiagramImport(::oox::drawingml::Shape &rRootShape)
Definition: datamodel.cxx:392
const TextBodyPtr & getTextBody() const
Definition: shape.hxx:188
TextBodyPtr msTextBody
constexpr OUStringLiteral UNO_NAME_CHAR_SHADOWED
eFillStyle
#define SAL_INFO(area, stream)
constexpr OUStringLiteral UNO_NAME_CHAR_WEIGHT
const css::uno::Reference< css::drawing::XShape > & getXShape() const
Definition: shape.hxx:208
constexpr OUStringLiteral UNO_NAME_CHAR_COLOR
PointStylePtr maBackgroundShapeStyle
constexpr OUStringLiteral UNO_NAME_FILLCOLOR
void restoreStyleDataFromShapeToModel(::oox::drawingml::Shape &rShape)
Definition: datamodel.cxx:326
std::shared_ptr< Shape > ShapePtr
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)
Definition: datamodel.cxx:89