LibreOffice Module oox (master) 1
diagramlayoutatoms.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
21
22#include <set>
23
25
27#include <sal/log.hxx>
28
31#include <oox/token/properties.hxx>
36#include <drawingml/textrun.hxx>
38#include <com/sun/star/drawing/TextFitToSizeType.hpp>
39
40using namespace ::com::sun::star;
41using namespace ::com::sun::star::uno;
42using namespace ::com::sun::star::xml::sax;
43using namespace ::oox::core;
44
45namespace
46{
48std::optional<sal_Int32> findProperty(const oox::drawingml::LayoutPropertyMap& rProperties,
49 const OUString& rInternalName, sal_Int32 nProperty)
50{
51 std::optional<sal_Int32> oRet;
52
53 auto it = rProperties.find(rInternalName);
54 if (it != rProperties.end())
55 {
56 const oox::drawingml::LayoutProperty& rProperty = it->second;
57 auto itProperty = rProperty.find(nProperty);
58 if (itProperty != rProperty.end())
59 oRet = itProperty->second;
60 }
61
62 return oRet;
63}
64
69bool isFontUnit(sal_Int32 nUnit)
70{
71 return nUnit == oox::XML_primFontSz || nUnit == oox::XML_secFontSz;
72}
73
75sal_Int32 getPropertyFromConstraint(sal_Int32 nConstraint)
76{
77 switch (nConstraint)
78 {
79 case oox::XML_lMarg:
80 return oox::PROP_TextLeftDistance;
81 case oox::XML_rMarg:
82 return oox::PROP_TextRightDistance;
83 case oox::XML_tMarg:
84 return oox::PROP_TextUpperDistance;
85 case oox::XML_bMarg:
86 return oox::PROP_TextLowerDistance;
87 }
88
89 return 0;
90}
91
96bool containsDataNodeType(const oox::drawingml::ShapePtr& pShape, sal_Int32 nType)
97{
98 if (pShape->getDataNodeType() == nType)
99 return true;
100
101 for (const auto& pChild : pShape->getChildren())
102 {
103 if (containsDataNodeType(pChild, nType))
104 return true;
105 }
106
107 return false;
108}
109}
110
111namespace oox::drawingml {
112void SnakeAlg::layoutShapeChildren(const AlgAtom& rAlg, const ShapePtr& rShape,
113 const std::vector<Constraint>& rConstraints)
114{
115 if (rShape->getChildren().empty() || rShape->getSize().Width == 0
116 || rShape->getSize().Height == 0)
117 return;
118
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))
126 {
127 fShapeWidth = fShapeHeight * fChildAspectRatio;
128 }
129
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)
137 {
138 if (rConstr.mnRefType == XML_w || rConstr.mnRefType == XML_h)
139 {
140 if (rConstr.mnType == XML_sp && rConstr.msForName.isEmpty())
141 fSpaceFromConstraint = rConstr.mfFactor;
142 }
143
144 auto itRefForName = aPropertiesByName.find(rConstr.msRefForName);
145 if (itRefForName == aPropertiesByName.end())
146 {
147 continue;
148 }
149
150 auto it = itRefForName->second.find(rConstr.mnRefType);
151 if (it == itRefForName->second.end())
152 {
153 continue;
154 }
155
156 if (rConstr.mfValue != 0.0)
157 {
158 continue;
159 }
160
161 sal_Int32 nValue = it->second * rConstr.mfFactor;
162
163 if (rConstr.mnPointType == XML_none)
164 {
165 aPropertiesByName[rConstr.msForName][rConstr.mnType] = nValue;
166 }
167 else
168 {
169 aPropertiesByType[rConstr.mnPointType][rConstr.mnType] = nValue;
170 }
171 }
172
173 std::vector<sal_Int32> aShapeWidths(rShape->getChildren().size());
174 for (size_t i = 0; i < rShape->getChildren().size(); ++i)
175 {
176 ShapePtr pChild = rShape->getChildren()[i];
177 if (!pChild->getDataNodeType())
178 {
179 // TODO handle the case when the requirement applies by name, not by point type.
180 aShapeWidths[i] = fShapeWidth;
181 continue;
182 }
183
184 auto itNodeType = aPropertiesByType.find(pChild->getDataNodeType());
185 if (itNodeType == aPropertiesByType.end())
186 {
187 aShapeWidths[i] = fShapeWidth;
188 continue;
189 }
190
191 auto it = itNodeType->second.find(XML_w);
192 if (it == itNodeType->second.end())
193 {
194 aShapeWidths[i] = fShapeWidth;
195 continue;
196 }
197
198 aShapeWidths[i] = it->second;
199 }
200
201 bool bSpaceFromConstraints = fSpaceFromConstraint != 1.0;
202
203 const AlgAtom::ParamMap& rMap = rAlg.getMap();
204 const sal_Int32 nDir = rMap.count(XML_grDir) ? rMap.find(XML_grDir)->second : XML_tL;
205 sal_Int32 nIncX = 1;
206 sal_Int32 nIncY = 1;
207 bool bHorizontal = true;
208 switch (nDir)
209 {
210 case XML_tL:
211 nIncX = 1;
212 nIncY = 1;
213 break;
214 case XML_tR:
215 nIncX = -1;
216 nIncY = 1;
217 break;
218 case XML_bL:
219 nIncX = 1;
220 nIncY = -1;
221 bHorizontal = false;
222 break;
223 case XML_bR:
224 nIncX = -1;
225 nIncY = -1;
226 bHorizontal = false;
227 break;
228 }
229
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
234
235 sal_Int32 nCol = 1;
236 sal_Int32 nRow = 1;
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.
241 nRow = nCount;
242 else
243 {
244 for (; nRow < nCount; nRow++)
245 {
246 nCol = std::ceil(static_cast<double>(nCount) / nRow);
247 sal_Int32 nRowWidth = 0;
248 for (sal_Int32 i = 0; i < nCol; ++i)
249 {
250 if (i >= nCount)
251 {
252 break;
253 }
254
255 nRowWidth += aShapeWidths[i];
256 }
257 double fTotalShapesHeight = fShapeHeight * nRow;
258 if (nRowWidth && fTotalShapesHeight / nRowWidth >= fAspectRatio)
259 {
260 if (nRowWidth > nMaxRowWidth)
261 {
262 nMaxRowWidth = nRowWidth;
263 }
264 break;
265 }
266 }
267 }
268
269 SAL_INFO("oox.drawingml", "Snake layout grid: " << nCol << "x" << nRow);
270
271 sal_Int32 nWidth = rShape->getSize().Width / (nCol + (nCol - 1) * fSpace);
272 awt::Size aChildSize(nWidth, nWidth * fAspectRatio);
273 if (nCol == 1 && nRow > 1)
274 {
275 // We have a single column, so count the height based on the parent height, not
276 // based on width.
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)
281 nNumSpaces += 4;
282 sal_Int32 nHeight = rShape->getSize().Height / (nRow + (nRow + nNumSpaces) * fSpace);
283
284 if (fChildAspectRatio > 1)
285 {
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);
290 }
291
292 bHorizontal = false;
293 }
294
295 awt::Point aCurrPos(0, 0);
296 if (nIncX == -1)
297 aCurrPos.X = rShape->getSize().Width - aChildSize.Width;
298 if (nIncY == -1)
299 aCurrPos.Y = rShape->getSize().Height - aChildSize.Height;
300 else if (bSpaceFromConstraints)
301 {
302 if (!bHorizontal)
303 {
304 // Initial vertical offset to have upper spacing (outside, so double amount).
305 aCurrPos.Y = aChildSize.Height * fSpace * 2;
306 }
307 }
308
309 sal_Int32 nStartX = aCurrPos.X;
310 sal_Int32 nColIdx = 0, index = 0;
311
312 const sal_Int32 aContDir
313 = rMap.count(XML_contDir) ? rMap.find(XML_contDir)->second : XML_sameDir;
314
315 switch (aContDir)
316 {
317 case XML_sameDir:
318 {
319 sal_Int32 nRowHeight = 0;
320 for (auto& aCurrShape : rShape->getChildren())
321 {
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)
329 {
330 double fWidthFactor = static_cast<double>(aShapeWidths[index]) / nMaxRowWidth;
331 // We can only work from constraints if spacing is represented by a real
332 // child shape.
333 aCurrSize.Width = rShape->getSize().Width * fWidthFactor;
334 }
335 if (fChildAspectRatio)
336 {
337 aCurrSize.Height = aCurrSize.Width / fChildAspectRatio;
338
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));
342 }
343 if (aCurrSize.Height > nRowHeight)
344 {
345 nRowHeight = aCurrSize.Height;
346 }
347 aCurrShape->setSize(aCurrSize);
348 aCurrShape->setChildSize(aCurrSize);
349
350 index++; // counts index of child, helpful for positioning.
351
352 if (index % nCol == 0 || ((index / nCol) + 1) != nRow)
353 aCurrPos.X += nIncX * (aCurrSize.Width + fSpace * aCurrSize.Width);
354
355 if (++nColIdx == nCol) // condition for next row
356 {
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)
360 {
361 // position first child of last row
362 if (bWidthsFromConstraints)
363 {
364 aCurrPos.X = nStartX;
365 }
366 else
367 {
368 // Can assume that all child shape has the same width.
369 aCurrPos.X
370 = nStartX
371 + (nIncX * (aCurrSize.Width + fSpace * aCurrSize.Width)) / 2;
372 }
373 }
374 else
375 // if not last row, positions first child of that row
376 aCurrPos.X = nStartX;
377 aCurrPos.Y += nIncY * (nRowHeight + fSpace * nRowHeight);
378 nColIdx = 0;
379 nRowHeight = 0;
380 }
381
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));
385 }
386 break;
387 }
388 case XML_revDir:
389 for (auto& aCurrShape : rShape->getChildren())
390 {
391 aCurrShape->setPosition(aCurrPos);
392 aCurrShape->setSize(aChildSize);
393 aCurrShape->setChildSize(aChildSize);
394
395 index++; // counts index of child, helpful for positioning.
396
397 /*
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.
402 */
403
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);
410
411 if (++nColIdx == nCol) // condition for next row
412 {
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)
422 aCurrPos.X = nStartX
423 + (nIncX * (aChildSize.Width + fSpace * aChildSize.Width)) / 2;
424 else if (((index / nCol) + 1) % 2 != 0)
425 aCurrPos.X = nStartX;
426
427 aCurrPos.Y += nIncY * (aChildSize.Height + fSpace * aChildSize.Height);
428 nColIdx = 0;
429 }
430
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)
438 // start from right
439 aCurrPos.X += (nIncX * (aChildSize.Width + fSpace * aChildSize.Width));
440 }
441 break;
442 }
443}
444
446{
447 if (rShape->getChildren().empty() || rShape->getSize().Width == 0
448 || rShape->getSize().Height == 0)
449 return;
450
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.
456
457 sal_Int32 nCount = rShape->getChildren().size();
458 double fAspectRatio = 0.32;
459
460 awt::Size aChildSize = rShape->getSize();
461 aChildSize.Width /= nCount;
462 aChildSize.Height /= nCount;
463
464 awt::Point aCurrPos(0, 0);
465 aCurrPos.X = fAspectRatio * aChildSize.Width * (nCount - 1);
466 aCurrPos.Y = fAspectRatio * aChildSize.Height;
467
468 for (auto& aCurrShape : rShape->getChildren())
469 {
470 aCurrShape->setPosition(aCurrPos);
471 if (nCount > 1)
472 {
473 aCurrPos.X -= aChildSize.Height / (nCount - 1);
474 }
475 aChildSize.Width += aChildSize.Height;
476 aCurrShape->setSize(aChildSize);
477 aCurrShape->setChildSize(aChildSize);
478 aCurrPos.Y += (aChildSize.Height);
479 }
480}
481
482bool CompositeAlg::inferFromLayoutProperty(const LayoutProperty& rMap, sal_Int32 nRefType,
483 sal_Int32& rValue)
484{
485 switch (nRefType)
486 {
487 case XML_r:
488 {
489 auto it = rMap.find(XML_l);
490 if (it == rMap.end())
491 {
492 return false;
493 }
494 sal_Int32 nLeft = it->second;
495 it = rMap.find(XML_w);
496 if (it == rMap.end())
497 {
498 return false;
499 }
500 rValue = nLeft + it->second;
501 return true;
502 }
503 default:
504 break;
505 }
506
507 return false;
508}
509
511 LayoutPropertyMap& rProperties)
512{
513 // TODO handle the case when we have ptType="...", not forName="...".
514 if (rConstraint.msForName.isEmpty())
515 {
516 return;
517 }
518
519 const LayoutPropertyMap::const_iterator aRef = rProperties.find(rConstraint.msRefForName);
520 if (aRef == rProperties.end())
521 return;
522
523 const LayoutProperty::const_iterator aRefType = aRef->second.find(rConstraint.mnRefType);
524 sal_Int32 nInferredValue = 0;
525 if (aRefType != aRef->second.end())
526 {
527 // Reference is found directly.
528 rProperties[rConstraint.msForName][rConstraint.mnType]
529 = aRefType->second * rConstraint.mfFactor;
530 }
531 else if (inferFromLayoutProperty(aRef->second, rConstraint.mnRefType, nInferredValue))
532 {
533 // Reference can be inferred.
534 rProperties[rConstraint.msForName][rConstraint.mnType]
535 = nInferredValue * rConstraint.mfFactor;
536 }
537 else
538 {
539 // Reference not found, assume a fixed value.
540 // Values are never in EMU, while oox::drawingml::Shape position and size are always in
541 // EMU.
542 const double fValue = o3tl::convert(rConstraint.mfValue,
543 isFontUnit(rConstraint.mnRefType) ? o3tl::Length::pt
546 rProperties[rConstraint.msForName][rConstraint.mnType] = fValue;
547 }
548}
549
551 const std::vector<Constraint>& rConstraints)
552{
554 LayoutProperty& rParent = aProperties[""];
555
556 sal_Int32 nParentXOffset = 0;
557
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;
561
562 if (rAlg.getAspectRatio() != 1.0)
563 {
564 rParent[XML_w] = rShape->getSize().Width;
565 rParent[XML_h] = rShape->getSize().Height;
566 rParent[XML_l] = 0;
567 rParent[XML_t] = 0;
568 rParent[XML_r] = rShape->getSize().Width;
569 rParent[XML_b] = rShape->getSize().Height;
570 }
571 else
572 {
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;
579 rParent[XML_t] = 0;
580 rParent[XML_r] = rShape->getSize().Width - rParent[XML_l];
581 rParent[XML_b] = rShape->getSize().Height;
582 }
583
584 for (const auto& rConstr : rConstraints)
585 {
586 // Apply direct constraints for all layout nodes.
588 }
589
590 for (auto& aCurrShape : rShape->getChildren())
591 {
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)
595 {
596 if (rConstr.msForName != aCurrShape->getInternalName())
597 {
598 continue;
599 }
600
602 }
603
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())
609 {
610 auto pLayoutNode = dynamic_cast<LayoutNode*>(pDirectChild.get());
611 if (!pLayoutNode)
612 {
613 continue;
614 }
615
616 if (pLayoutNode->getName() != aCurrShape->getInternalName())
617 {
618 continue;
619 }
620
621 for (const auto& pChild : pLayoutNode->getChildren())
622 {
623 auto pConstraintAtom = dynamic_cast<ConstraintAtom*>(pChild.get());
624 if (!pConstraintAtom)
625 {
626 continue;
627 }
628
629 const Constraint& rConstraint = pConstraintAtom->getConstraint();
630 if (!rConstraint.msForName.isEmpty())
631 {
632 continue;
633 }
634
635 if (!rConstraint.msRefForName.isEmpty())
636 {
637 continue;
638 }
639
640 // Either an absolute value or a factor of a property.
641 if (rConstraint.mfValue == 0.0 && rConstraint.mnRefType == XML_none)
642 {
643 continue;
644 }
645
646 Constraint aConstraint(rConstraint);
647 aConstraint.msForName = pLayoutNode->getName();
648 aConstraint.msRefForName = pLayoutNode->getName();
649
651 }
652 }
653
654 awt::Size aSize = rShape->getSize();
655 awt::Point aPos(0, 0);
656
657 const LayoutPropertyMap::const_iterator aPropIt
658 = aProperties.find(aCurrShape->getInternalName());
659 if (aPropIt != aProperties.end())
660 {
661 const LayoutProperty& rProp = aPropIt->second;
662 LayoutProperty::const_iterator it, it2;
663
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);
668
669 if ((it = rProp.find(XML_l)) != rProp.end())
670 aPos.X = it->second;
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;
675
676 if ((it = rProp.find(XML_t)) != rProp.end())
677 aPos.Y = it->second;
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;
682
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;
687
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);
691 }
692 else
693 SAL_WARN("oox.drawingml", "composite layout properties not found for shape "
694 << aCurrShape->getInternalName());
695
696 aCurrShape->setSize(aSize);
697 aCurrShape->setChildSize(aSize);
698 aCurrShape->setPosition(aPos);
699
700 nVertMin = std::min(aPos.Y, nVertMin);
701 nVertMax = std::max(aPos.Y + aSize.Height, nVertMax);
702
703 NamedShapePairs& rDiagramFontHeights
705 auto it = rDiagramFontHeights.find(aCurrShape->getInternalName());
706 if (it != rDiagramFontHeights.end())
707 {
708 // Internal name matches: put drawingml::Shape to the relevant group, for
709 // synchronized font height handling.
710 it->second.insert({ aCurrShape, {} });
711 }
712 }
713
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]))
716 return;
717
718 sal_Int32 nDiff = rParent[XML_h] - (nVertMax - nVertMin);
719 if (nDiff > 0)
720 {
721 for (auto& aCurrShape : rShape->getChildren())
722 {
723 awt::Point aPosition = aCurrShape->getPosition();
724 aPosition.Y += nDiff / 2;
725 aCurrShape->setPosition(aPosition);
726 }
727 }
728}
729
731 : mnCnt( -1 )
732 , mbHideLastTrans( true )
733 , mnPtType( 0 )
734 , mnSt( 0 )
735 , mnStep( 1 )
736{
737}
738
740{
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 );
747
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();
751}
752
754 : mnFunc( 0 )
755 , mnArg( 0 )
756 , mnOp( 0 )
757 , mnVal( 0 )
758{
759}
760
762{
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 );
768}
769
770void LayoutAtom::dump(int level)
771{
772 SAL_INFO("oox.drawingml", "level = " << level << " - " << msName << " of type " << typeid(*this).name() );
773 for (const auto& pAtom : getChildren())
774 pAtom->dump(level + 1);
775}
776
778 LayoutAtom(rLayoutNode)
779{
780 maIter.loadFromXAttr(xAttributes);
781}
782
784{
785 rVisitor.visit(*this);
786}
787
789{
790 if (!msRef.isEmpty())
791 {
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;
796 else
797 SAL_WARN("oox.drawingml", "ForEach reference \"" << msRef << "\" not found");
798 }
799 return LayoutAtomPtr();
800}
801
803{
804 rVisitor.visit(*this);
805}
806
807ConditionAtom::ConditionAtom(LayoutNode& rLayoutNode, bool isElse, const Reference< XFastAttributeList >& xAttributes) :
808 LayoutAtom(rLayoutNode),
809 mIsElse(isElse)
810{
811 maIter.loadFromXAttr( xAttributes );
812 maCond.loadFromXAttr( xAttributes );
813}
814
815bool ConditionAtom::compareResult(sal_Int32 nOperator, sal_Int32 nFirst, sal_Int32 nSecond)
816{
817 switch (nOperator)
818 {
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;
825 default:
826 SAL_WARN("oox.drawingml", "unsupported operator: " << nOperator);
827 return false;
828 }
829}
830
831namespace
832{
837OUString navigate(LayoutNode& rLayoutNode, svx::diagram::TypeConstant nType, std::u16string_view rFrom,
838 bool bSourceToDestination)
839{
840 for (const auto& rConnection : rLayoutNode.getDiagram().getData()->getConnections())
841 {
842 if (rConnection.mnXMLType != nType)
843 continue;
844
845 if (bSourceToDestination)
846 {
847 if (rConnection.msSourceId == rFrom)
848 return rConnection.msDestId;
849 }
850 else
851 {
852 if (rConnection.msDestId == rFrom)
853 return rConnection.msSourceId;
854 }
855 }
856
857 return OUString();
858}
859
860sal_Int32 calcMaxDepth(std::u16string_view rNodeName, const svx::diagram::Connections& rConnections)
861{
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);
866
867 return nMaxLength;
868}
869}
870
871sal_Int32 ConditionAtom::getNodeCount(const svx::diagram::Point* pPresPoint) const
872{
873 sal_Int32 nCount = 0;
874 OUString sNodeId = pPresPoint->msPresentationAssociationId;
875
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);
879
880 if (!sNodeId.isEmpty())
881 {
882 for (const auto& aCxn : mrLayoutNode.getDiagram().getData()->getConnections())
883 if (aCxn.mnXMLType == svx::diagram::TypeConstant::XML_parOf && aCxn.msSourceId == sNodeId)
884 nCount++;
885 }
886
887 return nCount;
888}
889
891{
892 if (mIsElse)
893 return true;
894 if (!pPresPoint)
895 return false;
896
897 switch (maCond.mnFunc)
898 {
899 case XML_var:
900 {
901 if (maCond.mnArg == XML_dir)
902 return compareResult(maCond.mnOp, pPresPoint->mnDirection, maCond.mnVal);
903 else if (maCond.mnArg == XML_hierBranch)
904 {
905 sal_Int32 nHierarchyBranch = pPresPoint->moHierarchyBranch.value_or(XML_std);
906 if (!pPresPoint->moHierarchyBranch.has_value())
907 {
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())
916 {
917 const svx::diagram::Point* pParent = it->second;
918 if (pParent->moHierarchyBranch.has_value())
919 nHierarchyBranch = pParent->moHierarchyBranch.value();
920 }
921 }
922 return compareResult(maCond.mnOp, nHierarchyBranch, maCond.mnVal);
923 }
924 break;
925 }
926
927 case XML_cnt:
928 return compareResult(maCond.mnOp, getNodeCount(pPresPoint), maCond.msVal.toInt32());
929
930 case XML_maxDepth:
931 {
932 sal_Int32 nMaxDepth = calcMaxDepth(pPresPoint->msPresentationAssociationId, mrLayoutNode.getDiagram().getData()->getConnections());
933 return compareResult(maCond.mnOp, nMaxDepth, maCond.msVal.toInt32());
934 }
935
936 case XML_depth:
937 case XML_pos:
938 case XML_revPos:
939 case XML_posEven:
940 case XML_posOdd:
941 // TODO
942 default:
943 SAL_WARN("oox.drawingml", "unknown function " << maCond.mnFunc);
944 break;
945 }
946
947 return true;
948}
949
951{
952 rVisitor.visit(*this);
953}
954
956{
957 rVisitor.visit(*this);
958}
959
961{
962 rVisitor.visit(*this);
963}
964
965void ConstraintAtom::parseConstraint(std::vector<Constraint>& rConstraints,
966 bool bRequireForName) const
967{
968 // Allowlist for cases where empty forName is handled.
969 if (bRequireForName)
970 {
971 switch (maConstraint.mnType)
972 {
973 case XML_sp:
974 case XML_lMarg:
975 case XML_rMarg:
976 case XML_tMarg:
977 case XML_bMarg:
978 bRequireForName = false;
979 break;
980 }
982 {
983 case XML_sibTrans:
984 bRequireForName = false;
985 break;
986 }
987 }
988
989 if (bRequireForName && maConstraint.msForName.isEmpty())
990 return;
991
992 // accepting only basic equality constraints
995 {
996 rConstraints.push_back(maConstraint);
997 }
998}
999
1000void RuleAtom::parseRule(std::vector<Rule>& rRules) const
1001{
1002 if (!maRule.msForName.isEmpty())
1003 {
1004 rRules.push_back(maRule);
1005 }
1006}
1007
1009{
1010 rVisitor.visit(*this);
1011}
1012
1014{
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;
1024
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;
1033
1034 return oox::XML_rightArrow;
1035}
1036
1038{
1039 if (rShape->getChildren().empty())
1040 return (rShape->getSubType() != XML_conn) ? 1 : 0;
1041
1042 sal_Int32 nDir = XML_fromL;
1043 if (mnType == XML_hierRoot)
1044 nDir = XML_fromT;
1045 else if (maMap.count(XML_linDir))
1046 nDir = maMap.find(XML_linDir)->second;
1047
1048 const sal_Int32 nSecDir = maMap.count(XML_secLinDir) ? maMap.find(XML_secLinDir)->second : 0;
1049
1050 sal_Int32 nCount = 0;
1051 if (nDir == XML_fromT || nDir == XML_fromB)
1052 {
1053 for (const ShapePtr& pChild : rShape->getChildren())
1054 nCount += pChild->getVerticalShapesCount();
1055 }
1056 else if ((nDir == XML_fromL || nDir == XML_fromR) && nSecDir == XML_fromT)
1057 {
1058 for (const ShapePtr& pChild : rShape->getChildren())
1059 nCount += pChild->getVerticalShapesCount();
1060 nCount = (nCount + 1) / 2;
1061 }
1062 else
1063 {
1064 for (const ShapePtr& pChild : rShape->getChildren())
1065 nCount = std::max(nCount, pChild->getVerticalShapesCount());
1066 }
1067
1068 return nCount;
1069}
1070
1071namespace
1072{
1074bool HasCustomText(const ShapePtr& rShape, LayoutNode& rLayoutNode)
1075{
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)
1085 {
1086 if (rPair.second == rShape)
1087 {
1088 pPresNode = rPair.first;
1089 break;
1090 }
1091 }
1092 // Get the first data node of the presentation node.
1093 svx::diagram::Point* pDataNode = nullptr;
1094 if (pPresNode)
1095 {
1096 auto itPresToData = rPresOfNameMap.find(pPresNode->msModelId);
1097 if (itPresToData != rPresOfNameMap.end())
1098 {
1099 for (const auto& rPair : itPresToData->second)
1100 {
1101 const DiagramData::SourceIdAndDepth& rItem = rPair.second;
1102 auto it = rPointNameMap.find(rItem.msSourceId);
1103 if (it != rPointNameMap.end())
1104 {
1105 pDataNode = it->second;
1106 break;
1107 }
1108 }
1109 }
1110 }
1111
1112 // If we have a data node, see if its text is customized or not.
1113 if (pDataNode)
1114 {
1115 return pDataNode->mbCustomText;
1116 }
1117
1118 return false;
1119}
1120}
1121
1122void AlgAtom::layoutShape(const ShapePtr& rShape, const std::vector<Constraint>& rConstraints,
1123 const std::vector<Rule>& rRules)
1124{
1125 if (mnType != XML_lin)
1126 {
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();
1133 }),
1134 rShape->getChildren().end());
1135 }
1136
1137 switch(mnType)
1138 {
1139 case XML_composite:
1140 {
1141 CompositeAlg::layoutShapeChildren(*this, rShape, rConstraints);
1142 break;
1143 }
1144
1145 case XML_conn:
1146 {
1147 if (rShape->getSubType() == XML_conn)
1148 {
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();
1152
1153 rShape->setSubType(nType);
1154 rShape->getCustomShapeProperties()->setShapePresetType(nType);
1155 }
1156
1157 // Parse constraints to adjust the size.
1158 std::vector<Constraint> aDirectConstraints;
1159 const LayoutNode& rLayoutNode = getLayoutNode();
1160 for (const auto& pChild : rLayoutNode.getChildren())
1161 {
1162 auto pConstraintAtom = dynamic_cast<ConstraintAtom*>(pChild.get());
1163 if (pConstraintAtom)
1164 pConstraintAtom->parseConstraint(aDirectConstraints, /*bRequireForName=*/false);
1165 }
1166
1168 LayoutProperty& rParent = aProperties[""];
1169 rParent[XML_w] = rShape->getSize().Width;
1170 rParent[XML_h] = rShape->getSize().Height;
1171 rParent[XML_l] = 0;
1172 rParent[XML_t] = 0;
1173 rParent[XML_r] = rShape->getSize().Width;
1174 rParent[XML_b] = rShape->getSize().Height;
1175 for (const auto& rConstr : aDirectConstraints)
1176 {
1177 const LayoutPropertyMap::const_iterator aRef
1178 = aProperties.find(rConstr.msRefForName);
1179 if (aRef != aProperties.end())
1180 {
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;
1186 }
1187 }
1188 awt::Size aSize;
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);
1197 break;
1198 }
1199
1200 case XML_cycle:
1201 {
1202 if (rShape->getChildren().empty())
1203 break;
1204
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);
1215
1216 std::vector<oox::drawingml::ShapePtr> aCycleChildren = rShape->getChildren();
1217
1218 if (nctrShpMap == XML_fNode)
1219 {
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);
1228 }
1229
1230 const sal_Int32 nShapes = aCycleChildren.size();
1231 if (nShapes)
1232 {
1233 const sal_Int32 nConnectorRadius = nRadius * cos(basegfx::deg2rad(nSpanAngle / nShapes));
1234 const sal_Int32 nConnectorAngle = nSpanAngle > 0 ? 0 : 180;
1235
1236 sal_Int32 idx = 0;
1237 for (auto & aCurrShape : aCycleChildren)
1238 {
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)
1243 {
1244 aCurrSize = aConnectorSize;
1245 nCurrRadius = nConnectorRadius;
1246 }
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);
1250
1251 aCurrShape->setPosition(aCurrPos);
1252 aCurrShape->setSize(aCurrSize);
1253 aCurrShape->setChildSize(aCurrSize);
1254
1255 if (nRotationPath == XML_alongPath)
1256 aCurrShape->setRotation(fAngle * PER_DEGREE);
1257
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);
1262
1263 idx++;
1264 }
1265 }
1266 break;
1267 }
1268
1269 case XML_hierChild:
1270 case XML_hierRoot:
1271 {
1272 if (rShape->getChildren().empty() || rShape->getSize().Width == 0 || rShape->getSize().Height == 0)
1273 break;
1274
1275 // hierRoot is the manager -> employees vertical linear path,
1276 // hierChild is the first employee -> last employee horizontal
1277 // linear path.
1278 sal_Int32 nDir = XML_fromL;
1279 if (mnType == XML_hierRoot)
1280 nDir = XML_fromT;
1281 else if (maMap.count(XML_linDir))
1282 nDir = maMap.find(XML_linDir)->second;
1283
1284 const sal_Int32 nSecDir = maMap.count(XML_secLinDir) ? maMap.find(XML_secLinDir)->second : 0;
1285
1286 sal_Int32 nCount = rShape->getChildren().size();
1287
1288 if (mnType == XML_hierChild)
1289 {
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; });
1294 }
1295
1296 const double fSpaceWidth = 0.1;
1297 const double fSpaceHeight = 0.3;
1298
1299 if (mnType == XML_hierRoot && nCount == 3)
1300 {
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]);
1306 }
1307
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;
1313
1314 awt::Size aChildSize = rShape->getSize();
1315 aChildSize.Height /= (rShape->getVerticalShapesCount() + (rShape->getVerticalShapesCount() - 1) * fSpaceHeight);
1316 aChildSize.Width /= (nHorizontalShapesCount + (nHorizontalShapesCount - 1) * fSpaceWidth);
1317
1318 awt::Size aConnectorSize = aChildSize;
1319 aConnectorSize.Width = 1;
1320
1321 awt::Point aChildPos(0, 0);
1322
1323 // indent children to show they are descendants, not siblings
1324 if (mnType == XML_hierChild && nHorizontalShapesCount == 1)
1325 {
1326 const double fChildIndent = 0.1;
1327 aChildPos.X = aChildSize.Width * fChildIndent;
1328 aChildSize.Width *= (1 - 2 * fChildIndent);
1329 }
1330
1331 sal_Int32 nIdx = 0;
1332 sal_Int32 nRowHeight = 0;
1333 for (auto& pChild : rShape->getChildren())
1334 {
1335 pChild->setPosition(aChildPos);
1336
1337 if (mnType == XML_hierChild && pChild->getSubType() == XML_conn)
1338 {
1339 // Connectors should not influence the position of
1340 // non-connect shapes.
1341 pChild->setSize(aConnectorSize);
1342 pChild->setChildSize(aConnectorSize);
1343 continue;
1344 }
1345
1346 awt::Size aCurrSize = aChildSize;
1347 aCurrSize.Height *= pChild->getVerticalShapesCount() + (pChild->getVerticalShapesCount() - 1) * fSpaceHeight;
1348
1349 pChild->setSize(aCurrSize);
1350 pChild->setChildSize(aCurrSize);
1351
1352 if (nDir == XML_fromT || nDir == XML_fromB)
1353 aChildPos.Y += aCurrSize.Height + aChildSize.Height * fSpaceHeight;
1354 else
1355 aChildPos.X += aCurrSize.Width + aCurrSize.Width * fSpaceWidth;
1356
1357 nRowHeight = std::max(nRowHeight, aCurrSize.Height);
1358
1359 if (nSecDir == XML_fromT && nIdx % 2 == 1)
1360 {
1361 aChildPos.X = 0;
1362 aChildPos.Y += nRowHeight + aChildSize.Height * fSpaceHeight;
1363 nRowHeight = 0;
1364 }
1365
1366 nIdx++;
1367 }
1368
1369 break;
1370 }
1371
1372 case XML_lin:
1373 {
1374 // spread children evenly across one axis, stretch across second
1375
1376 if (rShape->getChildren().empty() || rShape->getSize().Width == 0 || rShape->getSize().Height == 0)
1377 break;
1378
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);
1382
1383 double fCount = rShape->getChildren().size();
1384 sal_Int32 nConnectorAngle = 0;
1385 switch (nDir)
1386 {
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;
1391 }
1392
1393 awt::Size aSpaceSize;
1394
1395 // Find out which constraint is relevant for which (internal) name.
1397 for (const auto& rConstraint : rConstraints)
1398 {
1399 if (rConstraint.msForName.isEmpty())
1400 continue;
1401
1402 LayoutProperty& rProperty = aProperties[rConstraint.msForName];
1403 if (rConstraint.mnType == XML_w)
1404 {
1405 rProperty[XML_w] = rShape->getSize().Width * rConstraint.mfFactor;
1406 if (rProperty[XML_w] > rShape->getSize().Width)
1407 {
1408 rProperty[XML_w] = rShape->getSize().Width;
1409 }
1410 }
1411 if (rConstraint.mnType == XML_h)
1412 {
1413 rProperty[XML_h] = rShape->getSize().Height * rConstraint.mfFactor;
1414 if (rProperty[XML_h] > rShape->getSize().Height)
1415 {
1416 rProperty[XML_h] = rShape->getSize().Height;
1417 }
1418 }
1419
1420 if (rConstraint.mnType == XML_primFontSz && rConstraint.mnFor == XML_des
1421 && rConstraint.mnOperator == XML_equ)
1422 {
1423 NamedShapePairs& rDiagramFontHeights
1425 auto it = rDiagramFontHeights.find(rConstraint.msForName);
1426 if (it == rDiagramFontHeights.end())
1427 {
1428 // Start tracking all shapes with this internal name: they'll have the same
1429 // font height.
1430 rDiagramFontHeights[rConstraint.msForName] = {};
1431 }
1432 }
1433
1434 // TODO: get values from differently named constraints as well
1435 if (rConstraint.msForName == "sp" || rConstraint.msForName == "space" || rConstraint.msForName == "sibTrans")
1436 {
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;
1441 }
1442 }
1443
1444 // first approximation of children size
1445 std::set<OUString> aChildrenToShrink;
1446 for (const auto& rRule : rRules)
1447 {
1448 // Consider rules: when scaling down, only change children where the rule allows
1449 // doing so.
1450 aChildrenToShrink.insert(rRule.msForName);
1451 }
1452
1453 if (nDir == XML_fromT || nDir == XML_fromB)
1454 {
1455 // TODO consider rules for vertical linear layout as well.
1456 aChildrenToShrink.clear();
1457 }
1458
1459 if (!aChildrenToShrink.empty())
1460 {
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())
1465 {
1466 if (aChildrenToShrink.find(aCurrShape->getInternalName())
1467 == aChildrenToShrink.end())
1468 {
1469 if (fCount > 1.0)
1470 {
1471 fCount -= 1.0;
1472
1473 bool bIsDependency = false;
1474 double fFactor = 0;
1475 for (const auto& rConstraint : rConstraints)
1476 {
1477 if (rConstraint.msForName != aCurrShape->getInternalName())
1478 {
1479 continue;
1480 }
1481
1482 if ((nDir == XML_fromL || nDir == XML_fromR) && rConstraint.mnType != XML_w)
1483 {
1484 continue;
1485 }
1486 if ((nDir == XML_fromL || nDir == XML_fromR) && rConstraint.mnType == XML_w)
1487 {
1488 fFactor = rConstraint.mfFactor;
1489 }
1490
1491 if ((nDir == XML_fromT || nDir == XML_fromB) && rConstraint.mnType != XML_h)
1492 {
1493 continue;
1494 }
1495 if ((nDir == XML_fromT || nDir == XML_fromB) && rConstraint.mnType == XML_h)
1496 {
1497 fFactor = rConstraint.mfFactor;
1498 }
1499
1500 if (aChildrenToShrink.find(rConstraint.msRefForName) == aChildrenToShrink.end())
1501 {
1502 continue;
1503 }
1504
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;
1510 break;
1511 }
1512
1513 if (!bIsDependency && aCurrShape->getServiceName() == "com.sun.star.drawing.GroupShape")
1514 {
1515 bool bScaleDownEmptySpacing = false;
1516 if (nDir == XML_fromL || nDir == XML_fromR)
1517 {
1518 std::optional<sal_Int32> oWidth = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);
1519 bScaleDownEmptySpacing = oWidth.has_value() && oWidth.value() > 0;
1520 }
1521 if (!bScaleDownEmptySpacing && (nDir == XML_fromT || nDir == XML_fromB))
1522 {
1523 std::optional<sal_Int32> oHeight = findProperty(aProperties, aCurrShape->getInternalName(), XML_h);
1524 bScaleDownEmptySpacing = oHeight.has_value() && oHeight.value() > 0;
1525 }
1526 if (bScaleDownEmptySpacing && aCurrShape->getChildren().empty())
1527 {
1528 fCount += fFactor;
1529 aChildrenToShrinkDeps.insert(aCurrShape->getInternalName());
1530 }
1531 }
1532 }
1533 }
1534 }
1535
1536 aChildrenToShrink.insert(aChildrenToShrinkDeps.begin(), aChildrenToShrinkDeps.end());
1537
1538 // No manual spacing: spacings are children as well.
1539 aSpaceSize = awt::Size();
1540 }
1541 else
1542 {
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();
1550 }),
1551 rShape->getChildren().end());
1552 fCount = rShape->getChildren().size();
1553 }
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;
1559
1560 awt::Point aCurrPos(0, 0);
1561 if (nIncX == -1)
1562 aCurrPos.X = rShape->getSize().Width - aChildSize.Width;
1563 if (nIncY == -1)
1564 aCurrPos.Y = rShape->getSize().Height - aChildSize.Height;
1565
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())
1570 {
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;
1580 }
1581
1582 aTotalSize.Width += (fCount-1) * aSpaceSize.Width;
1583 aTotalSize.Height += (fCount-1) * aSpaceSize.Height;
1584
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;
1593
1594 for (auto& aCurrShape : rShape->getChildren())
1595 {
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);
1599
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())
1608 {
1609 aSize.Width *= fWidthScale;
1610 }
1611 if (aChildrenToShrink.empty()
1612 || aChildrenToShrink.find(aCurrShape->getInternalName())
1613 != aChildrenToShrink.end())
1614 {
1615 aSize.Height *= fHeightScale;
1616 }
1617 aCurrShape->setSize(aSize);
1618 aCurrShape->setChildSize(aSize);
1619
1620 // center in the other axis - probably some parameter controls it
1621 if (nIncX)
1622 aCurrPos.Y = (rShape->getSize().Height - aSize.Height) / 2;
1623 if (nIncY)
1624 aCurrPos.X = (rShape->getSize().Width - aSize.Width) / 2;
1625 if (aCurrPos.X < 0)
1626 {
1627 aCurrPos.X = 0;
1628 }
1629 if (aCurrPos.Y < 0)
1630 {
1631 aCurrPos.Y = 0;
1632 }
1633
1634 aCurrShape->setPosition(aCurrPos);
1635
1636 aCurrPos.X += nIncX * (aSize.Width + aSpaceSize.Width);
1637 aCurrPos.Y += nIncY * (aSize.Height + aSpaceSize.Height);
1638
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);
1643 }
1644
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())
1649 {
1650 auto pLayoutNode = dynamic_cast<LayoutNode*>(pAtom.get());
1651 if (pLayoutNode)
1652 {
1653 pParentLayoutNode = pLayoutNode;
1654 break;
1655 }
1656 }
1657 if (pParentLayoutNode)
1658 {
1659 nChildOrder = pParentLayoutNode->getChildOrder();
1660 }
1661 if (nChildOrder == XML_t)
1662 {
1663 std::reverse(rShape->getChildren().begin(), rShape->getChildren().end());
1664 }
1665
1666 break;
1667 }
1668
1669 case XML_pyra:
1670 {
1672 break;
1673 }
1674
1675 case XML_snake:
1676 {
1677 SnakeAlg::layoutShapeChildren(*this, rShape, rConstraints);
1678 break;
1679 }
1680
1681 case XML_sp:
1682 {
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());
1686 break;
1687 }
1688
1689 case XML_tx:
1690 {
1691 // adjust text alignment
1692
1693 // Parse constraints, only self margins as a start.
1694 double fFontSize = 0;
1695 for (const auto& rConstr : rConstraints)
1696 {
1697 if (rConstr.mnRefType == XML_w)
1698 {
1699 if (!rConstr.msForName.isEmpty())
1700 continue;
1701
1702 sal_Int32 nProperty = getPropertyFromConstraint(rConstr.mnType);
1703 if (!nProperty)
1704 continue;
1705
1706 // PowerPoint takes size as points, but gives margin as MMs.
1707 double fFactor = convertPointToMms(rConstr.mfFactor);
1708
1709 // DrawingML works in EMUs, UNO API works in MM100s.
1710 sal_Int32 nValue = o3tl::convert(rShape->getSize().Width * fFactor,
1712
1713 rShape->getShapeProperties().setProperty(nProperty, nValue);
1714 }
1715 if (rConstr.mnType == XML_primFontSz)
1716 fFontSize = rConstr.mfValue;
1717 }
1718
1719 TextBodyPtr pTextBody = rShape->getTextBody();
1720 if (!pTextBody || pTextBody->isEmpty())
1721 break;
1722
1723 // adjust text size to fit shape
1724 if (fFontSize != 0)
1725 {
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;
1730 }
1731
1732 if (!HasCustomText(rShape, getLayoutNode()))
1733 {
1734 // No customized text properties: enable autofit.
1735 pTextBody->getTextProperties().maPropertyMap.setProperty(
1736 PROP_TextFitToSize, drawing::TextFitToSizeType_AUTOFIT);
1737 }
1738
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;
1746
1747 switch(nautoTxRot)
1748 {
1749 case XML_upr:
1750 {
1751 int n90x = 0;
1752 if (nShapeRot >= 315 * PER_DEGREE)
1753 /* keep 0 */;
1754 else if (nShapeRot > 225 * PER_DEGREE)
1755 n90x = -3;
1756 else if (nShapeRot >= 135 * PER_DEGREE)
1757 n90x = -2;
1758 else if (nShapeRot > 45 * PER_DEGREE)
1759 n90x = -1;
1760 pTextBody->getTextProperties().moRotation = n90x * 90 * PER_DEGREE;
1761 }
1762 break;
1763 case XML_grav:
1764 {
1765 if (nShapeRot > (90 * PER_DEGREE) && nShapeRot < (270 * PER_DEGREE))
1766 pTextBody->getTextProperties().moRotation = -180 * PER_DEGREE;
1767 }
1768 break;
1769 case XML_none:
1770 break;
1771 }
1772
1773 const sal_Int32 atxAnchorVert = maMap.count(XML_txAnchorVert) ? maMap.find(XML_txAnchorVert)->second : XML_mid;
1774
1775 switch(atxAnchorVert)
1776 {
1777 case XML_t:
1778 pTextBody->getTextProperties().meVA = css::drawing::TextVerticalAdjust_TOP;
1779 break;
1780 case XML_b:
1781 pTextBody->getTextProperties().meVA = css::drawing::TextVerticalAdjust_BOTTOM;
1782 break;
1783 case XML_mid:
1784 // text centered vertically by default
1785 default:
1786 pTextBody->getTextProperties().meVA = css::drawing::TextVerticalAdjust_CENTER;
1787 break;
1788 }
1789
1790 pTextBody->getTextProperties().maPropertyMap.setProperty(PROP_TextVerticalAdjust, pTextBody->getTextProperties().meVA);
1791
1792 // normalize list level
1793 sal_Int32 nBaseLevel = pTextBody->getParagraphs().front()->getProperties().getLevel();
1794 for (auto & aParagraph : pTextBody->getParagraphs())
1795 {
1796 if (aParagraph->getProperties().getLevel() < nBaseLevel)
1797 nBaseLevel = aParagraph->getProperties().getLevel();
1798 }
1799
1800 // Start bullets at:
1801 // 1 - top level
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--;
1808
1809 bool isBulletList = false;
1810 for (auto & aParagraph : pTextBody->getParagraphs())
1811 {
1812 sal_Int32 nLevel = aParagraph->getProperties().getLevel() - nBaseLevel;
1813 aParagraph->getProperties().setLevel(nLevel);
1814 if (nLevel >= nStartBulletsAtLevel)
1815 {
1816 if (!aParagraph->getProperties().getParaLeftMargin().has_value())
1817 {
1818 sal_Int32 nLeftMargin
1819 = o3tl::convert(285750 * (nLevel - nStartBulletsAtLevel + 1),
1821 aParagraph->getProperties().getParaLeftMargin() = nLeftMargin;
1822 }
1823
1824 if (!aParagraph->getProperties().getFirstLineIndentation().has_value())
1825 aParagraph->getProperties().getFirstLineIndentation()
1827
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;
1832 }
1833 }
1834
1835 // explicit alignment
1836 ParamMap::const_iterator aDir = maMap.find(XML_parTxLTRAlign);
1837 // TODO: XML_parTxRTLAlign
1838 if (aDir != maMap.end())
1839 {
1840 css::style::ParagraphAdjust aAlignment = GetParaAdjust(aDir->second);
1841 for (auto & aParagraph : pTextBody->getParagraphs())
1842 aParagraph->getProperties().setParaAdjust(aAlignment);
1843 }
1844 else if (!isBulletList)
1845 {
1846 // if not list use default alignment - centered
1847 for (auto & aParagraph : pTextBody->getParagraphs())
1848 aParagraph->getProperties().setParaAdjust(css::style::ParagraphAdjust::ParagraphAdjust_CENTER);
1849 }
1850 break;
1851 }
1852
1853 default:
1854 break;
1855 }
1856
1857 SAL_INFO(
1858 "oox.drawingml",
1859 "Layouting shape " << rShape->getInternalName() << ", alg type: " << mnType << ", ("
1860 << rShape->getPosition().X << "," << rShape->getPosition().Y << ","
1861 << rShape->getSize().Width << "," << rShape->getSize().Height << ")");
1862}
1863
1865{
1866 rVisitor.visit(*this);
1867}
1868
1869bool LayoutNode::setupShape( const ShapePtr& rShape, const svx::diagram::Point* pPresNode, sal_Int32 nCurrIdx ) const
1870{
1871 SAL_INFO(
1872 "oox.drawingml",
1873 "Filling content from layout node named \"" << msName
1874 << "\", modelId \"" << pPresNode->msModelId << "\"");
1875
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() )
1880 {
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)
1884 {
1885 if (rPair.second.mnDepth < nMinDepth)
1886 nMinDepth = rPair.second.mnDepth;
1887 }
1888
1889 for (const auto& rPair : aNodeName->second)
1890 {
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())
1896 {
1897 //busted, skip it
1898 continue;
1899 }
1900
1901 Shape* pDataNode2Shape(mrDgm.getData()->getOrCreateAssociatedShape(*aDataNode2->second));
1902 if (nullptr == pDataNode2Shape)
1903 {
1904 //busted, skip it
1905 continue;
1906 }
1907
1908 rShape->setDataNodeType(aDataNode2->second->mnXMLType);
1909
1910 if (rItem.mnDepth == 0)
1911 {
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() );
1918
1919 SAL_INFO(
1920 "oox.drawingml",
1921 "Custom shape with preset type "
1922 << (rShape->getCustomShapeProperties()
1923 ->getShapePresetType())
1924 << " added for layout node named \"" << msName
1925 << "\"");
1926 }
1927 else if (rItem.mnDepth == nMinDepth)
1928 {
1929 // If no real topmost element, then take properties from the one that's the closest
1930 // to topmost.
1931 rShape->getLineProperties() = pDataNode2Shape->getLineProperties();
1932 rShape->getFillProperties() = pDataNode2Shape->getFillProperties();
1933 }
1934
1935 // append text with right outline level
1936 if( pDataNode2Shape->getTextBody() &&
1937 !pDataNode2Shape->getTextBody()->getParagraphs().empty() &&
1938 !pDataNode2Shape->getTextBody()->getParagraphs().front()->getRuns().empty() )
1939 {
1940 TextBodyPtr pTextBody=rShape->getTextBody();
1941 if( !pTextBody )
1942 {
1943 pTextBody = std::make_shared<TextBody>();
1944
1945 // also copy text attrs
1946 pTextBody->getTextListStyle() =
1947 pDataNode2Shape->getTextBody()->getTextListStyle();
1948 pTextBody->getTextProperties() =
1949 pDataNode2Shape->getTextBody()->getTextProperties();
1950
1951 rShape->setTextBody(pTextBody);
1952 }
1953
1954 const TextParagraphVector& rSourceParagraphs
1955 = pDataNode2Shape->getTextBody()->getParagraphs();
1956 for (const auto& pSourceParagraph : rSourceParagraphs)
1957 {
1958 TextParagraph& rPara = pTextBody->addParagraph();
1959 if (rItem.mnDepth != -1)
1960 rPara.getProperties().setLevel(rItem.mnDepth);
1961
1962 for (const auto& pRun : pSourceParagraph->getRuns())
1963 rPara.addRun(pRun);
1964 const TextBodyPtr& rBody = pDataNode2Shape->getTextBody();
1965 rPara.getProperties().apply(rBody->getParagraphs().front()->getProperties());
1966 }
1967 }
1968 }
1969 }
1970 else
1971 {
1972 SAL_INFO(
1973 "oox.drawingml",
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());
1981 }
1982
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() )
1991 {
1992 const DiagramQStyleMap::const_iterator aStyle = mrDgm.getStyles().find(aStyleLabel);
1993 if( aStyle != mrDgm.getStyles().end() )
1994 {
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;
2000 }
2001 else
2002 {
2003 SAL_WARN("oox.drawingml", "Style " << aStyleLabel << " not found");
2004 }
2005
2006 const DiagramColorMap::const_iterator aColor = mrDgm.getColors().find(aStyleLabel);
2007 if( aColor != mrDgm.getColors().end() )
2008 {
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);
2020 }
2021 }
2022
2023 // even if no data node found, successful anyway. it's
2024 // contained at the layoutnode
2025 return true;
2026}
2027
2029{
2030 for (LayoutAtomPtr pAtom = getParent(); pAtom; pAtom = pAtom->getParent())
2031 {
2032 auto pLayoutNode = dynamic_cast<LayoutNode*>(pAtom.get());
2033 if (pLayoutNode)
2034 return pLayoutNode;
2035 }
2036
2037 return nullptr;
2038}
2039
2041{
2042 rVisitor.visit(*this);
2043}
2044
2045}
2046
2047/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
PropertiesInfo aProperties
Provides access to attribute values of an element.
std::vector< sal_Int32 > getTokenList(sal_Int32 nAttrToken) const
std::optional< sal_Int32 > getInteger(sal_Int32 nAttrToken) const
Returns the 32-bit signed integer value of the specified attribute (decimal).
std::optional< bool > getBool(sal_Int32 nAttrToken) const
Returns the boolean value of the specified attribute.
virtual void accept(LayoutAtomVisitor &) override
visitor acceptance
void layoutShape(const ShapePtr &rShape, const std::vector< Constraint > &rConstraints, const std::vector< Rule > &rRules)
const ParamMap & getMap() const
std::map< sal_Int32, sal_Int32 > ParamMap
sal_Int32 getVerticalShapesCount(const ShapePtr &rShape)
sal_Int32 getConnectorType()
Determines the connector shape type for conn algorithm.
virtual void accept(LayoutAtomVisitor &) override
visitor acceptance
static void applyConstraintToLayout(const Constraint &rConstraint, LayoutPropertyMap &rProperties)
Apply rConstraint to the rProperties shared layout state.
static bool inferFromLayoutProperty(const LayoutProperty &rMap, sal_Int32 nRefType, sal_Int32 &rValue)
Decides if a certain reference type (e.g.
static void layoutShapeChildren(AlgAtom &rAlg, const ShapePtr &rShape, const std::vector< Constraint > &rConstraints)
bool getDecision(const svx::diagram::Point *pPresPoint) const
virtual void accept(LayoutAtomVisitor &) override
visitor acceptance
sal_Int32 getNodeCount(const svx::diagram::Point *pPresPoint) const
static bool compareResult(sal_Int32 nOperator, sal_Int32 nFirst, sal_Int32 nSecond)
ConditionAtom(LayoutNode &rLayoutNode, bool isElse, const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttributes)
virtual void accept(LayoutAtomVisitor &) override
visitor acceptance
void parseConstraint(std::vector< Constraint > &rConstraints, bool bRequireForName) const
oox::core::NamedShapePairs & getDiagramFontHeights()
const OoxDiagramDataPtr & getData() const
const DiagramLayoutPtr & getLayout() const
virtual void accept(LayoutAtomVisitor &) override
visitor acceptance
ForEachAtom(LayoutNode &rLayoutNode, const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttributes)
abstract Atom for the layout
const std::vector< LayoutAtomPtr > & getChildren() const
LayoutAtomPtr getParent() const
const LayoutNode * getParentLayoutNode() const
bool setupShape(const ShapePtr &rShape, const svx::diagram::Point *pPresNode, sal_Int32 nCurrIdx) const
virtual void accept(LayoutAtomVisitor &) override
visitor acceptance
static void layoutShapeChildren(const ShapePtr &rShape)
void parseRule(std::vector< Rule > &rRules) const
virtual void accept(LayoutAtomVisitor &) override
visitor acceptance
virtual void accept(LayoutAtomVisitor &) override
visitor acceptance
PropertyMap & getShapeProperties()
Definition: shape.hxx:126
CustomShapePropertiesPtr & getCustomShapeProperties()
Definition: shape.hxx:137
LineProperties & getLineProperties()
Definition: shape.hxx:128
const TextListStylePtr & getMasterTextListStyle() const
Definition: shape.hxx:195
FillProperties & getFillProperties()
Definition: shape.hxx:131
const TextBodyPtr & getTextBody() const
Definition: shape.hxx:193
static void layoutShapeChildren(const AlgAtom &rAlg, const ShapePtr &rShape, const std::vector< Constraint > &rConstraints)
void apply(const TextParagraphProperties &rSourceProps)
void addRun(const TextRunPtr &pRun)
TextParagraphProperties & getProperties()
std::map< OUString, std::map< sal_Int32, SourceIdAndDepth > > StringMap
std::map< OUString, Point * > PointNameMap
int nCount
float u
sal_Int16 nValue
const sal_uInt16 idx[]
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
if(aStr !=aBuf) UpdateName_Impl(m_xFollowLb.get()
tools::Long const nLeftMargin
constexpr double deg2rad(double v)
size
index
constexpr Point convert(const Point &rPoint, o3tl::Length eFrom, o3tl::Length eTo)
std::map< OUString, ShapePairs > NamedShapePairs
double convertPointToMms(double fValue)
Converts the passed double value from points to mm.
std::shared_ptr< Shape > ShapePtr
std::map< OUString, LayoutAtomPtr > LayoutAtomMap
std::shared_ptr< LayoutAtom > LayoutAtomPtr
std::map< const svx::diagram::Point *, ShapePtr > PresPointShapeMap
ParagraphAdjust GetParaAdjust(sal_Int32 nAlign)
converts a paragraph align to a ParaAdjust
std::map< OUString, LayoutProperty > LayoutPropertyMap
const sal_Int32 PER_DEGREE
std::shared_ptr< TextBody > TextBodyPtr
std::map< sal_Int32, sal_Int32 > LayoutProperty
XML_asst
XML_none
XML_sibTrans
std::vector< Connection > Connections
QPRO_FUNC_TYPE nType
void loadFromXAttr(const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttributes)
Constraints allow you to specify an ideal (or starting point) size for each shape.
static const oox::drawingml::Color & getColorByIndex(const std::vector< oox::drawingml::Color > &rColors, sal_Int32 nIndex)
Definition: diagram.cxx:432
std::vector< oox::drawingml::Color > maTextFillColors
std::vector< oox::drawingml::Color > maFillColors
std::vector< oox::drawingml::Color > maEffectColors
std::vector< oox::drawingml::Color > maLineColors
std::vector< sal_Int32 > maAxis
void loadFromXAttr(const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttributes)
virtual void visit(ConstraintAtom &rAtom)=0
std::optional< sal_Int32 > moHierarchyBranch
OUString msPresentationLayoutStyleLabel
OUString msPresentationAssociationId