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 "diagramlayoutatoms.hxx"
24 #include "layoutatomvisitorbase.hxx"
26 #include <basegfx/numeric/ftools.hxx>
27 #include <sal/log.hxx>
29 #include <o3tl/unit_conversion.hxx>
30 #include <oox/helper/attributelist.hxx>
31 #include <oox/token/properties.hxx>
32 #include <drawingml/fillproperties.hxx>
33 #include <drawingml/lineproperties.hxx>
34 #include <drawingml/textbody.hxx>
35 #include <drawingml/textparagraph.hxx>
36 #include <drawingml/textrun.hxx>
37 #include <drawingml/customshapeproperties.hxx>
38 #include <com/sun/star/drawing/TextFitToSizeType.hpp>
40 using namespace ::com::sun::star
;
41 using namespace ::com::sun::star::uno
;
42 using namespace ::com::sun::star::xml::sax
;
43 using namespace ::oox::core
;
47 /// Looks up the value of the rInternalName -> nProperty key in rProperties.
48 std::optional
<sal_Int32
> findProperty(const oox::drawingml::LayoutPropertyMap
& rProperties
,
49 const OUString
& rInternalName
, sal_Int32 nProperty
)
51 std::optional
<sal_Int32
> oRet
;
53 auto it
= rProperties
.find(rInternalName
);
54 if (it
!= rProperties
.end())
56 const oox::drawingml::LayoutProperty
& rProperty
= it
->second
;
57 auto itProperty
= rProperty
.find(nProperty
);
58 if (itProperty
!= rProperty
.end())
59 oRet
= itProperty
->second
;
66 * Determines if nUnit is a font unit (measured in points) or not (measured in
69 bool isFontUnit(sal_Int32 nUnit
)
71 return nUnit
== oox::XML_primFontSz
|| nUnit
== oox::XML_secFontSz
;
74 /// Determines which UNO property should be set for a given constraint type.
75 sal_Int32
getPropertyFromConstraint(sal_Int32 nConstraint
)
80 return oox::PROP_TextLeftDistance
;
82 return oox::PROP_TextRightDistance
;
84 return oox::PROP_TextUpperDistance
;
86 return oox::PROP_TextLowerDistance
;
93 * Determines if pShape is (or contains) a presentation of a data node of type
96 bool containsDataNodeType(const oox::drawingml::ShapePtr
& pShape
, sal_Int32 nType
)
98 if (pShape
->getDataNodeType() == nType
)
101 for (const auto& pChild
: pShape
->getChildren())
103 if (containsDataNodeType(pChild
, nType
))
111 namespace oox::drawingml
{
112 void SnakeAlg::layoutShapeChildren(const AlgAtom
& rAlg
, const ShapePtr
& rShape
,
113 const std::vector
<Constraint
>& rConstraints
)
115 if (rShape
->getChildren().empty() || rShape
->getSize().Width
== 0
116 || rShape
->getSize().Height
== 0)
119 // Parse constraints.
120 double fChildAspectRatio
= rShape
->getChildren()[0]->getAspectRatio();
121 double fShapeHeight
= rShape
->getSize().Height
;
122 double fShapeWidth
= rShape
->getSize().Width
;
123 // Check if we have a child aspect ratio. If so, need to shrink one dimension to
124 // achieve that ratio.
125 if (fChildAspectRatio
&& fShapeHeight
&& fChildAspectRatio
< (fShapeWidth
/ fShapeHeight
))
127 fShapeWidth
= fShapeHeight
* fChildAspectRatio
;
130 double fSpaceFromConstraint
= 1.0;
131 LayoutPropertyMap aPropertiesByName
;
132 std::map
<sal_Int32
, LayoutProperty
> aPropertiesByType
;
133 LayoutProperty
& rParent
= aPropertiesByName
[""];
134 rParent
[XML_w
] = fShapeWidth
;
135 rParent
[XML_h
] = fShapeHeight
;
136 for (const auto& rConstr
: rConstraints
)
138 if (rConstr
.mnRefType
== XML_w
|| rConstr
.mnRefType
== XML_h
)
140 if (rConstr
.mnType
== XML_sp
&& rConstr
.msForName
.isEmpty())
141 fSpaceFromConstraint
= rConstr
.mfFactor
;
144 auto itRefForName
= aPropertiesByName
.find(rConstr
.msRefForName
);
145 if (itRefForName
== aPropertiesByName
.end())
150 auto it
= itRefForName
->second
.find(rConstr
.mnRefType
);
151 if (it
== itRefForName
->second
.end())
156 if (rConstr
.mfValue
!= 0.0)
161 sal_Int32 nValue
= it
->second
* rConstr
.mfFactor
;
163 if (rConstr
.mnPointType
== XML_none
)
165 aPropertiesByName
[rConstr
.msForName
][rConstr
.mnType
] = nValue
;
169 aPropertiesByType
[rConstr
.mnPointType
][rConstr
.mnType
] = nValue
;
173 std::vector
<sal_Int32
> aShapeWidths(rShape
->getChildren().size());
174 for (size_t i
= 0; i
< rShape
->getChildren().size(); ++i
)
176 ShapePtr pChild
= rShape
->getChildren()[i
];
177 if (!pChild
->getDataNodeType())
179 // TODO handle the case when the requirement applies by name, not by point type.
180 aShapeWidths
[i
] = fShapeWidth
;
184 auto itNodeType
= aPropertiesByType
.find(pChild
->getDataNodeType());
185 if (itNodeType
== aPropertiesByType
.end())
187 aShapeWidths
[i
] = fShapeWidth
;
191 auto it
= itNodeType
->second
.find(XML_w
);
192 if (it
== itNodeType
->second
.end())
194 aShapeWidths
[i
] = fShapeWidth
;
198 aShapeWidths
[i
] = it
->second
;
201 bool bSpaceFromConstraints
= fSpaceFromConstraint
!= 1.0;
203 const AlgAtom::ParamMap
& rMap
= rAlg
.getMap();
204 const sal_Int32 nDir
= rMap
.count(XML_grDir
) ? rMap
.find(XML_grDir
)->second
: XML_tL
;
207 bool bHorizontal
= true;
230 sal_Int32 nCount
= rShape
->getChildren().size();
231 // Defaults in case not provided by constraints.
232 double fSpace
= bSpaceFromConstraints
? fSpaceFromConstraint
: 0.3;
233 double fAspectRatio
= 0.54; // diagram should not spill outside, earlier it was 0.6
237 sal_Int32 nMaxRowWidth
= 0;
238 if (nCount
<= fChildAspectRatio
)
239 // Child aspect ratio request (width/height) is N, and we have at most N shapes.
240 // This means we don't need multiple columns.
244 for (; nRow
< nCount
; nRow
++)
246 nCol
= std::ceil(static_cast<double>(nCount
) / nRow
);
247 sal_Int32 nRowWidth
= 0;
248 for (sal_Int32 i
= 0; i
< nCol
; ++i
)
255 nRowWidth
+= aShapeWidths
[i
];
257 double fTotalShapesHeight
= fShapeHeight
* nRow
;
258 if (nRowWidth
&& fTotalShapesHeight
/ nRowWidth
>= fAspectRatio
)
260 if (nRowWidth
> nMaxRowWidth
)
262 nMaxRowWidth
= nRowWidth
;
269 SAL_INFO("oox.drawingml", "Snake layout grid: " << nCol
<< "x" << nRow
);
271 sal_Int32 nWidth
= rShape
->getSize().Width
/ (nCol
+ (nCol
- 1) * fSpace
);
272 awt::Size
aChildSize(nWidth
, nWidth
* fAspectRatio
);
273 if (nCol
== 1 && nRow
> 1)
275 // We have a single column, so count the height based on the parent height, not
277 // Space occurs inside children; also double amount of space is needed outside (on
278 // both sides), if the factor comes from a constraint.
279 sal_Int32 nNumSpaces
= -1;
280 if (bSpaceFromConstraints
)
282 sal_Int32 nHeight
= rShape
->getSize().Height
/ (nRow
+ (nRow
+ nNumSpaces
) * fSpace
);
284 if (fChildAspectRatio
> 1)
286 // Shrink width if the aspect ratio requires it.
287 nWidth
= std::min(rShape
->getSize().Width
,
288 static_cast<sal_Int32
>(nHeight
* fChildAspectRatio
));
289 aChildSize
= awt::Size(nWidth
, nHeight
);
295 awt::Point
aCurrPos(0, 0);
297 aCurrPos
.X
= rShape
->getSize().Width
- aChildSize
.Width
;
299 aCurrPos
.Y
= rShape
->getSize().Height
- aChildSize
.Height
;
300 else if (bSpaceFromConstraints
)
304 // Initial vertical offset to have upper spacing (outside, so double amount).
305 aCurrPos
.Y
= aChildSize
.Height
* fSpace
* 2;
309 sal_Int32 nStartX
= aCurrPos
.X
;
310 sal_Int32 nColIdx
= 0, index
= 0;
312 const sal_Int32 aContDir
313 = rMap
.count(XML_contDir
) ? rMap
.find(XML_contDir
)->second
: XML_sameDir
;
319 sal_Int32 nRowHeight
= 0;
320 for (auto& aCurrShape
: rShape
->getChildren())
322 aCurrShape
->setPosition(aCurrPos
);
323 awt::Size
aCurrSize(aChildSize
);
324 // aShapeWidths items are a portion of nMaxRowWidth. We want the same ratio,
325 // based on the original parent width, ignoring the aspect ratio request.
326 bool bWidthsFromConstraints
327 = nCount
>= 2 && rShape
->getChildren()[1]->getDataNodeType() == XML_sibTrans
;
328 if (bWidthsFromConstraints
&& nMaxRowWidth
)
330 double fWidthFactor
= static_cast<double>(aShapeWidths
[index
]) / nMaxRowWidth
;
331 // We can only work from constraints if spacing is represented by a real
333 aCurrSize
.Width
= rShape
->getSize().Width
* fWidthFactor
;
335 if (fChildAspectRatio
)
337 aCurrSize
.Height
= aCurrSize
.Width
/ fChildAspectRatio
;
339 // Child shapes are not allowed to leave their parent.
340 aCurrSize
.Height
= std::min
<sal_Int32
>(
341 aCurrSize
.Height
, rShape
->getSize().Height
/ (nRow
+ (nRow
- 1) * fSpace
));
343 if (aCurrSize
.Height
> nRowHeight
)
345 nRowHeight
= aCurrSize
.Height
;
347 aCurrShape
->setSize(aCurrSize
);
348 aCurrShape
->setChildSize(aCurrSize
);
350 index
++; // counts index of child, helpful for positioning.
352 if (index
% nCol
== 0 || ((index
/ nCol
) + 1) != nRow
)
353 aCurrPos
.X
+= nIncX
* (aCurrSize
.Width
+ fSpace
* aCurrSize
.Width
);
355 if (++nColIdx
== nCol
) // condition for next row
357 // if last row, then position children according to number of shapes.
358 if ((index
+ 1) % nCol
!= 0 && (index
+ 1) >= 3
359 && ((index
+ 1) / nCol
+ 1) == nRow
&& nCount
!= nRow
* nCol
)
361 // position first child of last row
362 if (bWidthsFromConstraints
)
364 aCurrPos
.X
= nStartX
;
368 // Can assume that all child shape has the same width.
371 + (nIncX
* (aCurrSize
.Width
+ fSpace
* aCurrSize
.Width
)) / 2;
375 // if not last row, positions first child of that row
376 aCurrPos
.X
= nStartX
;
377 aCurrPos
.Y
+= nIncY
* (nRowHeight
+ fSpace
* nRowHeight
);
382 // positions children in the last row.
383 if (index
% nCol
!= 0 && index
>= 3 && ((index
/ nCol
) + 1) == nRow
)
384 aCurrPos
.X
+= (nIncX
* (aCurrSize
.Width
+ fSpace
* aCurrSize
.Width
));
389 for (auto& aCurrShape
: rShape
->getChildren())
391 aCurrShape
->setPosition(aCurrPos
);
392 aCurrShape
->setSize(aChildSize
);
393 aCurrShape
->setChildSize(aChildSize
);
395 index
++; // counts index of child, helpful for positioning.
398 index%col -> tests node is at last column
399 ((index/nCol)+1)!=nRow) -> tests node is at last row or not
400 ((index/nCol)+1)%2!=0 -> tests node is at row which is multiple of 2, important for revDir
401 num!=nRow*nCol -> tests how last row nodes should be spread.
404 if ((index
% nCol
== 0 || ((index
/ nCol
) + 1) != nRow
)
405 && ((index
/ nCol
) + 1) % 2 != 0)
406 aCurrPos
.X
+= (aChildSize
.Width
+ fSpace
* aChildSize
.Width
);
407 else if (index
% nCol
!= 0
408 && ((index
/ nCol
) + 1) != nRow
) // child other than placed at last column
409 aCurrPos
.X
-= (aChildSize
.Width
+ fSpace
* aChildSize
.Width
);
411 if (++nColIdx
== nCol
) // condition for next row
413 // if last row, then position children according to number of shapes.
414 if ((index
+ 1) % nCol
!= 0 && (index
+ 1) >= 4
415 && ((index
+ 1) / nCol
+ 1) == nRow
&& nCount
!= nRow
* nCol
416 && ((index
/ nCol
) + 1) % 2 == 0)
417 // position first child of last row
418 aCurrPos
.X
-= aChildSize
.Width
* 3 / 2;
419 else if ((index
+ 1) % nCol
!= 0 && (index
+ 1) >= 4
420 && ((index
+ 1) / nCol
+ 1) == nRow
&& nCount
!= nRow
* nCol
421 && ((index
/ nCol
) + 1) % 2 != 0)
423 + (nIncX
* (aChildSize
.Width
+ fSpace
* aChildSize
.Width
)) / 2;
424 else if (((index
/ nCol
) + 1) % 2 != 0)
425 aCurrPos
.X
= nStartX
;
427 aCurrPos
.Y
+= nIncY
* (aChildSize
.Height
+ fSpace
* aChildSize
.Height
);
431 // positions children in the last row.
432 if (index
% nCol
!= 0 && index
>= 3 && ((index
/ nCol
) + 1) == nRow
433 && ((index
/ nCol
) + 1) % 2 == 0)
434 //if row%2=0 then start from left else
435 aCurrPos
.X
-= (nIncX
* (aChildSize
.Width
+ fSpace
* aChildSize
.Width
));
436 else if (index
% nCol
!= 0 && index
>= 3 && ((index
/ nCol
) + 1) == nRow
437 && ((index
/ nCol
) + 1) % 2 != 0)
439 aCurrPos
.X
+= (nIncX
* (aChildSize
.Width
+ fSpace
* aChildSize
.Width
));
445 void PyraAlg::layoutShapeChildren(const ShapePtr
& rShape
)
447 if (rShape
->getChildren().empty() || rShape
->getSize().Width
== 0
448 || rShape
->getSize().Height
== 0)
451 // const sal_Int32 nDir = maMap.count(XML_linDir) ? maMap.find(XML_linDir)->second : XML_fromT;
452 // const sal_Int32 npyraAcctPos = maMap.count(XML_pyraAcctPos) ? maMap.find(XML_pyraAcctPos)->second : XML_bef;
453 // const sal_Int32 ntxDir = maMap.count(XML_txDir) ? maMap.find(XML_txDir)->second : XML_fromT;
454 // const sal_Int32 npyraLvlNode = maMap.count(XML_pyraLvlNode) ? maMap.find(XML_pyraLvlNode)->second : XML_level;
455 // uncomment when use in code.
457 sal_Int32 nCount
= rShape
->getChildren().size();
458 double fAspectRatio
= 0.32;
460 awt::Size aChildSize
= rShape
->getSize();
461 aChildSize
.Width
/= nCount
;
462 aChildSize
.Height
/= nCount
;
464 awt::Point
aCurrPos(0, 0);
465 aCurrPos
.X
= fAspectRatio
* aChildSize
.Width
* (nCount
- 1);
466 aCurrPos
.Y
= fAspectRatio
* aChildSize
.Height
;
468 for (auto& aCurrShape
: rShape
->getChildren())
470 aCurrShape
->setPosition(aCurrPos
);
473 aCurrPos
.X
-= aChildSize
.Height
/ (nCount
- 1);
475 aChildSize
.Width
+= aChildSize
.Height
;
476 aCurrShape
->setSize(aChildSize
);
477 aCurrShape
->setChildSize(aChildSize
);
478 aCurrPos
.Y
+= (aChildSize
.Height
);
482 bool CompositeAlg::inferFromLayoutProperty(const LayoutProperty
& rMap
, sal_Int32 nRefType
,
489 auto it
= rMap
.find(XML_l
);
490 if (it
== rMap
.end())
494 sal_Int32 nLeft
= it
->second
;
495 it
= rMap
.find(XML_w
);
496 if (it
== rMap
.end())
500 rValue
= nLeft
+ it
->second
;
510 void CompositeAlg::applyConstraintToLayout(const Constraint
& rConstraint
,
511 LayoutPropertyMap
& rProperties
)
513 // TODO handle the case when we have ptType="...", not forName="...".
514 if (rConstraint
.msForName
.isEmpty())
519 const LayoutPropertyMap::const_iterator aRef
= rProperties
.find(rConstraint
.msRefForName
);
520 if (aRef
== rProperties
.end())
523 const LayoutProperty::const_iterator aRefType
= aRef
->second
.find(rConstraint
.mnRefType
);
524 sal_Int32 nInferredValue
= 0;
525 if (aRefType
!= aRef
->second
.end())
527 // Reference is found directly.
528 rProperties
[rConstraint
.msForName
][rConstraint
.mnType
]
529 = aRefType
->second
* rConstraint
.mfFactor
;
531 else if (inferFromLayoutProperty(aRef
->second
, rConstraint
.mnRefType
, nInferredValue
))
533 // Reference can be inferred.
534 rProperties
[rConstraint
.msForName
][rConstraint
.mnType
]
535 = nInferredValue
* rConstraint
.mfFactor
;
539 // Reference not found, assume a fixed value.
540 // Values are never in EMU, while oox::drawingml::Shape position and size are always in
542 const double fValue
= o3tl::convert(rConstraint
.mfValue
,
543 isFontUnit(rConstraint
.mnRefType
) ? o3tl::Length::pt
546 rProperties
[rConstraint
.msForName
][rConstraint
.mnType
] = fValue
;
550 void CompositeAlg::layoutShapeChildren(AlgAtom
& rAlg
, const ShapePtr
& rShape
,
551 const std::vector
<Constraint
>& rConstraints
)
553 LayoutPropertyMap aProperties
;
554 LayoutProperty
& rParent
= aProperties
[""];
556 sal_Int32 nParentXOffset
= 0;
558 // Track min/max vertical positions, so we can center everything at the end, if needed.
559 sal_Int32 nVertMin
= std::numeric_limits
<sal_Int32
>::max();
560 sal_Int32 nVertMax
= 0;
562 if (rAlg
.getAspectRatio() != 1.0)
564 rParent
[XML_w
] = rShape
->getSize().Width
;
565 rParent
[XML_h
] = rShape
->getSize().Height
;
568 rParent
[XML_r
] = rShape
->getSize().Width
;
569 rParent
[XML_b
] = rShape
->getSize().Height
;
573 // Shrink width to be only as large as height.
574 rParent
[XML_w
] = std::min(rShape
->getSize().Width
, rShape
->getSize().Height
);
575 rParent
[XML_h
] = rShape
->getSize().Height
;
576 if (rParent
[XML_w
] < rShape
->getSize().Width
)
577 nParentXOffset
= (rShape
->getSize().Width
- rParent
[XML_w
]) / 2;
578 rParent
[XML_l
] = nParentXOffset
;
580 rParent
[XML_r
] = rShape
->getSize().Width
- rParent
[XML_l
];
581 rParent
[XML_b
] = rShape
->getSize().Height
;
584 for (const auto& rConstr
: rConstraints
)
586 // Apply direct constraints for all layout nodes.
587 applyConstraintToLayout(rConstr
, aProperties
);
590 for (auto& aCurrShape
: rShape
->getChildren())
592 // Apply constraints from the current layout node for this child shape.
593 // Previous child shapes may have changed aProperties.
594 for (const auto& rConstr
: rConstraints
)
596 if (rConstr
.msForName
!= aCurrShape
->getInternalName())
601 applyConstraintToLayout(rConstr
, aProperties
);
604 // Apply constraints from the child layout node for this child shape.
605 // This builds on top of the own parent state + the state of previous shapes in the
606 // same composite algorithm.
607 const LayoutNode
& rLayoutNode
= rAlg
.getLayoutNode();
608 for (const auto& pDirectChild
: rLayoutNode
.getChildren())
610 auto pLayoutNode
= dynamic_cast<LayoutNode
*>(pDirectChild
.get());
616 if (pLayoutNode
->getName() != aCurrShape
->getInternalName())
621 for (const auto& pChild
: pLayoutNode
->getChildren())
623 auto pConstraintAtom
= dynamic_cast<ConstraintAtom
*>(pChild
.get());
624 if (!pConstraintAtom
)
629 const Constraint
& rConstraint
= pConstraintAtom
->getConstraint();
630 if (!rConstraint
.msForName
.isEmpty())
635 if (!rConstraint
.msRefForName
.isEmpty())
640 // Either an absolute value or a factor of a property.
641 if (rConstraint
.mfValue
== 0.0 && rConstraint
.mnRefType
== XML_none
)
646 Constraint
aConstraint(rConstraint
);
647 aConstraint
.msForName
= pLayoutNode
->getName();
648 aConstraint
.msRefForName
= pLayoutNode
->getName();
650 applyConstraintToLayout(aConstraint
, aProperties
);
654 awt::Size aSize
= rShape
->getSize();
655 awt::Point
aPos(0, 0);
657 const LayoutPropertyMap::const_iterator aPropIt
658 = aProperties
.find(aCurrShape
->getInternalName());
659 if (aPropIt
!= aProperties
.end())
661 const LayoutProperty
& rProp
= aPropIt
->second
;
662 LayoutProperty::const_iterator it
, it2
;
664 if ((it
= rProp
.find(XML_w
)) != rProp
.end())
665 aSize
.Width
= std::min(it
->second
, rShape
->getSize().Width
);
666 if ((it
= rProp
.find(XML_h
)) != rProp
.end())
667 aSize
.Height
= std::min(it
->second
, rShape
->getSize().Height
);
669 if ((it
= rProp
.find(XML_l
)) != rProp
.end())
671 else if ((it
= rProp
.find(XML_ctrX
)) != rProp
.end())
672 aPos
.X
= it
->second
- aSize
.Width
/ 2;
673 else if ((it
= rProp
.find(XML_r
)) != rProp
.end())
674 aPos
.X
= it
->second
- aSize
.Width
;
676 if ((it
= rProp
.find(XML_t
)) != rProp
.end())
678 else if ((it
= rProp
.find(XML_ctrY
)) != rProp
.end())
679 aPos
.Y
= it
->second
- aSize
.Height
/ 2;
680 else if ((it
= rProp
.find(XML_b
)) != rProp
.end())
681 aPos
.Y
= it
->second
- aSize
.Height
;
683 if ((it
= rProp
.find(XML_l
)) != rProp
.end() && (it2
= rProp
.find(XML_r
)) != rProp
.end())
684 aSize
.Width
= it2
->second
- it
->second
;
685 if ((it
= rProp
.find(XML_t
)) != rProp
.end() && (it2
= rProp
.find(XML_b
)) != rProp
.end())
686 aSize
.Height
= it2
->second
- it
->second
;
688 aPos
.X
+= nParentXOffset
;
689 aSize
.Width
= std::min(aSize
.Width
, rShape
->getSize().Width
- aPos
.X
);
690 aSize
.Height
= std::min(aSize
.Height
, rShape
->getSize().Height
- aPos
.Y
);
693 SAL_WARN("oox.drawingml", "composite layout properties not found for shape "
694 << aCurrShape
->getInternalName());
696 aCurrShape
->setSize(aSize
);
697 aCurrShape
->setChildSize(aSize
);
698 aCurrShape
->setPosition(aPos
);
700 nVertMin
= std::min(aPos
.Y
, nVertMin
);
701 nVertMax
= std::max(aPos
.Y
+ aSize
.Height
, nVertMax
);
703 NamedShapePairs
& rDiagramFontHeights
704 = rAlg
.getLayoutNode().getDiagram().getDiagramFontHeights();
705 auto it
= rDiagramFontHeights
.find(aCurrShape
->getInternalName());
706 if (it
!= rDiagramFontHeights
.end())
708 // Internal name matches: put drawingml::Shape to the relevant group, for
709 // synchronized font height handling.
710 it
->second
.insert({ aCurrShape
, {} });
714 // See if all vertical space is used or we have to center the content.
715 if (!(nVertMin
>= 0 && nVertMin
<= nVertMax
&& nVertMax
<= rParent
[XML_h
]))
718 sal_Int32 nDiff
= rParent
[XML_h
] - (nVertMax
- nVertMin
);
721 for (auto& aCurrShape
: rShape
->getChildren())
723 awt::Point aPosition
= aCurrShape
->getPosition();
724 aPosition
.Y
+= nDiff
/ 2;
725 aCurrShape
->setPosition(aPosition
);
730 IteratorAttr::IteratorAttr( )
732 , mbHideLastTrans( true )
739 void IteratorAttr::loadFromXAttr( const Reference
< XFastAttributeList
>& xAttr
)
741 AttributeList
attr( xAttr
);
742 maAxis
= attr
.getTokenList(XML_axis
);
743 mnCnt
= attr
.getInteger( XML_cnt
, -1 );
744 mbHideLastTrans
= attr
.getBool( XML_hideLastTrans
, true );
745 mnSt
= attr
.getInteger( XML_st
, 0 );
746 mnStep
= attr
.getInteger( XML_step
, 1 );
748 // better to keep first token instead of error when multiple values
749 std::vector
<sal_Int32
> aPtTypes
= attr
.getTokenList(XML_ptType
);
750 mnPtType
= aPtTypes
.empty() ? XML_all
: aPtTypes
.front();
753 ConditionAttr::ConditionAttr()
761 void ConditionAttr::loadFromXAttr( const Reference
< XFastAttributeList
>& xAttr
)
763 mnFunc
= xAttr
->getOptionalValueToken( XML_func
, 0 );
764 mnArg
= xAttr
->getOptionalValueToken( XML_arg
, XML_none
);
765 mnOp
= xAttr
->getOptionalValueToken( XML_op
, 0 );
766 msVal
= xAttr
->getOptionalValue( XML_val
);
767 mnVal
= xAttr
->getOptionalValueToken( XML_val
, 0 );
770 void LayoutAtom::dump(int level
)
772 SAL_INFO("oox.drawingml", "level = " << level
<< " - " << msName
<< " of type " << typeid(*this).name() );
773 for (const auto& pAtom
: getChildren())
774 pAtom
->dump(level
+ 1);
777 ForEachAtom::ForEachAtom(LayoutNode
& rLayoutNode
, const Reference
< XFastAttributeList
>& xAttributes
) :
778 LayoutAtom(rLayoutNode
)
780 maIter
.loadFromXAttr(xAttributes
);
783 void ForEachAtom::accept( LayoutAtomVisitor
& rVisitor
)
785 rVisitor
.visit(*this);
788 LayoutAtomPtr
ForEachAtom::getRefAtom()
790 if (!msRef
.isEmpty())
792 const LayoutAtomMap
& rLayoutAtomMap
= getLayoutNode().getDiagram().getLayout()->getLayoutAtomMap();
793 LayoutAtomMap::const_iterator pRefAtom
= rLayoutAtomMap
.find(msRef
);
794 if (pRefAtom
!= rLayoutAtomMap
.end())
795 return pRefAtom
->second
;
797 SAL_WARN("oox.drawingml", "ForEach reference \"" << msRef
<< "\" not found");
799 return LayoutAtomPtr();
802 void ChooseAtom::accept( LayoutAtomVisitor
& rVisitor
)
804 rVisitor
.visit(*this);
807 ConditionAtom::ConditionAtom(LayoutNode
& rLayoutNode
, bool isElse
, const Reference
< XFastAttributeList
>& xAttributes
) :
808 LayoutAtom(rLayoutNode
),
811 maIter
.loadFromXAttr( xAttributes
);
812 maCond
.loadFromXAttr( xAttributes
);
815 bool ConditionAtom::compareResult(sal_Int32 nOperator
, sal_Int32 nFirst
, sal_Int32 nSecond
)
819 case XML_equ
: return nFirst
== nSecond
;
820 case XML_gt
: return nFirst
> nSecond
;
821 case XML_gte
: return nFirst
>= nSecond
;
822 case XML_lt
: return nFirst
< nSecond
;
823 case XML_lte
: return nFirst
<= nSecond
;
824 case XML_neq
: return nFirst
!= nSecond
;
826 SAL_WARN("oox.drawingml", "unsupported operator: " << nOperator
);
834 * Takes the connection list from rLayoutNode, navigates from rFrom on an edge
835 * of type nType, using a direction determined by bSourceToDestination.
837 OUString
navigate(LayoutNode
& rLayoutNode
, svx::diagram::TypeConstant nType
, std::u16string_view rFrom
,
838 bool bSourceToDestination
)
840 for (const auto& rConnection
: rLayoutNode
.getDiagram().getData()->getConnections())
842 if (rConnection
.mnXMLType
!= nType
)
845 if (bSourceToDestination
)
847 if (rConnection
.msSourceId
== rFrom
)
848 return rConnection
.msDestId
;
852 if (rConnection
.msDestId
== rFrom
)
853 return rConnection
.msSourceId
;
860 sal_Int32
calcMaxDepth(std::u16string_view rNodeName
, const svx::diagram::Connections
& rConnections
)
862 sal_Int32 nMaxLength
= 0;
863 for (auto const& aCxn
: rConnections
)
864 if (aCxn
.mnXMLType
== svx::diagram::TypeConstant::XML_parOf
&& aCxn
.msSourceId
== rNodeName
)
865 nMaxLength
= std::max(nMaxLength
, calcMaxDepth(aCxn
.msDestId
, rConnections
) + 1);
871 sal_Int32
ConditionAtom::getNodeCount(const svx::diagram::Point
* pPresPoint
) const
873 sal_Int32 nCount
= 0;
874 OUString sNodeId
= pPresPoint
->msPresentationAssociationId
;
876 // HACK: special case - count children of first child
877 if (maIter
.maAxis
.size() == 2 && maIter
.maAxis
[0] == XML_ch
&& maIter
.maAxis
[1] == XML_ch
)
878 sNodeId
= navigate(mrLayoutNode
, svx::diagram::TypeConstant::XML_parOf
, sNodeId
, /*bSourceToDestination*/ true);
880 if (!sNodeId
.isEmpty())
882 for (const auto& aCxn
: mrLayoutNode
.getDiagram().getData()->getConnections())
883 if (aCxn
.mnXMLType
== svx::diagram::TypeConstant::XML_parOf
&& aCxn
.msSourceId
== sNodeId
)
890 bool ConditionAtom::getDecision(const svx::diagram::Point
* pPresPoint
) const
897 switch (maCond
.mnFunc
)
901 if (maCond
.mnArg
== XML_dir
)
902 return compareResult(maCond
.mnOp
, pPresPoint
->mnDirection
, maCond
.mnVal
);
903 else if (maCond
.mnArg
== XML_hierBranch
)
905 sal_Int32 nHierarchyBranch
= pPresPoint
->moHierarchyBranch
.value_or(XML_std
);
906 if (!pPresPoint
->moHierarchyBranch
.has_value())
908 // If <dgm:hierBranch> is missing in the current presentation
909 // point, ask the parent.
910 OUString aParent
= navigate(mrLayoutNode
, svx::diagram::TypeConstant::XML_presParOf
, pPresPoint
->msModelId
,
911 /*bSourceToDestination*/ false);
912 DiagramData::PointNameMap
& rPointNameMap
913 = mrLayoutNode
.getDiagram().getData()->getPointNameMap();
914 auto it
= rPointNameMap
.find(aParent
);
915 if (it
!= rPointNameMap
.end())
917 const svx::diagram::Point
* pParent
= it
->second
;
918 if (pParent
->moHierarchyBranch
.has_value())
919 nHierarchyBranch
= pParent
->moHierarchyBranch
.value();
922 return compareResult(maCond
.mnOp
, nHierarchyBranch
, maCond
.mnVal
);
928 return compareResult(maCond
.mnOp
, getNodeCount(pPresPoint
), maCond
.msVal
.toInt32());
932 sal_Int32 nMaxDepth
= calcMaxDepth(pPresPoint
->msPresentationAssociationId
, mrLayoutNode
.getDiagram().getData()->getConnections());
933 return compareResult(maCond
.mnOp
, nMaxDepth
, maCond
.msVal
.toInt32());
943 SAL_WARN("oox.drawingml", "unknown function " << maCond
.mnFunc
);
950 void ConditionAtom::accept( LayoutAtomVisitor
& rVisitor
)
952 rVisitor
.visit(*this);
955 void ConstraintAtom::accept( LayoutAtomVisitor
& rVisitor
)
957 rVisitor
.visit(*this);
960 void RuleAtom::accept( LayoutAtomVisitor
& rVisitor
)
962 rVisitor
.visit(*this);
965 void ConstraintAtom::parseConstraint(std::vector
<Constraint
>& rConstraints
,
966 bool bRequireForName
) const
968 // Allowlist for cases where empty forName is handled.
971 switch (maConstraint
.mnType
)
978 bRequireForName
= false;
981 switch (maConstraint
.mnPointType
)
984 bRequireForName
= false;
989 if (bRequireForName
&& maConstraint
.msForName
.isEmpty())
992 // accepting only basic equality constraints
993 if ((maConstraint
.mnOperator
== XML_none
|| maConstraint
.mnOperator
== XML_equ
)
994 && maConstraint
.mnType
!= XML_none
)
996 rConstraints
.push_back(maConstraint
);
1000 void RuleAtom::parseRule(std::vector
<Rule
>& rRules
) const
1002 if (!maRule
.msForName
.isEmpty())
1004 rRules
.push_back(maRule
);
1008 void AlgAtom::accept( LayoutAtomVisitor
& rVisitor
)
1010 rVisitor
.visit(*this);
1013 sal_Int32
AlgAtom::getConnectorType()
1015 sal_Int32 nConnRout
= 0;
1016 sal_Int32 nBegSty
= 0;
1017 sal_Int32 nEndSty
= 0;
1018 if (maMap
.count(oox::XML_connRout
))
1019 nConnRout
= maMap
.find(oox::XML_connRout
)->second
;
1020 if (maMap
.count(oox::XML_begSty
))
1021 nBegSty
= maMap
.find(oox::XML_begSty
)->second
;
1022 if (maMap
.count(oox::XML_endSty
))
1023 nEndSty
= maMap
.find(oox::XML_endSty
)->second
;
1025 if (nConnRout
== oox::XML_bend
)
1026 return 0; // was oox::XML_bentConnector3 - connectors are hidden in org chart as they don't work anyway
1027 if (nBegSty
== oox::XML_arr
&& nEndSty
== oox::XML_arr
)
1028 return oox::XML_leftRightArrow
;
1029 if (nBegSty
== oox::XML_arr
)
1030 return oox::XML_leftArrow
;
1031 if (nEndSty
== oox::XML_arr
)
1032 return oox::XML_rightArrow
;
1034 return oox::XML_rightArrow
;
1037 sal_Int32
AlgAtom::getVerticalShapesCount(const ShapePtr
& rShape
)
1039 if (rShape
->getChildren().empty())
1040 return (rShape
->getSubType() != XML_conn
) ? 1 : 0;
1042 sal_Int32 nDir
= XML_fromL
;
1043 if (mnType
== XML_hierRoot
)
1045 else if (maMap
.count(XML_linDir
))
1046 nDir
= maMap
.find(XML_linDir
)->second
;
1048 const sal_Int32 nSecDir
= maMap
.count(XML_secLinDir
) ? maMap
.find(XML_secLinDir
)->second
: 0;
1050 sal_Int32 nCount
= 0;
1051 if (nDir
== XML_fromT
|| nDir
== XML_fromB
)
1053 for (const ShapePtr
& pChild
: rShape
->getChildren())
1054 nCount
+= pChild
->getVerticalShapesCount();
1056 else if ((nDir
== XML_fromL
|| nDir
== XML_fromR
) && nSecDir
== XML_fromT
)
1058 for (const ShapePtr
& pChild
: rShape
->getChildren())
1059 nCount
+= pChild
->getVerticalShapesCount();
1060 nCount
= (nCount
+ 1) / 2;
1064 for (const ShapePtr
& pChild
: rShape
->getChildren())
1065 nCount
= std::max(nCount
, pChild
->getVerticalShapesCount());
1073 /// Does the first data node of this shape have customized text properties?
1074 bool HasCustomText(const ShapePtr
& rShape
, LayoutNode
& rLayoutNode
)
1076 const PresPointShapeMap
& rPresPointShapeMap
1077 = rLayoutNode
.getDiagram().getLayout()->getPresPointShapeMap();
1078 const DiagramData::StringMap
& rPresOfNameMap
1079 = rLayoutNode
.getDiagram().getData()->getPresOfNameMap();
1080 const DiagramData::PointNameMap
& rPointNameMap
1081 = rLayoutNode
.getDiagram().getData()->getPointNameMap();
1082 // Get the first presentation node of the shape.
1083 const svx::diagram::Point
* pPresNode
= nullptr;
1084 for (const auto& rPair
: rPresPointShapeMap
)
1086 if (rPair
.second
== rShape
)
1088 pPresNode
= rPair
.first
;
1092 // Get the first data node of the presentation node.
1093 svx::diagram::Point
* pDataNode
= nullptr;
1096 auto itPresToData
= rPresOfNameMap
.find(pPresNode
->msModelId
);
1097 if (itPresToData
!= rPresOfNameMap
.end())
1099 for (const auto& rPair
: itPresToData
->second
)
1101 const DiagramData::SourceIdAndDepth
& rItem
= rPair
.second
;
1102 auto it
= rPointNameMap
.find(rItem
.msSourceId
);
1103 if (it
!= rPointNameMap
.end())
1105 pDataNode
= it
->second
;
1112 // If we have a data node, see if its text is customized or not.
1115 return pDataNode
->mbCustomText
;
1122 void AlgAtom::layoutShape(const ShapePtr
& rShape
, const std::vector
<Constraint
>& rConstraints
,
1123 const std::vector
<Rule
>& rRules
)
1125 if (mnType
!= XML_lin
)
1127 // TODO Handle spacing from constraints for non-lin algorithms as well.
1128 rShape
->getChildren().erase(
1129 std::remove_if(rShape
->getChildren().begin(), rShape
->getChildren().end(),
1130 [](const ShapePtr
& aChild
) {
1131 return aChild
->getServiceName() == "com.sun.star.drawing.GroupShape"
1132 && aChild
->getChildren().empty();
1134 rShape
->getChildren().end());
1141 CompositeAlg::layoutShapeChildren(*this, rShape
, rConstraints
);
1147 if (rShape
->getSubType() == XML_conn
)
1149 // There is no shape type "conn", replace it by an arrow based
1150 // on the direction of the parent linear layout.
1151 sal_Int32 nType
= getConnectorType();
1153 rShape
->setSubType(nType
);
1154 rShape
->getCustomShapeProperties()->setShapePresetType(nType
);
1157 // Parse constraints to adjust the size.
1158 std::vector
<Constraint
> aDirectConstraints
;
1159 const LayoutNode
& rLayoutNode
= getLayoutNode();
1160 for (const auto& pChild
: rLayoutNode
.getChildren())
1162 auto pConstraintAtom
= dynamic_cast<ConstraintAtom
*>(pChild
.get());
1163 if (pConstraintAtom
)
1164 pConstraintAtom
->parseConstraint(aDirectConstraints
, /*bRequireForName=*/false);
1167 LayoutPropertyMap aProperties
;
1168 LayoutProperty
& rParent
= aProperties
[""];
1169 rParent
[XML_w
] = rShape
->getSize().Width
;
1170 rParent
[XML_h
] = rShape
->getSize().Height
;
1173 rParent
[XML_r
] = rShape
->getSize().Width
;
1174 rParent
[XML_b
] = rShape
->getSize().Height
;
1175 for (const auto& rConstr
: aDirectConstraints
)
1177 const LayoutPropertyMap::const_iterator aRef
1178 = aProperties
.find(rConstr
.msRefForName
);
1179 if (aRef
!= aProperties
.end())
1181 const LayoutProperty::const_iterator aRefType
1182 = aRef
->second
.find(rConstr
.mnRefType
);
1183 if (aRefType
!= aRef
->second
.end())
1184 aProperties
[rConstr
.msForName
][rConstr
.mnType
]
1185 = aRefType
->second
* rConstr
.mfFactor
;
1189 aSize
.Width
= rParent
[XML_w
];
1190 aSize
.Height
= rParent
[XML_h
];
1191 // keep center position
1192 awt::Point aPos
= rShape
->getPosition();
1193 aPos
.X
+= (rShape
->getSize().Width
- aSize
.Width
) / 2;
1194 aPos
.Y
+= (rShape
->getSize().Height
- aSize
.Height
) / 2;
1195 rShape
->setPosition(aPos
);
1196 rShape
->setSize(aSize
);
1202 if (rShape
->getChildren().empty())
1205 const sal_Int32 nStartAngle
= maMap
.count(XML_stAng
) ? maMap
.find(XML_stAng
)->second
: 0;
1206 const sal_Int32 nSpanAngle
= maMap
.count(XML_spanAng
) ? maMap
.find(XML_spanAng
)->second
: 360;
1207 const sal_Int32 nRotationPath
= maMap
.count(XML_rotPath
) ? maMap
.find(XML_rotPath
)->second
: XML_none
;
1208 const sal_Int32 nctrShpMap
= maMap
.count(XML_ctrShpMap
) ? maMap
.find(XML_ctrShpMap
)->second
: XML_none
;
1209 const awt::Size
aCenter(rShape
->getSize().Width
/ 2, rShape
->getSize().Height
/ 2);
1210 const awt::Size
aChildSize(rShape
->getSize().Width
/ 4, rShape
->getSize().Height
/ 4);
1211 const awt::Size
aConnectorSize(rShape
->getSize().Width
/ 12, rShape
->getSize().Height
/ 12);
1212 const sal_Int32 nRadius
= std::min(
1213 (rShape
->getSize().Width
- aChildSize
.Width
) / 2,
1214 (rShape
->getSize().Height
- aChildSize
.Height
) / 2);
1216 std::vector
<oox::drawingml::ShapePtr
> aCycleChildren
= rShape
->getChildren();
1218 if (nctrShpMap
== XML_fNode
)
1220 // first node placed in center, others around
1221 oox::drawingml::ShapePtr pCenterShape
= aCycleChildren
.front();
1222 aCycleChildren
.erase(aCycleChildren
.begin());
1223 const awt::Point
aCurrPos(aCenter
.Width
- aChildSize
.Width
/ 2,
1224 aCenter
.Height
- aChildSize
.Height
/ 2);
1225 pCenterShape
->setPosition(aCurrPos
);
1226 pCenterShape
->setSize(aChildSize
);
1227 pCenterShape
->setChildSize(aChildSize
);
1230 const sal_Int32 nShapes
= aCycleChildren
.size();
1233 const sal_Int32 nConnectorRadius
= nRadius
* cos(basegfx::deg2rad(nSpanAngle
/ nShapes
));
1234 const sal_Int32 nConnectorAngle
= nSpanAngle
> 0 ? 0 : 180;
1237 for (auto & aCurrShape
: aCycleChildren
)
1239 const double fAngle
= static_cast<double>(idx
)*nSpanAngle
/nShapes
+ nStartAngle
;
1240 awt::Size aCurrSize
= aChildSize
;
1241 sal_Int32 nCurrRadius
= nRadius
;
1242 if (aCurrShape
->getSubType() == XML_conn
)
1244 aCurrSize
= aConnectorSize
;
1245 nCurrRadius
= nConnectorRadius
;
1247 const awt::Point
aCurrPos(
1248 aCenter
.Width
+ nCurrRadius
*sin(basegfx::deg2rad(fAngle
)) - aCurrSize
.Width
/2,
1249 aCenter
.Height
- nCurrRadius
*cos(basegfx::deg2rad(fAngle
)) - aCurrSize
.Height
/2);
1251 aCurrShape
->setPosition(aCurrPos
);
1252 aCurrShape
->setSize(aCurrSize
);
1253 aCurrShape
->setChildSize(aCurrSize
);
1255 if (nRotationPath
== XML_alongPath
)
1256 aCurrShape
->setRotation(fAngle
* PER_DEGREE
);
1258 // connectors should be handled in conn, but we don't have
1259 // reference to previous and next child, so it's easier here
1260 if (aCurrShape
->getSubType() == XML_conn
)
1261 aCurrShape
->setRotation((nConnectorAngle
+ fAngle
) * PER_DEGREE
);
1272 if (rShape
->getChildren().empty() || rShape
->getSize().Width
== 0 || rShape
->getSize().Height
== 0)
1275 // hierRoot is the manager -> employees vertical linear path,
1276 // hierChild is the first employee -> last employee horizontal
1278 sal_Int32 nDir
= XML_fromL
;
1279 if (mnType
== XML_hierRoot
)
1281 else if (maMap
.count(XML_linDir
))
1282 nDir
= maMap
.find(XML_linDir
)->second
;
1284 const sal_Int32 nSecDir
= maMap
.count(XML_secLinDir
) ? maMap
.find(XML_secLinDir
)->second
: 0;
1286 sal_Int32 nCount
= rShape
->getChildren().size();
1288 if (mnType
== XML_hierChild
)
1290 // Connectors should not influence the size of non-connect shapes.
1291 nCount
= std::count_if(
1292 rShape
->getChildren().begin(), rShape
->getChildren().end(),
1293 [](const ShapePtr
& pShape
) { return pShape
->getSubType() != XML_conn
; });
1296 const double fSpaceWidth
= 0.1;
1297 const double fSpaceHeight
= 0.3;
1299 if (mnType
== XML_hierRoot
&& nCount
== 3)
1301 // Order assistant nodes above employee nodes.
1302 std::vector
<ShapePtr
>& rChildren
= rShape
->getChildren();
1303 if (!containsDataNodeType(rChildren
[1], XML_asst
)
1304 && containsDataNodeType(rChildren
[2], XML_asst
))
1305 std::swap(rChildren
[1], rChildren
[2]);
1308 sal_Int32 nHorizontalShapesCount
= 1;
1309 if (nSecDir
== XML_fromT
)
1310 nHorizontalShapesCount
= 2;
1311 else if (nDir
== XML_fromL
|| nDir
== XML_fromR
)
1312 nHorizontalShapesCount
= nCount
;
1314 awt::Size aChildSize
= rShape
->getSize();
1315 aChildSize
.Height
/= (rShape
->getVerticalShapesCount() + (rShape
->getVerticalShapesCount() - 1) * fSpaceHeight
);
1316 aChildSize
.Width
/= (nHorizontalShapesCount
+ (nHorizontalShapesCount
- 1) * fSpaceWidth
);
1318 awt::Size aConnectorSize
= aChildSize
;
1319 aConnectorSize
.Width
= 1;
1321 awt::Point
aChildPos(0, 0);
1323 // indent children to show they are descendants, not siblings
1324 if (mnType
== XML_hierChild
&& nHorizontalShapesCount
== 1)
1326 const double fChildIndent
= 0.1;
1327 aChildPos
.X
= aChildSize
.Width
* fChildIndent
;
1328 aChildSize
.Width
*= (1 - 2 * fChildIndent
);
1332 sal_Int32 nRowHeight
= 0;
1333 for (auto& pChild
: rShape
->getChildren())
1335 pChild
->setPosition(aChildPos
);
1337 if (mnType
== XML_hierChild
&& pChild
->getSubType() == XML_conn
)
1339 // Connectors should not influence the position of
1340 // non-connect shapes.
1341 pChild
->setSize(aConnectorSize
);
1342 pChild
->setChildSize(aConnectorSize
);
1346 awt::Size aCurrSize
= aChildSize
;
1347 aCurrSize
.Height
*= pChild
->getVerticalShapesCount() + (pChild
->getVerticalShapesCount() - 1) * fSpaceHeight
;
1349 pChild
->setSize(aCurrSize
);
1350 pChild
->setChildSize(aCurrSize
);
1352 if (nDir
== XML_fromT
|| nDir
== XML_fromB
)
1353 aChildPos
.Y
+= aCurrSize
.Height
+ aChildSize
.Height
* fSpaceHeight
;
1355 aChildPos
.X
+= aCurrSize
.Width
+ aCurrSize
.Width
* fSpaceWidth
;
1357 nRowHeight
= std::max(nRowHeight
, aCurrSize
.Height
);
1359 if (nSecDir
== XML_fromT
&& nIdx
% 2 == 1)
1362 aChildPos
.Y
+= nRowHeight
+ aChildSize
.Height
* fSpaceHeight
;
1374 // spread children evenly across one axis, stretch across second
1376 if (rShape
->getChildren().empty() || rShape
->getSize().Width
== 0 || rShape
->getSize().Height
== 0)
1379 const sal_Int32 nDir
= maMap
.count(XML_linDir
) ? maMap
.find(XML_linDir
)->second
: XML_fromL
;
1380 const sal_Int32 nIncX
= nDir
==XML_fromL
? 1 : (nDir
==XML_fromR
? -1 : 0);
1381 const sal_Int32 nIncY
= nDir
==XML_fromT
? 1 : (nDir
==XML_fromB
? -1 : 0);
1383 double fCount
= rShape
->getChildren().size();
1384 sal_Int32 nConnectorAngle
= 0;
1387 case XML_fromL
: nConnectorAngle
= 0; break;
1388 case XML_fromR
: nConnectorAngle
= 180; break;
1389 case XML_fromT
: nConnectorAngle
= 270; break;
1390 case XML_fromB
: nConnectorAngle
= 90; break;
1393 awt::Size aSpaceSize
;
1395 // Find out which constraint is relevant for which (internal) name.
1396 LayoutPropertyMap aProperties
;
1397 for (const auto& rConstraint
: rConstraints
)
1399 if (rConstraint
.msForName
.isEmpty())
1402 LayoutProperty
& rProperty
= aProperties
[rConstraint
.msForName
];
1403 if (rConstraint
.mnType
== XML_w
)
1405 rProperty
[XML_w
] = rShape
->getSize().Width
* rConstraint
.mfFactor
;
1406 if (rProperty
[XML_w
] > rShape
->getSize().Width
)
1408 rProperty
[XML_w
] = rShape
->getSize().Width
;
1411 if (rConstraint
.mnType
== XML_h
)
1413 rProperty
[XML_h
] = rShape
->getSize().Height
* rConstraint
.mfFactor
;
1414 if (rProperty
[XML_h
] > rShape
->getSize().Height
)
1416 rProperty
[XML_h
] = rShape
->getSize().Height
;
1420 if (rConstraint
.mnType
== XML_primFontSz
&& rConstraint
.mnFor
== XML_des
1421 && rConstraint
.mnOperator
== XML_equ
)
1423 NamedShapePairs
& rDiagramFontHeights
1424 = getLayoutNode().getDiagram().getDiagramFontHeights();
1425 auto it
= rDiagramFontHeights
.find(rConstraint
.msForName
);
1426 if (it
== rDiagramFontHeights
.end())
1428 // Start tracking all shapes with this internal name: they'll have the same
1430 rDiagramFontHeights
[rConstraint
.msForName
] = {};
1434 // TODO: get values from differently named constraints as well
1435 if (rConstraint
.msForName
== "sp" || rConstraint
.msForName
== "space" || rConstraint
.msForName
== "sibTrans")
1437 if (rConstraint
.mnType
== XML_w
)
1438 aSpaceSize
.Width
= rShape
->getSize().Width
* rConstraint
.mfFactor
;
1439 if (rConstraint
.mnType
== XML_h
)
1440 aSpaceSize
.Height
= rShape
->getSize().Height
* rConstraint
.mfFactor
;
1444 // first approximation of children size
1445 std::set
<OUString
> aChildrenToShrink
;
1446 for (const auto& rRule
: rRules
)
1448 // Consider rules: when scaling down, only change children where the rule allows
1450 aChildrenToShrink
.insert(rRule
.msForName
);
1453 if (nDir
== XML_fromT
|| nDir
== XML_fromB
)
1455 // TODO consider rules for vertical linear layout as well.
1456 aChildrenToShrink
.clear();
1459 if (!aChildrenToShrink
.empty())
1461 // Have scaling info from rules: then only count scaled children.
1462 // Also count children which are a fraction of a scaled child.
1463 std::set
<OUString
> aChildrenToShrinkDeps
;
1464 for (auto& aCurrShape
: rShape
->getChildren())
1466 if (aChildrenToShrink
.find(aCurrShape
->getInternalName())
1467 == aChildrenToShrink
.end())
1473 bool bIsDependency
= false;
1475 for (const auto& rConstraint
: rConstraints
)
1477 if (rConstraint
.msForName
!= aCurrShape
->getInternalName())
1482 if ((nDir
== XML_fromL
|| nDir
== XML_fromR
) && rConstraint
.mnType
!= XML_w
)
1486 if ((nDir
== XML_fromL
|| nDir
== XML_fromR
) && rConstraint
.mnType
== XML_w
)
1488 fFactor
= rConstraint
.mfFactor
;
1491 if ((nDir
== XML_fromT
|| nDir
== XML_fromB
) && rConstraint
.mnType
!= XML_h
)
1495 if ((nDir
== XML_fromT
|| nDir
== XML_fromB
) && rConstraint
.mnType
== XML_h
)
1497 fFactor
= rConstraint
.mfFactor
;
1500 if (aChildrenToShrink
.find(rConstraint
.msRefForName
) == aChildrenToShrink
.end())
1505 // At this point we have a child with a size which is a factor of an
1506 // other child which will be scaled.
1507 fCount
+= rConstraint
.mfFactor
;
1508 aChildrenToShrinkDeps
.insert(aCurrShape
->getInternalName());
1509 bIsDependency
= true;
1513 if (!bIsDependency
&& aCurrShape
->getServiceName() == "com.sun.star.drawing.GroupShape")
1515 bool bScaleDownEmptySpacing
= false;
1516 if (nDir
== XML_fromL
|| nDir
== XML_fromR
)
1518 std::optional
<sal_Int32
> oWidth
= findProperty(aProperties
, aCurrShape
->getInternalName(), XML_w
);
1519 bScaleDownEmptySpacing
= oWidth
.has_value() && oWidth
.value() > 0;
1521 if (!bScaleDownEmptySpacing
&& (nDir
== XML_fromT
|| nDir
== XML_fromB
))
1523 std::optional
<sal_Int32
> oHeight
= findProperty(aProperties
, aCurrShape
->getInternalName(), XML_h
);
1524 bScaleDownEmptySpacing
= oHeight
.has_value() && oHeight
.value() > 0;
1526 if (bScaleDownEmptySpacing
&& aCurrShape
->getChildren().empty())
1529 aChildrenToShrinkDeps
.insert(aCurrShape
->getInternalName());
1536 aChildrenToShrink
.insert(aChildrenToShrinkDeps
.begin(), aChildrenToShrinkDeps
.end());
1538 // No manual spacing: spacings are children as well.
1539 aSpaceSize
= awt::Size();
1543 // TODO Handle spacing from constraints without rules as well.
1544 rShape
->getChildren().erase(
1545 std::remove_if(rShape
->getChildren().begin(), rShape
->getChildren().end(),
1546 [](const ShapePtr
& aChild
) {
1547 return aChild
->getServiceName()
1548 == "com.sun.star.drawing.GroupShape"
1549 && aChild
->getChildren().empty();
1551 rShape
->getChildren().end());
1552 fCount
= rShape
->getChildren().size();
1554 awt::Size aChildSize
= rShape
->getSize();
1555 if (nDir
== XML_fromL
|| nDir
== XML_fromR
)
1556 aChildSize
.Width
/= fCount
;
1557 else if (nDir
== XML_fromT
|| nDir
== XML_fromB
)
1558 aChildSize
.Height
/= fCount
;
1560 awt::Point
aCurrPos(0, 0);
1562 aCurrPos
.X
= rShape
->getSize().Width
- aChildSize
.Width
;
1564 aCurrPos
.Y
= rShape
->getSize().Height
- aChildSize
.Height
;
1566 // See if children requested more than 100% space in total: scale
1567 // down in that case.
1568 awt::Size aTotalSize
;
1569 for (const auto & aCurrShape
: rShape
->getChildren())
1571 std::optional
<sal_Int32
> oWidth
= findProperty(aProperties
, aCurrShape
->getInternalName(), XML_w
);
1572 std::optional
<sal_Int32
> oHeight
= findProperty(aProperties
, aCurrShape
->getInternalName(), XML_h
);
1573 awt::Size aSize
= aChildSize
;
1574 if (oWidth
.has_value())
1575 aSize
.Width
= oWidth
.value();
1576 if (oHeight
.has_value())
1577 aSize
.Height
= oHeight
.value();
1578 aTotalSize
.Width
+= aSize
.Width
;
1579 aTotalSize
.Height
+= aSize
.Height
;
1582 aTotalSize
.Width
+= (fCount
-1) * aSpaceSize
.Width
;
1583 aTotalSize
.Height
+= (fCount
-1) * aSpaceSize
.Height
;
1585 double fWidthScale
= 1.0;
1586 double fHeightScale
= 1.0;
1587 if (nIncX
&& aTotalSize
.Width
> rShape
->getSize().Width
)
1588 fWidthScale
= static_cast<double>(rShape
->getSize().Width
) / aTotalSize
.Width
;
1589 if (nIncY
&& aTotalSize
.Height
> rShape
->getSize().Height
)
1590 fHeightScale
= static_cast<double>(rShape
->getSize().Height
) / aTotalSize
.Height
;
1591 aSpaceSize
.Width
*= fWidthScale
;
1592 aSpaceSize
.Height
*= fHeightScale
;
1594 for (auto& aCurrShape
: rShape
->getChildren())
1596 // Extract properties relevant for this shape from constraints.
1597 std::optional
<sal_Int32
> oWidth
= findProperty(aProperties
, aCurrShape
->getInternalName(), XML_w
);
1598 std::optional
<sal_Int32
> oHeight
= findProperty(aProperties
, aCurrShape
->getInternalName(), XML_h
);
1600 awt::Size aSize
= aChildSize
;
1601 if (oWidth
.has_value())
1602 aSize
.Width
= oWidth
.value();
1603 if (oHeight
.has_value())
1604 aSize
.Height
= oHeight
.value();
1605 if (aChildrenToShrink
.empty()
1606 || aChildrenToShrink
.find(aCurrShape
->getInternalName())
1607 != aChildrenToShrink
.end())
1609 aSize
.Width
*= fWidthScale
;
1611 if (aChildrenToShrink
.empty()
1612 || aChildrenToShrink
.find(aCurrShape
->getInternalName())
1613 != aChildrenToShrink
.end())
1615 aSize
.Height
*= fHeightScale
;
1617 aCurrShape
->setSize(aSize
);
1618 aCurrShape
->setChildSize(aSize
);
1620 // center in the other axis - probably some parameter controls it
1622 aCurrPos
.Y
= (rShape
->getSize().Height
- aSize
.Height
) / 2;
1624 aCurrPos
.X
= (rShape
->getSize().Width
- aSize
.Width
) / 2;
1634 aCurrShape
->setPosition(aCurrPos
);
1636 aCurrPos
.X
+= nIncX
* (aSize
.Width
+ aSpaceSize
.Width
);
1637 aCurrPos
.Y
+= nIncY
* (aSize
.Height
+ aSpaceSize
.Height
);
1639 // connectors should be handled in conn, but we don't have
1640 // reference to previous and next child, so it's easier here
1641 if (aCurrShape
->getSubType() == XML_conn
)
1642 aCurrShape
->setRotation(nConnectorAngle
* PER_DEGREE
);
1645 // Newer shapes are behind older ones by default. Reverse this if requested.
1646 sal_Int32 nChildOrder
= XML_b
;
1647 const LayoutNode
* pParentLayoutNode
= nullptr;
1648 for (LayoutAtomPtr pAtom
= getParent(); pAtom
; pAtom
= pAtom
->getParent())
1650 auto pLayoutNode
= dynamic_cast<LayoutNode
*>(pAtom
.get());
1653 pParentLayoutNode
= pLayoutNode
;
1657 if (pParentLayoutNode
)
1659 nChildOrder
= pParentLayoutNode
->getChildOrder();
1661 if (nChildOrder
== XML_t
)
1663 std::reverse(rShape
->getChildren().begin(), rShape
->getChildren().end());
1671 PyraAlg::layoutShapeChildren(rShape
);
1677 SnakeAlg::layoutShapeChildren(*this, rShape
, rConstraints
);
1683 // HACK: Handled one level higher. Or rather, planned to
1684 // HACK: text should appear only in tx node; we're assigning it earlier, so let's remove it here
1685 rShape
->setTextBody(TextBodyPtr());
1691 // adjust text alignment
1693 // Parse constraints, only self margins as a start.
1694 double fFontSize
= 0;
1695 for (const auto& rConstr
: rConstraints
)
1697 if (rConstr
.mnRefType
== XML_w
)
1699 if (!rConstr
.msForName
.isEmpty())
1702 sal_Int32 nProperty
= getPropertyFromConstraint(rConstr
.mnType
);
1706 // PowerPoint takes size as points, but gives margin as MMs.
1707 double fFactor
= convertPointToMms(rConstr
.mfFactor
);
1709 // DrawingML works in EMUs, UNO API works in MM100s.
1710 sal_Int32 nValue
= o3tl::convert(rShape
->getSize().Width
* fFactor
,
1711 o3tl::Length::emu
, o3tl::Length::mm100
);
1713 rShape
->getShapeProperties().setProperty(nProperty
, nValue
);
1715 if (rConstr
.mnType
== XML_primFontSz
)
1716 fFontSize
= rConstr
.mfValue
;
1719 TextBodyPtr pTextBody
= rShape
->getTextBody();
1720 if (!pTextBody
|| pTextBody
->isEmpty())
1723 // adjust text size to fit shape
1726 for (auto& aParagraph
: pTextBody
->getParagraphs())
1727 for (auto& aRun
: aParagraph
->getRuns())
1728 if (!aRun
->getTextCharacterProperties().moHeight
.has_value())
1729 aRun
->getTextCharacterProperties().moHeight
= fFontSize
* 100;
1732 if (!HasCustomText(rShape
, getLayoutNode()))
1734 // No customized text properties: enable autofit.
1735 pTextBody
->getTextProperties().maPropertyMap
.setProperty(
1736 PROP_TextFitToSize
, drawing::TextFitToSizeType_AUTOFIT
);
1739 // ECMA-376-1:2016 21.4.7.5 ST_AutoTextRotation (Auto Text Rotation)
1740 const sal_Int32 nautoTxRot
= maMap
.count(XML_autoTxRot
) ? maMap
.find(XML_autoTxRot
)->second
: XML_upr
;
1741 sal_Int32 nShapeRot
= rShape
->getRotation();
1742 while (nShapeRot
< 0)
1743 nShapeRot
+= 360 * PER_DEGREE
;
1744 while (nShapeRot
> 360 * PER_DEGREE
)
1745 nShapeRot
-= 360 * PER_DEGREE
;
1752 if (nShapeRot
>= 315 * PER_DEGREE
)
1754 else if (nShapeRot
> 225 * PER_DEGREE
)
1756 else if (nShapeRot
>= 135 * PER_DEGREE
)
1758 else if (nShapeRot
> 45 * PER_DEGREE
)
1760 pTextBody
->getTextProperties().moTextPreRotation
= n90x
* 90 * PER_DEGREE
;
1765 if (nShapeRot
> (90 * PER_DEGREE
) && nShapeRot
< (270 * PER_DEGREE
))
1766 pTextBody
->getTextProperties().moTextPreRotation
= -180 * PER_DEGREE
;
1773 const sal_Int32 atxAnchorVert
= maMap
.count(XML_txAnchorVert
) ? maMap
.find(XML_txAnchorVert
)->second
: XML_mid
;
1775 switch(atxAnchorVert
)
1778 pTextBody
->getTextProperties().meVA
= css::drawing::TextVerticalAdjust_TOP
;
1781 pTextBody
->getTextProperties().meVA
= css::drawing::TextVerticalAdjust_BOTTOM
;
1784 // text centered vertically by default
1786 pTextBody
->getTextProperties().meVA
= css::drawing::TextVerticalAdjust_CENTER
;
1790 pTextBody
->getTextProperties().maPropertyMap
.setProperty(PROP_TextVerticalAdjust
, pTextBody
->getTextProperties().meVA
);
1792 // normalize list level
1793 sal_Int32 nBaseLevel
= pTextBody
->getParagraphs().front()->getProperties().getLevel();
1794 for (auto & aParagraph
: pTextBody
->getParagraphs())
1796 if (aParagraph
->getProperties().getLevel() < nBaseLevel
)
1797 nBaseLevel
= aParagraph
->getProperties().getLevel();
1800 // Start bullets at:
1802 // 2 - with children (default)
1803 int nStartBulletsAtLevel
= 2;
1804 ParamMap::const_iterator aBulletLvl
= maMap
.find(XML_stBulletLvl
);
1805 if (aBulletLvl
!= maMap
.end())
1806 nStartBulletsAtLevel
= aBulletLvl
->second
;
1807 nStartBulletsAtLevel
--;
1809 bool isBulletList
= false;
1810 for (auto & aParagraph
: pTextBody
->getParagraphs())
1812 sal_Int32 nLevel
= aParagraph
->getProperties().getLevel() - nBaseLevel
;
1813 aParagraph
->getProperties().setLevel(nLevel
);
1814 if (nLevel
>= nStartBulletsAtLevel
)
1816 if (!aParagraph
->getProperties().getParaLeftMargin().has_value())
1818 sal_Int32 nLeftMargin
1819 = o3tl::convert(285750 * (nLevel
- nStartBulletsAtLevel
+ 1),
1820 o3tl::Length::emu
, o3tl::Length::mm100
);
1821 aParagraph
->getProperties().getParaLeftMargin() = nLeftMargin
;
1824 if (!aParagraph
->getProperties().getFirstLineIndentation().has_value())
1825 aParagraph
->getProperties().getFirstLineIndentation()
1826 = o3tl::convert(-285750, o3tl::Length::emu
, o3tl::Length::mm100
);
1828 // It is not possible to change the bullet style for text.
1829 aParagraph
->getProperties().getBulletList().setBulletChar(u
"•");
1830 aParagraph
->getProperties().getBulletList().setSuffixNone();
1831 isBulletList
= true;
1835 // explicit alignment
1836 ParamMap::const_iterator aDir
= maMap
.find(XML_parTxLTRAlign
);
1837 // TODO: XML_parTxRTLAlign
1838 if (aDir
!= maMap
.end())
1840 css::style::ParagraphAdjust aAlignment
= GetParaAdjust(aDir
->second
);
1841 for (auto & aParagraph
: pTextBody
->getParagraphs())
1842 aParagraph
->getProperties().setParaAdjust(aAlignment
);
1844 else if (!isBulletList
)
1846 // if not list use default alignment - centered
1847 for (auto & aParagraph
: pTextBody
->getParagraphs())
1848 aParagraph
->getProperties().setParaAdjust(css::style::ParagraphAdjust::ParagraphAdjust_CENTER
);
1859 "Layouting shape " << rShape
->getInternalName() << ", alg type: " << mnType
<< ", ("
1860 << rShape
->getPosition().X
<< "," << rShape
->getPosition().Y
<< ","
1861 << rShape
->getSize().Width
<< "," << rShape
->getSize().Height
<< ")");
1864 void LayoutNode::accept( LayoutAtomVisitor
& rVisitor
)
1866 rVisitor
.visit(*this);
1869 bool LayoutNode::setupShape( const ShapePtr
& rShape
, const svx::diagram::Point
* pPresNode
, sal_Int32 nCurrIdx
) const
1873 "Filling content from layout node named \"" << msName
1874 << "\", modelId \"" << pPresNode
->msModelId
<< "\"");
1876 // have the presentation node - now, need the actual data node:
1877 const DiagramData::StringMap::const_iterator aNodeName
= mrDgm
.getData()->getPresOfNameMap().find(
1878 pPresNode
->msModelId
);
1879 if( aNodeName
!= mrDgm
.getData()->getPresOfNameMap().end() )
1881 // Calculate the depth of what is effectively the topmost element.
1882 sal_Int32 nMinDepth
= std::numeric_limits
<sal_Int32
>::max();
1883 for (const auto& rPair
: aNodeName
->second
)
1885 if (rPair
.second
.mnDepth
< nMinDepth
)
1886 nMinDepth
= rPair
.second
.mnDepth
;
1889 for (const auto& rPair
: aNodeName
->second
)
1891 const DiagramData::SourceIdAndDepth
& rItem
= rPair
.second
;
1892 DiagramData::PointNameMap
& rMap
= mrDgm
.getData()->getPointNameMap();
1893 // pPresNode is the presentation node of the aDataNode2 data node.
1894 DiagramData::PointNameMap::const_iterator aDataNode2
= rMap
.find(rItem
.msSourceId
);
1895 if (aDataNode2
== rMap
.end())
1901 Shape
* pDataNode2Shape(mrDgm
.getData()->getOrCreateAssociatedShape(*aDataNode2
->second
));
1902 if (nullptr == pDataNode2Shape
)
1908 rShape
->setDataNodeType(aDataNode2
->second
->mnXMLType
);
1910 if (rItem
.mnDepth
== 0)
1912 // grab shape attr from topmost element(s)
1913 rShape
->getShapeProperties() = pDataNode2Shape
->getShapeProperties();
1914 rShape
->getLineProperties() = pDataNode2Shape
->getLineProperties();
1915 rShape
->getFillProperties() = pDataNode2Shape
->getFillProperties();
1916 rShape
->getCustomShapeProperties() = pDataNode2Shape
->getCustomShapeProperties();
1917 rShape
->setMasterTextListStyle( pDataNode2Shape
->getMasterTextListStyle() );
1921 "Custom shape with preset type "
1922 << (rShape
->getCustomShapeProperties()
1923 ->getShapePresetType())
1924 << " added for layout node named \"" << msName
1927 else if (rItem
.mnDepth
== nMinDepth
)
1929 // If no real topmost element, then take properties from the one that's the closest
1931 rShape
->getLineProperties() = pDataNode2Shape
->getLineProperties();
1932 rShape
->getFillProperties() = pDataNode2Shape
->getFillProperties();
1935 // append text with right outline level
1936 if( pDataNode2Shape
->getTextBody() &&
1937 !pDataNode2Shape
->getTextBody()->getParagraphs().empty() &&
1938 !pDataNode2Shape
->getTextBody()->getParagraphs().front()->getRuns().empty() )
1940 TextBodyPtr pTextBody
=rShape
->getTextBody();
1943 pTextBody
= std::make_shared
<TextBody
>();
1945 // also copy text attrs
1946 pTextBody
->getTextListStyle() =
1947 pDataNode2Shape
->getTextBody()->getTextListStyle();
1948 pTextBody
->getTextProperties() =
1949 pDataNode2Shape
->getTextBody()->getTextProperties();
1951 rShape
->setTextBody(pTextBody
);
1954 const TextParagraphVector
& rSourceParagraphs
1955 = pDataNode2Shape
->getTextBody()->getParagraphs();
1956 for (const auto& pSourceParagraph
: rSourceParagraphs
)
1958 TextParagraph
& rPara
= pTextBody
->addParagraph();
1959 if (rItem
.mnDepth
!= -1)
1960 rPara
.getProperties().setLevel(rItem
.mnDepth
);
1962 for (const auto& pRun
: pSourceParagraph
->getRuns())
1964 const TextBodyPtr
& rBody
= pDataNode2Shape
->getTextBody();
1965 rPara
.getProperties().apply(rBody
->getParagraphs().front()->getProperties());
1974 "ShapeCreationVisitor::visit: no data node name found while"
1975 " processing shape type "
1976 << rShape
->getCustomShapeProperties()->getShapePresetType()
1977 << " for layout node named \"" << msName
<< "\"");
1978 Shape
* pPresNodeShape(mrDgm
.getData()->getOrCreateAssociatedShape(*pPresNode
));
1979 if (nullptr != pPresNodeShape
)
1980 rShape
->getFillProperties().assignUsed(pPresNodeShape
->getFillProperties());
1983 // TODO(Q1): apply styling & coloring - take presentation
1984 // point's presStyleLbl for both style & color
1985 // if not found use layout node's styleLbl
1986 // however, docs are a bit unclear on this
1987 OUString aStyleLabel
= pPresNode
->msPresentationLayoutStyleLabel
;
1988 if (aStyleLabel
.isEmpty())
1989 aStyleLabel
= msStyleLabel
;
1990 if( !aStyleLabel
.isEmpty() )
1992 const DiagramQStyleMap::const_iterator aStyle
= mrDgm
.getStyles().find(aStyleLabel
);
1993 if( aStyle
!= mrDgm
.getStyles().end() )
1995 const DiagramStyle
& rStyle
= aStyle
->second
;
1996 rShape
->getShapeStyleRefs()[XML_fillRef
] = rStyle
.maFillStyle
;
1997 rShape
->getShapeStyleRefs()[XML_lnRef
] = rStyle
.maLineStyle
;
1998 rShape
->getShapeStyleRefs()[XML_effectRef
] = rStyle
.maEffectStyle
;
1999 rShape
->getShapeStyleRefs()[XML_fontRef
] = rStyle
.maTextStyle
;
2003 SAL_WARN("oox.drawingml", "Style " << aStyleLabel
<< " not found");
2006 const DiagramColorMap::const_iterator aColor
= mrDgm
.getColors().find(aStyleLabel
);
2007 if( aColor
!= mrDgm
.getColors().end() )
2009 // Take the nth color from the color list in case we are the nth shape in a
2010 // <dgm:forEach> loop.
2011 const DiagramColor
& rColor
=aColor
->second
;
2012 if( !rColor
.maFillColors
.empty() )
2013 rShape
->getShapeStyleRefs()[XML_fillRef
].maPhClr
= DiagramColor::getColorByIndex(rColor
.maFillColors
, nCurrIdx
);
2014 if( !rColor
.maLineColors
.empty() )
2015 rShape
->getShapeStyleRefs()[XML_lnRef
].maPhClr
= DiagramColor::getColorByIndex(rColor
.maLineColors
, nCurrIdx
);
2016 if( !rColor
.maEffectColors
.empty() )
2017 rShape
->getShapeStyleRefs()[XML_effectRef
].maPhClr
= DiagramColor::getColorByIndex(rColor
.maEffectColors
, nCurrIdx
);
2018 if( !rColor
.maTextFillColors
.empty() )
2019 rShape
->getShapeStyleRefs()[XML_fontRef
].maPhClr
= DiagramColor::getColorByIndex(rColor
.maTextFillColors
, nCurrIdx
);
2023 // even if no data node found, successful anyway. it's
2024 // contained at the layoutnode
2028 const LayoutNode
* LayoutNode::getParentLayoutNode() const
2030 for (LayoutAtomPtr pAtom
= getParent(); pAtom
; pAtom
= pAtom
->getParent())
2032 auto pLayoutNode
= dynamic_cast<LayoutNode
*>(pAtom
.get());
2040 void ShapeAtom::accept( LayoutAtomVisitor
& rVisitor
)
2042 rVisitor
.visit(*this);
2047 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */