Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / oox / source / drawingml / diagram / diagramlayoutatoms.cxx
blob14af84da6ac48b5952904e796a8dc3a9144274c6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
22 #include "layoutatomvisitorbase.hxx"
24 #include <basegfx/numeric/ftools.hxx>
25 #include <sal/log.hxx>
27 #include <oox/helper/attributelist.hxx>
28 #include <oox/token/properties.hxx>
29 #include <drawingml/fillproperties.hxx>
30 #include <drawingml/lineproperties.hxx>
31 #include <drawingml/textbody.hxx>
32 #include <drawingml/textparagraph.hxx>
33 #include <drawingml/textrun.hxx>
34 #include <drawingml/customshapeproperties.hxx>
35 #include <tools/gen.hxx>
36 #include <com/sun/star/drawing/TextFitToSizeType.hpp>
38 using namespace ::com::sun::star;
39 using namespace ::com::sun::star::uno;
40 using namespace ::com::sun::star::xml::sax;
41 using namespace ::oox::core;
43 namespace
45 /// Looks up the value of the rInternalName -> nProperty key in rProperties.
46 oox::OptValue<sal_Int32> findProperty(const oox::drawingml::LayoutPropertyMap& rProperties,
47 const OUString& rInternalName, sal_Int32 nProperty)
49 oox::OptValue<sal_Int32> oRet;
51 auto it = rProperties.find(rInternalName);
52 if (it != rProperties.end())
54 const oox::drawingml::LayoutProperty& rProperty = it->second;
55 auto itProperty = rProperty.find(nProperty);
56 if (itProperty != rProperty.end())
57 oRet = itProperty->second;
60 return oRet;
63 /**
64 * Determines if nUnit is a font unit (measured in points) or not (measured in
65 * millimeters).
67 bool isFontUnit(sal_Int32 nUnit)
69 return nUnit == oox::XML_primFontSz || nUnit == oox::XML_secFontSz;
72 /// Determines which UNO property should be set for a given constraint type.
73 sal_Int32 getPropertyFromConstraint(sal_Int32 nConstraint)
75 switch (nConstraint)
77 case oox::XML_lMarg:
78 return oox::PROP_TextLeftDistance;
79 case oox::XML_rMarg:
80 return oox::PROP_TextRightDistance;
81 case oox::XML_tMarg:
82 return oox::PROP_TextUpperDistance;
83 case oox::XML_bMarg:
84 return oox::PROP_TextLowerDistance;
87 return 0;
90 /**
91 * Determines if pShape is (or contains) a presentation of a data node of type
92 * nType.
94 bool containsDataNodeType(const oox::drawingml::ShapePtr& pShape, sal_Int32 nType)
96 if (pShape->getDataNodeType() == nType)
97 return true;
99 for (const auto& pChild : pShape->getChildren())
101 if (containsDataNodeType(pChild, nType))
102 return true;
105 return false;
109 namespace oox { namespace drawingml {
111 IteratorAttr::IteratorAttr( )
112 : mnCnt( -1 )
113 , mbHideLastTrans( true )
114 , mnPtType( 0 )
115 , mnSt( 0 )
116 , mnStep( 1 )
120 void IteratorAttr::loadFromXAttr( const Reference< XFastAttributeList >& xAttr )
122 AttributeList attr( xAttr );
123 maAxis = attr.getTokenList(XML_axis);
124 mnCnt = attr.getInteger( XML_cnt, -1 );
125 mbHideLastTrans = attr.getBool( XML_hideLastTrans, true );
126 mnSt = attr.getInteger( XML_st, 0 );
127 mnStep = attr.getInteger( XML_step, 1 );
129 // better to keep first token instead of error when multiple values
130 std::vector<sal_Int32> aPtTypes = attr.getTokenList(XML_ptType);
131 mnPtType = aPtTypes.empty() ? XML_all : aPtTypes.front();
134 ConditionAttr::ConditionAttr()
135 : mnFunc( 0 )
136 , mnArg( 0 )
137 , mnOp( 0 )
138 , mnVal( 0 )
142 void ConditionAttr::loadFromXAttr( const Reference< XFastAttributeList >& xAttr )
144 mnFunc = xAttr->getOptionalValueToken( XML_func, 0 );
145 mnArg = xAttr->getOptionalValueToken( XML_arg, XML_none );
146 mnOp = xAttr->getOptionalValueToken( XML_op, 0 );
147 msVal = xAttr->getOptionalValue( XML_val );
148 mnVal = xAttr->getOptionalValueToken( XML_val, 0 );
151 void LayoutAtom::dump(int level)
153 SAL_INFO("oox.drawingml", "level = " << level << " - " << msName << " of type " << typeid(*this).name() );
154 for (const auto& pAtom : getChildren())
155 pAtom->dump(level + 1);
158 ForEachAtom::ForEachAtom(LayoutNode& rLayoutNode, const Reference< XFastAttributeList >& xAttributes) :
159 LayoutAtom(rLayoutNode)
161 maIter.loadFromXAttr(xAttributes);
164 void ForEachAtom::accept( LayoutAtomVisitor& rVisitor )
166 rVisitor.visit(*this);
169 LayoutAtomPtr ForEachAtom::getRefAtom()
171 if (!msRef.isEmpty())
173 const LayoutAtomMap& rLayoutAtomMap = getLayoutNode().getDiagram().getLayout()->getLayoutAtomMap();
174 LayoutAtomMap::const_iterator pRefAtom = rLayoutAtomMap.find(msRef);
175 if (pRefAtom != rLayoutAtomMap.end())
176 return pRefAtom->second;
177 else
178 SAL_WARN("oox.drawingml", "ForEach reference \"" << msRef << "\" not found");
180 return LayoutAtomPtr();
183 void ChooseAtom::accept( LayoutAtomVisitor& rVisitor )
185 rVisitor.visit(*this);
188 ConditionAtom::ConditionAtom(LayoutNode& rLayoutNode, bool isElse, const Reference< XFastAttributeList >& xAttributes) :
189 LayoutAtom(rLayoutNode),
190 mIsElse(isElse)
192 maIter.loadFromXAttr( xAttributes );
193 maCond.loadFromXAttr( xAttributes );
196 bool ConditionAtom::compareResult(sal_Int32 nOperator, sal_Int32 nFirst, sal_Int32 nSecond)
198 switch (nOperator)
200 case XML_equ: return nFirst == nSecond;
201 case XML_gt: return nFirst > nSecond;
202 case XML_gte: return nFirst >= nSecond;
203 case XML_lt: return nFirst < nSecond;
204 case XML_lte: return nFirst <= nSecond;
205 case XML_neq: return nFirst != nSecond;
206 default:
207 SAL_WARN("oox.drawingml", "unsupported operator: " << nOperator);
208 return false;
212 namespace
215 * Takes the connection list from rLayoutNode, navigates from rFrom on an edge
216 * of type nType, using a direction determined by bSourceToDestination.
218 OUString navigate(const LayoutNode& rLayoutNode, sal_Int32 nType, const OUString& rFrom,
219 bool bSourceToDestination)
221 for (const auto& rConnection : rLayoutNode.getDiagram().getData()->getConnections())
223 if (rConnection.mnType != nType)
224 continue;
226 if (bSourceToDestination)
228 if (rConnection.msSourceId == rFrom)
229 return rConnection.msDestId;
231 else
233 if (rConnection.msDestId == rFrom)
234 return rConnection.msSourceId;
238 return OUString();
241 sal_Int32 calcMaxDepth(const OUString& rNodeName, const dgm::Connections& rConnections)
243 sal_Int32 nMaxLength = 0;
244 for (auto const& aCxn : rConnections)
245 if (aCxn.mnType == XML_parOf && aCxn.msSourceId == rNodeName)
246 nMaxLength = std::max(nMaxLength, calcMaxDepth(aCxn.msDestId, rConnections) + 1);
248 return nMaxLength;
252 sal_Int32 ConditionAtom::getNodeCount(const dgm::Point* pPresPoint) const
254 sal_Int32 nCount = 0;
255 OUString sNodeId = pPresPoint->msPresentationAssociationId;
257 // HACK: special case - count children of first child
258 if (maIter.maAxis.size() == 2 && maIter.maAxis[0] == XML_ch && maIter.maAxis[1] == XML_ch)
259 sNodeId = navigate(mrLayoutNode, XML_parOf, sNodeId, /*bSourceToDestination*/ true);
261 if (!sNodeId.isEmpty())
263 for (const auto& aCxn : mrLayoutNode.getDiagram().getData()->getConnections())
264 if (aCxn.mnType == XML_parOf && aCxn.msSourceId == sNodeId)
265 nCount++;
268 return nCount;
271 bool ConditionAtom::getDecision(const dgm::Point* pPresPoint) const
273 if (mIsElse)
274 return true;
275 if (!pPresPoint)
276 return false;
278 switch (maCond.mnFunc)
280 case XML_var:
282 if (maCond.mnArg == XML_dir)
283 return compareResult(maCond.mnOp, pPresPoint->mnDirection, maCond.mnVal);
284 else if (maCond.mnArg == XML_hierBranch)
286 sal_Int32 nHierarchyBranch = pPresPoint->moHierarchyBranch.get(XML_std);
287 if (!pPresPoint->moHierarchyBranch.has())
289 // If <dgm:hierBranch> is missing in the current presentation
290 // point, ask the parent.
291 OUString aParent = navigate(mrLayoutNode, XML_presParOf, pPresPoint->msModelId,
292 /*bSourceToDestination*/ false);
293 DiagramData::PointNameMap& rPointNameMap
294 = mrLayoutNode.getDiagram().getData()->getPointNameMap();
295 auto it = rPointNameMap.find(aParent);
296 if (it != rPointNameMap.end())
298 const dgm::Point* pParent = it->second;
299 if (pParent->moHierarchyBranch.has())
300 nHierarchyBranch = pParent->moHierarchyBranch.get();
303 return compareResult(maCond.mnOp, nHierarchyBranch, maCond.mnVal);
305 break;
308 case XML_cnt:
309 return compareResult(maCond.mnOp, getNodeCount(pPresPoint), maCond.msVal.toInt32());
311 case XML_maxDepth:
313 sal_Int32 nMaxDepth = calcMaxDepth(pPresPoint->msPresentationAssociationId, mrLayoutNode.getDiagram().getData()->getConnections());
314 return compareResult(maCond.mnOp, nMaxDepth, maCond.msVal.toInt32());
317 case XML_depth:
318 case XML_pos:
319 case XML_revPos:
320 case XML_posEven:
321 case XML_posOdd:
322 // TODO
323 default:
324 SAL_WARN("oox.drawingml", "unknown function " << maCond.mnFunc);
325 break;
328 return true;
331 void ConditionAtom::accept( LayoutAtomVisitor& rVisitor )
333 rVisitor.visit(*this);
336 void ConstraintAtom::accept( LayoutAtomVisitor& rVisitor )
338 rVisitor.visit(*this);
341 void ConstraintAtom::parseConstraint(std::vector<Constraint>& rConstraints,
342 bool bRequireForName) const
344 // Whitelist for cases where empty forName is handled.
345 if (bRequireForName)
347 switch (maConstraint.mnType)
349 case XML_sp:
350 case XML_lMarg:
351 case XML_rMarg:
352 case XML_tMarg:
353 case XML_bMarg:
354 bRequireForName = false;
355 break;
359 if (bRequireForName && maConstraint.msForName.isEmpty())
360 return;
362 // accepting only basic equality constraints
363 if ((maConstraint.mnOperator == XML_none || maConstraint.mnOperator == XML_equ)
364 && maConstraint.mnType != XML_none)
366 rConstraints.push_back(maConstraint);
370 void AlgAtom::accept( LayoutAtomVisitor& rVisitor )
372 rVisitor.visit(*this);
375 sal_Int32 AlgAtom::getConnectorType()
377 sal_Int32 nConnRout = 0;
378 sal_Int32 nBegSty = 0;
379 sal_Int32 nEndSty = 0;
380 if (maMap.count(oox::XML_connRout))
381 nConnRout = maMap.find(oox::XML_connRout)->second;
382 if (maMap.count(oox::XML_begSty))
383 nBegSty = maMap.find(oox::XML_begSty)->second;
384 if (maMap.count(oox::XML_endSty))
385 nEndSty = maMap.find(oox::XML_endSty)->second;
387 if (nConnRout == oox::XML_bend)
388 return 0; // was oox::XML_bentConnector3 - connectors are hidden in org chart as they don't work anyway
389 if (nBegSty == oox::XML_arr && nEndSty == oox::XML_arr)
390 return oox::XML_leftRightArrow;
391 if (nBegSty == oox::XML_arr)
392 return oox::XML_leftArrow;
393 if (nEndSty == oox::XML_arr)
394 return oox::XML_rightArrow;
396 return oox::XML_rightArrow;
399 sal_Int32 AlgAtom::getVerticalShapesCount(const ShapePtr& rShape)
401 if (rShape->getChildren().empty())
402 return (rShape->getSubType() != XML_conn) ? 1 : 0;
404 sal_Int32 nDir = XML_fromL;
405 if (mnType == XML_hierRoot)
406 nDir = XML_fromT;
407 else if (maMap.count(XML_linDir))
408 nDir = maMap.find(XML_linDir)->second;
410 const sal_Int32 nSecDir = maMap.count(XML_secLinDir) ? maMap.find(XML_secLinDir)->second : 0;
412 sal_Int32 nCount = 0;
413 if (nDir == XML_fromT || nDir == XML_fromB)
415 for (const ShapePtr& pChild : rShape->getChildren())
416 nCount += pChild->getVerticalShapesCount();
418 else if ((nDir == XML_fromL || nDir == XML_fromR) && nSecDir == XML_fromT)
420 for (const ShapePtr& pChild : rShape->getChildren())
421 nCount += pChild->getVerticalShapesCount();
422 nCount = (nCount + 1) / 2;
424 else
426 for (const ShapePtr& pChild : rShape->getChildren())
427 nCount = std::max(nCount, pChild->getVerticalShapesCount());
430 return nCount;
433 void AlgAtom::layoutShape( const ShapePtr& rShape,
434 const std::vector<Constraint>& rConstraints )
436 switch(mnType)
438 case XML_composite:
440 // layout shapes using basic constraints
442 LayoutPropertyMap aProperties;
443 LayoutProperty& rParent = aProperties[""];
445 sal_Int32 nParentXOffset = 0;
446 if (mfAspectRatio != 1.0)
448 rParent[XML_w] = rShape->getSize().Width;
449 rParent[XML_h] = rShape->getSize().Height;
450 rParent[XML_l] = 0;
451 rParent[XML_t] = 0;
452 rParent[XML_r] = rShape->getSize().Width;
453 rParent[XML_b] = rShape->getSize().Height;
455 else
457 // Shrink width to be only as large as height.
458 rParent[XML_w] = std::min(rShape->getSize().Width, rShape->getSize().Height);
459 rParent[XML_h] = rShape->getSize().Height;
460 if (rParent[XML_w] < rShape->getSize().Width)
461 nParentXOffset = (rShape->getSize().Width - rParent[XML_w]) / 2;
462 rParent[XML_l] = nParentXOffset;
463 rParent[XML_t] = 0;
464 rParent[XML_r] = rShape->getSize().Width - rParent[XML_l];
465 rParent[XML_b] = rShape->getSize().Height;
468 for (const auto & rConstr : rConstraints)
470 const LayoutPropertyMap::const_iterator aRef = aProperties.find(rConstr.msRefForName);
471 if (aRef != aProperties.end())
473 const LayoutProperty::const_iterator aRefType = aRef->second.find(rConstr.mnRefType);
474 if (aRefType != aRef->second.end())
475 aProperties[rConstr.msForName][rConstr.mnType] = aRefType->second * rConstr.mfFactor;
476 else
478 // Values are never in EMU, while oox::drawingml::Shape
479 // position and size are always in EMU.
480 double fUnitFactor = 0;
481 if (isFontUnit(rConstr.mnRefType))
482 // Points -> EMU.
483 fUnitFactor = EMU_PER_PT;
484 else
485 // Millimeters -> EMU.
486 fUnitFactor = EMU_PER_HMM * 100;
487 aProperties[rConstr.msForName][rConstr.mnType]
488 = rConstr.mfValue * fUnitFactor;
493 for (auto & aCurrShape : rShape->getChildren())
495 awt::Size aSize = rShape->getSize();
496 awt::Point aPos(0, 0);
498 const LayoutPropertyMap::const_iterator aPropIt = aProperties.find(aCurrShape->getInternalName());
499 if (aPropIt != aProperties.end())
501 const LayoutProperty& rProp = aPropIt->second;
502 LayoutProperty::const_iterator it, it2;
504 if ( (it = rProp.find(XML_w)) != rProp.end() )
505 aSize.Width = std::min(it->second, rShape->getSize().Width);
506 if ( (it = rProp.find(XML_h)) != rProp.end() )
507 aSize.Height = std::min(it->second, rShape->getSize().Height);
509 if ( (it = rProp.find(XML_l)) != rProp.end() )
510 aPos.X = it->second;
511 else if ( (it = rProp.find(XML_ctrX)) != rProp.end() )
512 aPos.X = it->second - aSize.Width/2;
513 else if ((it = rProp.find(XML_r)) != rProp.end())
514 aPos.X = it->second - aSize.Width;
516 if ( (it = rProp.find(XML_t)) != rProp.end())
517 aPos.Y = it->second;
518 else if ( (it = rProp.find(XML_ctrY)) != rProp.end() )
519 aPos.Y = it->second - aSize.Height/2;
520 else if ((it = rProp.find(XML_b)) != rProp.end())
521 aPos.Y = it->second - aSize.Height;
523 if ( (it = rProp.find(XML_l)) != rProp.end() && (it2 = rProp.find(XML_r)) != rProp.end() )
524 aSize.Width = it2->second - it->second;
525 if ( (it = rProp.find(XML_t)) != rProp.end() && (it2 = rProp.find(XML_b)) != rProp.end() )
526 aSize.Height = it2->second - it->second;
528 aPos.X += nParentXOffset;
529 aSize.Width = std::min(aSize.Width, rShape->getSize().Width - aPos.X);
530 aSize.Height = std::min(aSize.Height, rShape->getSize().Height - aPos.Y);
532 else
533 SAL_WARN("oox.drawingml", "composite layout properties not found for shape " << aCurrShape->getInternalName());
535 aCurrShape->setSize(aSize);
536 aCurrShape->setChildSize(aSize);
537 aCurrShape->setPosition(aPos);
539 break;
542 case XML_conn:
544 if (rShape->getSubType() == XML_conn)
546 // There is no shape type "conn", replace it by an arrow based
547 // on the direction of the parent linear layout.
548 sal_Int32 nType = getConnectorType();
550 rShape->setSubType(nType);
551 rShape->getCustomShapeProperties()->setShapePresetType(nType);
554 // Parse constraints to adjust the size.
555 std::vector<Constraint> aDirectConstraints;
556 const LayoutNode& rLayoutNode = getLayoutNode();
557 for (const auto& pChild : rLayoutNode.getChildren())
559 auto pConstraintAtom = dynamic_cast<ConstraintAtom*>(pChild.get());
560 if (pConstraintAtom)
561 pConstraintAtom->parseConstraint(aDirectConstraints, /*bRequireForName=*/false);
564 LayoutPropertyMap aProperties;
565 LayoutProperty& rParent = aProperties[""];
566 rParent[XML_w] = rShape->getSize().Width;
567 rParent[XML_h] = rShape->getSize().Height;
568 rParent[XML_l] = 0;
569 rParent[XML_t] = 0;
570 rParent[XML_r] = rShape->getSize().Width;
571 rParent[XML_b] = rShape->getSize().Height;
572 for (const auto& rConstr : aDirectConstraints)
574 const LayoutPropertyMap::const_iterator aRef
575 = aProperties.find(rConstr.msRefForName);
576 if (aRef != aProperties.end())
578 const LayoutProperty::const_iterator aRefType
579 = aRef->second.find(rConstr.mnRefType);
580 if (aRefType != aRef->second.end())
581 aProperties[rConstr.msForName][rConstr.mnType]
582 = aRefType->second * rConstr.mfFactor;
585 awt::Size aSize;
586 aSize.Width = rParent[XML_w];
587 aSize.Height = rParent[XML_h];
588 // keep center position
589 awt::Point aPos = rShape->getPosition();
590 aPos.X += (rShape->getSize().Width - aSize.Width) / 2;
591 aPos.Y += (rShape->getSize().Height - aSize.Height) / 2;
592 rShape->setPosition(aPos);
593 rShape->setSize(aSize);
594 break;
597 case XML_cycle:
599 if (rShape->getChildren().empty())
600 break;
602 const sal_Int32 nStartAngle = maMap.count(XML_stAng) ? maMap.find(XML_stAng)->second : 0;
603 const sal_Int32 nSpanAngle = maMap.count(XML_spanAng) ? maMap.find(XML_spanAng)->second : 360;
604 const sal_Int32 nRotationPath = maMap.count(XML_rotPath) ? maMap.find(XML_rotPath)->second : XML_none;
605 const sal_Int32 nctrShpMap = maMap.count(XML_ctrShpMap) ? maMap.find(XML_ctrShpMap)->second : XML_none;
606 const awt::Size aCenter(rShape->getSize().Width / 2, rShape->getSize().Height / 2);
607 const awt::Size aChildSize(rShape->getSize().Width / 4, rShape->getSize().Height / 4);
608 const awt::Size aConnectorSize(rShape->getSize().Width / 12, rShape->getSize().Height / 12);
609 const sal_Int32 nRadius = std::min(
610 (rShape->getSize().Width - aChildSize.Width) / 2,
611 (rShape->getSize().Height - aChildSize.Height) / 2);
613 std::vector<oox::drawingml::ShapePtr> aCycleChildren = rShape->getChildren();
615 if (nctrShpMap == XML_fNode)
617 // first node placed in center, others around
618 oox::drawingml::ShapePtr pCenterShape = aCycleChildren.front();
619 aCycleChildren.erase(aCycleChildren.begin());
620 const awt::Point aCurrPos(aCenter.Width - aChildSize.Width / 2,
621 aCenter.Height - aChildSize.Height / 2);
622 pCenterShape->setPosition(aCurrPos);
623 pCenterShape->setSize(aChildSize);
624 pCenterShape->setChildSize(aChildSize);
627 const sal_Int32 nShapes = aCycleChildren.size();
628 if (nShapes)
630 const sal_Int32 nConnectorRadius = nRadius * cos(basegfx::deg2rad(nSpanAngle / nShapes));
631 const sal_Int32 nConnectorAngle = nSpanAngle > 0 ? 0 : 180;
633 sal_Int32 idx = 0;
634 for (auto & aCurrShape : aCycleChildren)
636 const double fAngle = static_cast<double>(idx)*nSpanAngle/nShapes + nStartAngle;
637 awt::Size aCurrSize = aChildSize;
638 sal_Int32 nCurrRadius = nRadius;
639 if (aCurrShape->getSubType() == XML_conn)
641 aCurrSize = aConnectorSize;
642 nCurrRadius = nConnectorRadius;
644 const awt::Point aCurrPos(
645 aCenter.Width + nCurrRadius*sin(basegfx::deg2rad(fAngle)) - aCurrSize.Width/2,
646 aCenter.Height - nCurrRadius*cos(basegfx::deg2rad(fAngle)) - aCurrSize.Height/2);
648 aCurrShape->setPosition(aCurrPos);
649 aCurrShape->setSize(aCurrSize);
650 aCurrShape->setChildSize(aCurrSize);
652 if (nRotationPath == XML_alongPath)
653 aCurrShape->setRotation(fAngle * PER_DEGREE);
655 // connectors should be handled in conn, but we don't have
656 // reference to previous and next child, so it's easier here
657 if (aCurrShape->getSubType() == XML_conn)
658 aCurrShape->setRotation((nConnectorAngle + fAngle) * PER_DEGREE);
660 idx++;
663 break;
666 case XML_hierChild:
667 case XML_hierRoot:
669 if (rShape->getChildren().empty() || rShape->getSize().Width == 0 || rShape->getSize().Height == 0)
670 break;
672 // hierRoot is the manager -> employees vertical linear path,
673 // hierChild is the first employee -> last employee horizontal
674 // linear path.
675 sal_Int32 nDir = XML_fromL;
676 if (mnType == XML_hierRoot)
677 nDir = XML_fromT;
678 else if (maMap.count(XML_linDir))
679 nDir = maMap.find(XML_linDir)->second;
681 const sal_Int32 nSecDir = maMap.count(XML_secLinDir) ? maMap.find(XML_secLinDir)->second : 0;
683 sal_Int32 nCount = rShape->getChildren().size();
685 if (mnType == XML_hierChild)
687 // Connectors should not influence the size of non-connect shapes.
688 nCount = std::count_if(
689 rShape->getChildren().begin(), rShape->getChildren().end(),
690 [](const ShapePtr& pShape) { return pShape->getSubType() != XML_conn; });
693 const double fSpaceWidth = 0.1;
694 const double fSpaceHeight = 0.3;
696 if (mnType == XML_hierRoot && nCount == 3)
698 // Order assistant nodes above employee nodes.
699 std::vector<ShapePtr>& rChildren = rShape->getChildren();
700 if (!containsDataNodeType(rChildren[1], XML_asst)
701 && containsDataNodeType(rChildren[2], XML_asst))
702 std::swap(rChildren[1], rChildren[2]);
705 sal_Int32 nHorizontalShapesCount = 1;
706 if (nSecDir == XML_fromT)
707 nHorizontalShapesCount = 2;
708 else if (nDir == XML_fromL || nDir == XML_fromR)
709 nHorizontalShapesCount = nCount;
711 awt::Size aChildSize = rShape->getSize();
712 aChildSize.Height /= (rShape->getVerticalShapesCount() + (rShape->getVerticalShapesCount() - 1) * fSpaceHeight);
713 aChildSize.Width /= (nHorizontalShapesCount + (nHorizontalShapesCount - 1) * fSpaceWidth);
715 awt::Size aConnectorSize = aChildSize;
716 aConnectorSize.Width = 1;
718 awt::Point aChildPos(0, 0);
720 // indent children to show they are descendants, not siblings
721 if (mnType == XML_hierChild && nHorizontalShapesCount == 1)
723 const double fChildIndent = 0.1;
724 aChildPos.X = aChildSize.Width * fChildIndent;
725 aChildSize.Width *= (1 - 2 * fChildIndent);
728 sal_Int32 nIdx = 0;
729 sal_Int32 nRowHeight = 0;
730 for (auto& pChild : rShape->getChildren())
732 pChild->setPosition(aChildPos);
734 if (mnType == XML_hierChild && pChild->getSubType() == XML_conn)
736 // Connectors should not influence the position of
737 // non-connect shapes.
738 pChild->setSize(aConnectorSize);
739 pChild->setChildSize(aConnectorSize);
740 continue;
743 awt::Size aCurrSize = aChildSize;
744 aCurrSize.Height *= pChild->getVerticalShapesCount() + (pChild->getVerticalShapesCount() - 1) * fSpaceHeight;
746 pChild->setSize(aCurrSize);
747 pChild->setChildSize(aCurrSize);
749 if (nDir == XML_fromT || nDir == XML_fromB)
750 aChildPos.Y += aCurrSize.Height + aChildSize.Height * fSpaceHeight;
751 else
752 aChildPos.X += aCurrSize.Width + aCurrSize.Width * fSpaceWidth;
754 nRowHeight = std::max(nRowHeight, aCurrSize.Height);
756 if (nSecDir == XML_fromT && nIdx % 2 == 1)
758 aChildPos.X = 0;
759 aChildPos.Y += nRowHeight + aChildSize.Height * fSpaceHeight;
760 nRowHeight = 0;
763 nIdx++;
766 break;
769 case XML_lin:
771 // spread children evenly across one axis, stretch across second
773 if (rShape->getChildren().empty() || rShape->getSize().Width == 0 || rShape->getSize().Height == 0)
774 break;
776 const sal_Int32 nDir = maMap.count(XML_linDir) ? maMap.find(XML_linDir)->second : XML_fromL;
777 const sal_Int32 nIncX = nDir==XML_fromL ? 1 : (nDir==XML_fromR ? -1 : 0);
778 const sal_Int32 nIncY = nDir==XML_fromT ? 1 : (nDir==XML_fromB ? -1 : 0);
780 sal_Int32 nCount = rShape->getChildren().size();
781 sal_Int32 nConnectorAngle = 0;
782 switch (nDir)
784 case XML_fromL: nConnectorAngle = 0; break;
785 case XML_fromR: nConnectorAngle = 180; break;
786 case XML_fromT: nConnectorAngle = 270; break;
787 case XML_fromB: nConnectorAngle = 90; break;
790 awt::Size aSpaceSize;
792 // Find out which constraint is relevant for which (internal) name.
793 LayoutPropertyMap aProperties;
794 for (const auto& rConstraint : rConstraints)
796 if (rConstraint.msForName.isEmpty())
797 continue;
799 LayoutProperty& rProperty = aProperties[rConstraint.msForName];
800 if (rConstraint.mnType == XML_w)
801 rProperty[XML_w] = rShape->getSize().Width * rConstraint.mfFactor;
802 if (rConstraint.mnType == XML_h)
803 rProperty[XML_h] = rShape->getSize().Height * rConstraint.mfFactor;
805 // TODO: get values from differently named constraints as well
806 if (rConstraint.msForName == "sp" || rConstraint.msForName == "space" || rConstraint.msForName == "sibTrans")
808 if (rConstraint.mnType == XML_w)
809 aSpaceSize.Width = rShape->getSize().Width * rConstraint.mfFactor;
810 if (rConstraint.mnType == XML_h)
811 aSpaceSize.Height = rShape->getSize().Height * rConstraint.mfFactor;
815 // first approximation of children size
816 awt::Size aChildSize = rShape->getSize();
817 if (nDir == XML_fromL || nDir == XML_fromR)
818 aChildSize.Width /= nCount;
819 else if (nDir == XML_fromT || nDir == XML_fromB)
820 aChildSize.Height /= nCount;
822 awt::Point aCurrPos(0, 0);
823 if (nIncX == -1)
824 aCurrPos.X = rShape->getSize().Width - aChildSize.Width;
825 if (nIncY == -1)
826 aCurrPos.Y = rShape->getSize().Height - aChildSize.Height;
828 // See if children requested more than 100% space in total: scale
829 // down in that case.
830 awt::Size aTotalSize;
831 for (const auto & aCurrShape : rShape->getChildren())
833 oox::OptValue<sal_Int32> oWidth = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);
834 oox::OptValue<sal_Int32> oHeight = findProperty(aProperties, aCurrShape->getInternalName(), XML_h);
835 awt::Size aSize = aChildSize;
836 if (oWidth.has())
837 aSize.Width = oWidth.get();
838 if (oHeight.has())
839 aSize.Height = oHeight.get();
840 aTotalSize.Width += aSize.Width;
841 aTotalSize.Height += aSize.Height;
844 aTotalSize.Width += (nCount-1) * aSpaceSize.Width;
845 aTotalSize.Height += (nCount-1) * aSpaceSize.Height;
847 double fWidthScale = 1.0;
848 double fHeightScale = 1.0;
849 if (nIncX && aTotalSize.Width > rShape->getSize().Width)
850 fWidthScale = static_cast<double>(rShape->getSize().Width) / aTotalSize.Width;
851 if (nIncY && aTotalSize.Height > rShape->getSize().Height)
852 fHeightScale = static_cast<double>(rShape->getSize().Height) / aTotalSize.Height;
853 aSpaceSize.Width *= fWidthScale;
854 aSpaceSize.Height *= fHeightScale;
856 for (auto& aCurrShape : rShape->getChildren())
858 // Extract properties relevant for this shape from constraints.
859 oox::OptValue<sal_Int32> oWidth = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);
860 oox::OptValue<sal_Int32> oHeight = findProperty(aProperties, aCurrShape->getInternalName(), XML_h);
862 awt::Size aSize = aChildSize;
863 if (oWidth.has())
864 aSize.Width = oWidth.get();
865 if (oHeight.has())
866 aSize.Height = oHeight.get();
867 aSize.Width *= fWidthScale;
868 aSize.Height *= fHeightScale;
869 aCurrShape->setSize(aSize);
870 aCurrShape->setChildSize(aSize);
872 // center in the other axis - probably some parameter controls it
873 if (nIncX)
874 aCurrPos.Y = (rShape->getSize().Height - aSize.Height) / 2;
875 if (nIncY)
876 aCurrPos.X = (rShape->getSize().Width - aSize.Width) / 2;
878 aCurrShape->setPosition(aCurrPos);
880 aCurrPos.X += nIncX * (aSize.Width + aSpaceSize.Width);
881 aCurrPos.Y += nIncY * (aSize.Height + aSpaceSize.Height);
883 // connectors should be handled in conn, but we don't have
884 // reference to previous and next child, so it's easier here
885 if (aCurrShape->getSubType() == XML_conn)
886 aCurrShape->setRotation(nConnectorAngle * PER_DEGREE);
888 break;
891 case XML_pyra:
893 if (rShape->getChildren().empty() || rShape->getSize().Width == 0 || rShape->getSize().Height == 0)
894 break;
896 // const sal_Int32 nDir = maMap.count(XML_linDir) ? maMap.find(XML_linDir)->second : XML_fromT;
897 // const sal_Int32 npyraAcctPos = maMap.count(XML_pyraAcctPos) ? maMap.find(XML_pyraAcctPos)->second : XML_bef;
898 // const sal_Int32 ntxDir = maMap.count(XML_txDir) ? maMap.find(XML_txDir)->second : XML_fromT;
899 // const sal_Int32 npyraLvlNode = maMap.count(XML_pyraLvlNode) ? maMap.find(XML_pyraLvlNode)->second : XML_level;
900 // uncomment when use in code.
902 sal_Int32 nCount = rShape->getChildren().size();
903 double fAspectRatio = 0.32;
905 awt::Size aChildSize = rShape->getSize();
906 aChildSize.Width /= nCount;
907 aChildSize.Height /= nCount;
909 awt::Point aCurrPos(0, 0);
910 aCurrPos.X = fAspectRatio*aChildSize.Width*(nCount-1);
911 aCurrPos.Y = fAspectRatio*aChildSize.Height;
913 for (auto & aCurrShape : rShape->getChildren())
915 aCurrShape->setPosition(aCurrPos);
916 aCurrPos.X -= aChildSize.Height/(nCount-1);
917 aChildSize.Width += aChildSize.Height;
918 aCurrShape->setSize(aChildSize);
919 aCurrShape->setChildSize(aChildSize);
920 aCurrPos.Y += (aChildSize.Height);
922 break;
925 case XML_snake:
927 // find optimal grid to layout children that have fixed aspect ratio
929 if (rShape->getChildren().empty() || rShape->getSize().Width == 0 || rShape->getSize().Height == 0)
930 break;
932 // Parse constraints, only self spacing from height as a start.
933 double fSpaceFromConstraint = 0;
934 for (const auto& rConstr : rConstraints)
936 if (rConstr.mnRefType == XML_h)
938 if (rConstr.mnType == XML_sp && rConstr.msForName.isEmpty())
939 fSpaceFromConstraint = rConstr.mfFactor;
942 bool bSpaceFromConstraints = fSpaceFromConstraint != 0;
944 const sal_Int32 nDir = maMap.count(XML_grDir) ? maMap.find(XML_grDir)->second : XML_tL;
945 sal_Int32 nIncX = 1;
946 sal_Int32 nIncY = 1;
947 switch (nDir)
949 case XML_tL: nIncX = 1; nIncY = 1; break;
950 case XML_tR: nIncX = -1; nIncY = 1; break;
951 case XML_bL: nIncX = 1; nIncY = -1; break;
952 case XML_bR: nIncX = -1; nIncY = -1; break;
955 sal_Int32 nCount = rShape->getChildren().size();
956 // Defaults in case not provided by constraints.
957 double fSpace = bSpaceFromConstraints ? fSpaceFromConstraint : 0.3;
958 double fAspectRatio = 0.54; // diagram should not spill outside, earlier it was 0.6
960 sal_Int32 nCol = 1;
961 sal_Int32 nRow = 1;
962 double fChildAspectRatio = rShape->getChildren()[0]->getAspectRatio();
963 if (nCount <= fChildAspectRatio)
964 // Child aspect ratio request (width/height) is N, and we have at most N shapes.
965 // This means we don't need multiple columns.
966 nRow = nCount;
967 else
969 for ( ; nRow<nCount; nRow++)
971 nCol = (nCount+nRow-1) / nRow;
972 const double fShapeHeight = rShape->getSize().Height;
973 const double fShapeWidth = rShape->getSize().Width;
974 if ((fShapeHeight / nCol) / (fShapeWidth / nRow) >= fAspectRatio)
975 break;
979 SAL_INFO("oox.drawingml", "Snake layout grid: " << nCol << "x" << nRow);
981 sal_Int32 nWidth = rShape->getSize().Width / (nCol + (nCol-1)*fSpace);
982 awt::Size aChildSize(nWidth, nWidth * fAspectRatio);
983 if (nCol == 1 && nRow > 1)
985 // We have a single column, so count the height based on the parent height, not
986 // based on width.
987 // Space occurs inside children; also double amount of space is needed outside (on
988 // both sides), if the factor comes from a constraint.
989 sal_Int32 nNumSpaces = -1;
990 if (bSpaceFromConstraints)
991 nNumSpaces += 4;
992 sal_Int32 nHeight
993 = rShape->getSize().Height / (nRow + (nRow + nNumSpaces) * fSpace);
995 if (fChildAspectRatio > 1)
997 // Shrink width if the aspect ratio requires it.
998 nWidth = std::min(rShape->getSize().Width,
999 static_cast<sal_Int32>(nHeight * fChildAspectRatio));
1000 aChildSize = awt::Size(nWidth, nHeight);
1004 awt::Point aCurrPos(0, 0);
1005 if (nIncX == -1)
1006 aCurrPos.X = rShape->getSize().Width - aChildSize.Width;
1007 if (nIncY == -1)
1008 aCurrPos.Y = rShape->getSize().Height - aChildSize.Height;
1009 else if (bSpaceFromConstraints)
1010 // Initial vertical offset to have upper spacing (outside, so double amount).
1011 aCurrPos.Y = aChildSize.Height * fSpace * 2;
1013 sal_Int32 nStartX = aCurrPos.X;
1014 sal_Int32 nColIdx = 0,index = 0;
1016 const sal_Int32 aContDir = maMap.count(XML_contDir) ? maMap.find(XML_contDir)->second : XML_sameDir;
1018 switch(aContDir)
1020 case XML_sameDir:
1021 for (auto & aCurrShape : rShape->getChildren())
1023 aCurrShape->setPosition(aCurrPos);
1024 aCurrShape->setSize(aChildSize);
1025 aCurrShape->setChildSize(aChildSize);
1027 index++; // counts index of child, helpful for positioning.
1029 if(index%nCol==0 || ((index/nCol)+1)!=nRow)
1030 aCurrPos.X += nIncX * (aChildSize.Width + fSpace*aChildSize.Width);
1032 if(++nColIdx == nCol) // condition for next row
1034 // if last row, then position children according to number of shapes.
1035 if((index+1)%nCol!=0 && (index+1)>=3 && ((index+1)/nCol+1)==nRow && nCount!=nRow*nCol)
1036 // position first child of last row
1037 aCurrPos.X = nStartX + (nIncX * (aChildSize.Width + fSpace*aChildSize.Width))/2;
1038 else
1039 // if not last row, positions first child of that row
1040 aCurrPos.X = nStartX;
1041 aCurrPos.Y += nIncY * (aChildSize.Height + fSpace*aChildSize.Height);
1042 nColIdx = 0;
1045 // positions children in the last row.
1046 if(index%nCol!=0 && index>=3 && ((index/nCol)+1)==nRow)
1047 aCurrPos.X += (nIncX * (aChildSize.Width + fSpace*aChildSize.Width));
1049 break;
1050 case XML_revDir:
1051 for (auto & aCurrShape : rShape->getChildren())
1053 aCurrShape->setPosition(aCurrPos);
1054 aCurrShape->setSize(aChildSize);
1055 aCurrShape->setChildSize(aChildSize);
1057 index++; // counts index of child, helpful for positioning.
1060 index%col -> tests node is at last column
1061 ((index/nCol)+1)!=nRow) -> tests node is at last row or not
1062 ((index/nCol)+1)%2!=0 -> tests node is at row which is multiple of 2, important for revDir
1063 num!=nRow*nCol -> tests how last row nodes should be spread.
1066 if((index%nCol==0 || ((index/nCol)+1)!=nRow) && ((index/nCol)+1)%2!=0)
1067 aCurrPos.X += (aChildSize.Width + fSpace*aChildSize.Width);
1068 else if( index%nCol!=0 && ((index/nCol)+1)!=nRow) // child other than placed at last column
1069 aCurrPos.X -= (aChildSize.Width + fSpace*aChildSize.Width);
1071 if(++nColIdx == nCol) // condition for next row
1073 // if last row, then position children according to number of shapes.
1074 if((index+1)%nCol!=0 && (index+1)>=4 && ((index+1)/nCol+1)==nRow && nCount!=nRow*nCol && ((index/nCol)+1)%2==0)
1075 // position first child of last row
1076 aCurrPos.X -= aChildSize.Width*3/2;
1077 else if((index+1)%nCol!=0 && (index+1)>=4 && ((index+1)/nCol+1)==nRow && nCount!=nRow*nCol && ((index/nCol)+1)%2!=0)
1078 aCurrPos.X = nStartX + (nIncX * (aChildSize.Width + fSpace*aChildSize.Width))/2;
1079 else if(((index/nCol)+1)%2!=0)
1080 aCurrPos.X = nStartX;
1082 aCurrPos.Y += nIncY * (aChildSize.Height + fSpace*aChildSize.Height);
1083 nColIdx = 0;
1086 // positions children in the last row.
1087 if(index%nCol!=0 && index>=3 && ((index/nCol)+1)==nRow && ((index/nCol)+1)%2==0)
1088 //if row%2=0 then start from left else
1089 aCurrPos.X -= (nIncX * (aChildSize.Width + fSpace*aChildSize.Width));
1090 else if(index%nCol!=0 && index>=3 && ((index/nCol)+1)==nRow && ((index/nCol)+1)%2!=0)
1091 // start from right
1092 aCurrPos.X += (nIncX * (aChildSize.Width + fSpace*aChildSize.Width));
1094 break;
1096 break;
1099 case XML_sp:
1101 // HACK: Handled one level higher. Or rather, planned to
1102 // HACK: text should appear only in tx node; we're assigning it earlier, so let's remove it here
1103 rShape->setTextBody(TextBodyPtr());
1104 break;
1107 case XML_tx:
1109 // adjust text alignment
1111 // Parse constraints, only self margins as a start.
1112 double fFontSize = 0;
1113 for (const auto& rConstr : rConstraints)
1115 if (rConstr.mnRefType == XML_w)
1117 if (!rConstr.msForName.isEmpty())
1118 continue;
1120 sal_Int32 nProperty = getPropertyFromConstraint(rConstr.mnType);
1121 if (!nProperty)
1122 continue;
1124 // PowerPoint takes size as points, but gives margin as MMs.
1125 double fFactor = convertPointToMms(rConstr.mfFactor);
1127 // DrawingML works in EMUs, UNO API works in MM100s.
1128 sal_Int32 nValue = rShape->getSize().Width * fFactor / EMU_PER_HMM;
1130 rShape->getShapeProperties().setProperty(nProperty, nValue);
1132 if (rConstr.mnType == XML_primFontSz)
1133 fFontSize = rConstr.mfValue;
1136 TextBodyPtr pTextBody = rShape->getTextBody();
1137 if (!pTextBody || pTextBody->isEmpty())
1138 break;
1140 // adjust text size to fit shape
1141 if (fFontSize != 0)
1143 for (auto& aParagraph : pTextBody->getParagraphs())
1144 for (auto& aRun : aParagraph->getRuns())
1145 if (!aRun->getTextCharacterProperties().moHeight.has())
1146 aRun->getTextCharacterProperties().moHeight = fFontSize * 100;
1148 pTextBody->getTextProperties().maPropertyMap.setProperty(PROP_TextFitToSize, drawing::TextFitToSizeType_AUTOFIT);
1150 // ECMA-376-1:2016 21.4.7.5 ST_AutoTextRotation (Auto Text Rotation)
1151 const sal_Int32 nautoTxRot = maMap.count(XML_autoTxRot) ? maMap.find(XML_autoTxRot)->second : XML_upr;
1152 sal_Int32 nShapeRot = rShape->getRotation();
1153 while (nShapeRot < 0)
1154 nShapeRot += 360 * PER_DEGREE;
1155 while (nShapeRot > 360 * PER_DEGREE)
1156 nShapeRot -= 360 * PER_DEGREE;
1158 switch(nautoTxRot)
1160 case XML_upr:
1162 int n90x = 0;
1163 if (nShapeRot >= 315 * PER_DEGREE)
1164 /* keep 0 */;
1165 else if (nShapeRot > 225 * PER_DEGREE)
1166 n90x = -3;
1167 else if (nShapeRot >= 135 * PER_DEGREE)
1168 n90x = -2;
1169 else if (nShapeRot > 45 * PER_DEGREE)
1170 n90x = -1;
1171 pTextBody->getTextProperties().moRotation = n90x * 90 * PER_DEGREE;
1173 break;
1174 case XML_grav:
1176 if (nShapeRot > (90 * PER_DEGREE) && nShapeRot < (270 * PER_DEGREE))
1177 pTextBody->getTextProperties().moRotation = -180 * PER_DEGREE;
1179 break;
1180 case XML_none:
1181 break;
1184 const sal_Int32 atxAnchorVert = maMap.count(XML_txAnchorVert) ? maMap.find(XML_txAnchorVert)->second : XML_mid;
1186 switch(atxAnchorVert)
1188 case XML_t:
1189 pTextBody->getTextProperties().meVA = css::drawing::TextVerticalAdjust_TOP;
1190 break;
1191 case XML_b:
1192 pTextBody->getTextProperties().meVA = css::drawing::TextVerticalAdjust_BOTTOM;
1193 break;
1194 case XML_mid:
1195 // text centered vertically by default
1196 default:
1197 pTextBody->getTextProperties().meVA = css::drawing::TextVerticalAdjust_CENTER;
1198 break;
1201 pTextBody->getTextProperties().maPropertyMap.setProperty(PROP_TextVerticalAdjust, pTextBody->getTextProperties().meVA);
1203 // normalize list level
1204 sal_Int32 nBaseLevel = pTextBody->getParagraphs().front()->getProperties().getLevel();
1205 for (auto & aParagraph : pTextBody->getParagraphs())
1207 if (aParagraph->getProperties().getLevel() < nBaseLevel)
1208 nBaseLevel = aParagraph->getProperties().getLevel();
1211 // Start bullets at:
1212 // 1 - top level
1213 // 2 - with children (default)
1214 int nStartBulletsAtLevel = 2;
1215 ParamMap::const_iterator aBulletLvl = maMap.find(XML_stBulletLvl);
1216 if (aBulletLvl != maMap.end())
1217 nStartBulletsAtLevel = aBulletLvl->second;
1218 nStartBulletsAtLevel--;
1220 bool isBulletList = false;
1221 for (auto & aParagraph : pTextBody->getParagraphs())
1223 sal_Int32 nLevel = aParagraph->getProperties().getLevel() - nBaseLevel;
1224 aParagraph->getProperties().setLevel(nLevel);
1225 if (nLevel >= nStartBulletsAtLevel)
1227 // It is not possible to change the bullet style for text.
1228 sal_Int32 nLeftMargin = 285750 * (nLevel - nStartBulletsAtLevel + 1) / EMU_PER_HMM;
1229 aParagraph->getProperties().getParaLeftMargin() = nLeftMargin;
1230 aParagraph->getProperties().getFirstLineIndentation() = -285750 / EMU_PER_HMM;
1231 OUString aBulletChar = OUString::fromUtf8(u8"•");
1232 aParagraph->getProperties().getBulletList().setBulletChar(aBulletChar);
1233 aParagraph->getProperties().getBulletList().setSuffixNone();
1234 isBulletList = true;
1238 // explicit alignment
1239 ParamMap::const_iterator aDir = maMap.find(XML_parTxLTRAlign);
1240 // TODO: XML_parTxRTLAlign
1241 if (aDir != maMap.end())
1243 css::style::ParagraphAdjust aAlignment = GetParaAdjust(aDir->second);
1244 for (auto & aParagraph : pTextBody->getParagraphs())
1245 aParagraph->getProperties().setParaAdjust(aAlignment);
1247 else if (!isBulletList)
1249 // if not list use default alignment - centered
1250 for (auto & aParagraph : pTextBody->getParagraphs())
1251 aParagraph->getProperties().setParaAdjust(css::style::ParagraphAdjust::ParagraphAdjust_CENTER);
1253 break;
1256 default:
1257 break;
1260 SAL_INFO(
1261 "oox.drawingml",
1262 "Layouting shape " << rShape->getInternalName() << ", alg type: " << mnType << ", ("
1263 << rShape->getPosition().X << "," << rShape->getPosition().Y << ","
1264 << rShape->getSize().Width << "," << rShape->getSize().Height << ")");
1267 void LayoutNode::accept( LayoutAtomVisitor& rVisitor )
1269 rVisitor.visit(*this);
1272 bool LayoutNode::setupShape( const ShapePtr& rShape, const dgm::Point* pPresNode ) const
1274 SAL_INFO(
1275 "oox.drawingml",
1276 "Filling content from layout node named \"" << msName
1277 << "\", modelId \"" << pPresNode->msModelId << "\"");
1279 // have the presentation node - now, need the actual data node:
1280 const DiagramData::StringMap::const_iterator aNodeName = mrDgm.getData()->getPresOfNameMap().find(
1281 pPresNode->msModelId);
1282 if( aNodeName != mrDgm.getData()->getPresOfNameMap().end() )
1284 // Calculate the depth of what is effectively the topmost element.
1285 sal_Int32 nMinDepth = std::numeric_limits<sal_Int32>::max();
1286 for (const auto& rPair : aNodeName->second)
1288 if (rPair.second.mnDepth < nMinDepth)
1289 nMinDepth = rPair.second.mnDepth;
1292 for (const auto& rPair : aNodeName->second)
1294 const DiagramData::SourceIdAndDepth& rItem = rPair.second;
1295 DiagramData::PointNameMap& rMap = mrDgm.getData()->getPointNameMap();
1296 // pPresNode is the presentation node of the aDataNode2 data node.
1297 DiagramData::PointNameMap::const_iterator aDataNode2 = rMap.find(rItem.msSourceId);
1298 if (aDataNode2 == rMap.end())
1300 //busted, skip it
1301 continue;
1304 if (!aDataNode2->second->mpShape)
1306 //busted, skip it
1307 continue;
1310 rShape->setDataNodeType(aDataNode2->second->mnType);
1312 if (rItem.mnDepth == 0)
1314 // grab shape attr from topmost element(s)
1315 rShape->getShapeProperties() = aDataNode2->second->mpShape->getShapeProperties();
1316 rShape->getLineProperties() = aDataNode2->second->mpShape->getLineProperties();
1317 rShape->getFillProperties() = aDataNode2->second->mpShape->getFillProperties();
1318 rShape->getCustomShapeProperties() = aDataNode2->second->mpShape->getCustomShapeProperties();
1319 rShape->setMasterTextListStyle( aDataNode2->second->mpShape->getMasterTextListStyle() );
1321 SAL_INFO(
1322 "oox.drawingml",
1323 "Custom shape with preset type "
1324 << (rShape->getCustomShapeProperties()
1325 ->getShapePresetType())
1326 << " added for layout node named \"" << msName
1327 << "\"");
1329 else if (rItem.mnDepth == nMinDepth)
1331 // If no real topmost element, then take properties from the one that's the closest
1332 // to topmost.
1333 rShape->getLineProperties() = aDataNode2->second->mpShape->getLineProperties();
1334 rShape->getFillProperties() = aDataNode2->second->mpShape->getFillProperties();
1337 // append text with right outline level
1338 if( aDataNode2->second->mpShape->getTextBody() &&
1339 !aDataNode2->second->mpShape->getTextBody()->getParagraphs().empty() &&
1340 !aDataNode2->second->mpShape->getTextBody()->getParagraphs().front()->getRuns().empty() )
1342 TextBodyPtr pTextBody=rShape->getTextBody();
1343 if( !pTextBody )
1345 pTextBody.reset( new TextBody() );
1347 // also copy text attrs
1348 pTextBody->getTextListStyle() =
1349 aDataNode2->second->mpShape->getTextBody()->getTextListStyle();
1350 pTextBody->getTextProperties() =
1351 aDataNode2->second->mpShape->getTextBody()->getTextProperties();
1353 rShape->setTextBody(pTextBody);
1356 const TextParagraphVector& rSourceParagraphs
1357 = aDataNode2->second->mpShape->getTextBody()->getParagraphs();
1358 for (const auto& pSourceParagraph : rSourceParagraphs)
1360 TextParagraph& rPara = pTextBody->addParagraph();
1361 if (rItem.mnDepth != -1)
1362 rPara.getProperties().setLevel(rItem.mnDepth);
1364 for (const auto& pRun : pSourceParagraph->getRuns())
1365 rPara.addRun(pRun);
1366 const TextBodyPtr& rBody = aDataNode2->second->mpShape->getTextBody();
1367 rPara.getProperties().apply(rBody->getParagraphs().front()->getProperties());
1372 else
1374 SAL_INFO(
1375 "oox.drawingml",
1376 "ShapeCreationVisitor::visit: no data node name found while"
1377 " processing shape type "
1378 << rShape->getCustomShapeProperties()->getShapePresetType()
1379 << " for layout node named \"" << msName << "\"");
1380 if (pPresNode->mpShape)
1381 rShape->getFillProperties().assignUsed(pPresNode->mpShape->getFillProperties());
1384 // TODO(Q1): apply styling & coloring - take presentation
1385 // point's presStyleLbl for both style & color
1386 // if not found use layout node's styleLbl
1387 // however, docs are a bit unclear on this
1388 OUString aStyleLabel = pPresNode->msPresentationLayoutStyleLabel;
1389 if (aStyleLabel.isEmpty())
1390 aStyleLabel = msStyleLabel;
1391 if( !aStyleLabel.isEmpty() )
1393 const DiagramQStyleMap::const_iterator aStyle = mrDgm.getStyles().find(aStyleLabel);
1394 if( aStyle != mrDgm.getStyles().end() )
1396 const DiagramStyle& rStyle = aStyle->second;
1397 rShape->getShapeStyleRefs()[XML_fillRef] = rStyle.maFillStyle;
1398 rShape->getShapeStyleRefs()[XML_lnRef] = rStyle.maLineStyle;
1399 rShape->getShapeStyleRefs()[XML_effectRef] = rStyle.maEffectStyle;
1400 rShape->getShapeStyleRefs()[XML_fontRef] = rStyle.maTextStyle;
1402 else
1404 SAL_WARN("oox.drawingml", "Style " << aStyleLabel << " not found");
1407 const DiagramColorMap::const_iterator aColor = mrDgm.getColors().find(aStyleLabel);
1408 if( aColor != mrDgm.getColors().end() )
1410 const DiagramColor& rColor=aColor->second;
1411 if( rColor.maFillColor.isUsed() )
1412 rShape->getShapeStyleRefs()[XML_fillRef].maPhClr = rColor.maFillColor;
1413 if( rColor.maLineColor.isUsed() )
1414 rShape->getShapeStyleRefs()[XML_lnRef].maPhClr = rColor.maLineColor;
1415 if( rColor.maEffectColor.isUsed() )
1416 rShape->getShapeStyleRefs()[XML_effectRef].maPhClr = rColor.maEffectColor;
1417 if( rColor.maTextFillColor.isUsed() )
1418 rShape->getShapeStyleRefs()[XML_fontRef].maPhClr = rColor.maTextFillColor;
1422 // even if no data node found, successful anyway. it's
1423 // contained at the layoutnode
1424 return true;
1427 const LayoutNode* LayoutNode::getParentLayoutNode() const
1429 for (LayoutAtomPtr pAtom = getParent(); pAtom; pAtom = pAtom->getParent())
1431 auto pLayoutNode = dynamic_cast<LayoutNode*>(pAtom.get());
1432 if (pLayoutNode)
1433 return pLayoutNode;
1436 return nullptr;
1439 void ShapeAtom::accept( LayoutAtomVisitor& rVisitor )
1441 rVisitor.visit(*this);
1446 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */