1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "diagramlayoutatoms.hxx"
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
;
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
;
64 * Determines if nUnit is a font unit (measured in points) or not (measured in
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
)
78 return oox::PROP_TextLeftDistance
;
80 return oox::PROP_TextRightDistance
;
82 return oox::PROP_TextUpperDistance
;
84 return oox::PROP_TextLowerDistance
;
91 * Determines if pShape is (or contains) a presentation of a data node of type
94 bool containsDataNodeType(const oox::drawingml::ShapePtr
& pShape
, sal_Int32 nType
)
96 if (pShape
->getDataNodeType() == nType
)
99 for (const auto& pChild
: pShape
->getChildren())
101 if (containsDataNodeType(pChild
, nType
))
109 namespace oox
{ namespace drawingml
{
111 IteratorAttr::IteratorAttr( )
113 , mbHideLastTrans( true )
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()
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
;
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
),
192 maIter
.loadFromXAttr( xAttributes
);
193 maCond
.loadFromXAttr( xAttributes
);
196 bool ConditionAtom::compareResult(sal_Int32 nOperator
, sal_Int32 nFirst
, sal_Int32 nSecond
)
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
;
207 SAL_WARN("oox.drawingml", "unsupported operator: " << nOperator
);
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
)
226 if (bSourceToDestination
)
228 if (rConnection
.msSourceId
== rFrom
)
229 return rConnection
.msDestId
;
233 if (rConnection
.msDestId
== rFrom
)
234 return rConnection
.msSourceId
;
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);
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
)
271 bool ConditionAtom::getDecision(const dgm::Point
* pPresPoint
) const
278 switch (maCond
.mnFunc
)
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
);
309 return compareResult(maCond
.mnOp
, getNodeCount(pPresPoint
), maCond
.msVal
.toInt32());
313 sal_Int32 nMaxDepth
= calcMaxDepth(pPresPoint
->msPresentationAssociationId
, mrLayoutNode
.getDiagram().getData()->getConnections());
314 return compareResult(maCond
.mnOp
, nMaxDepth
, maCond
.msVal
.toInt32());
324 SAL_WARN("oox.drawingml", "unknown function " << maCond
.mnFunc
);
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.
347 switch (maConstraint
.mnType
)
354 bRequireForName
= false;
359 if (bRequireForName
&& maConstraint
.msForName
.isEmpty())
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
)
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;
426 for (const ShapePtr
& pChild
: rShape
->getChildren())
427 nCount
= std::max(nCount
, pChild
->getVerticalShapesCount());
433 void AlgAtom::layoutShape( const ShapePtr
& rShape
,
434 const std::vector
<Constraint
>& rConstraints
)
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
;
452 rParent
[XML_r
] = rShape
->getSize().Width
;
453 rParent
[XML_b
] = rShape
->getSize().Height
;
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
;
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
;
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
))
483 fUnitFactor
= EMU_PER_PT
;
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() )
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())
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
);
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
);
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());
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
;
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
;
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
);
599 if (rShape
->getChildren().empty())
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();
630 const sal_Int32 nConnectorRadius
= nRadius
* cos(basegfx::deg2rad(nSpanAngle
/ nShapes
));
631 const sal_Int32 nConnectorAngle
= nSpanAngle
> 0 ? 0 : 180;
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
);
669 if (rShape
->getChildren().empty() || rShape
->getSize().Width
== 0 || rShape
->getSize().Height
== 0)
672 // hierRoot is the manager -> employees vertical linear path,
673 // hierChild is the first employee -> last employee horizontal
675 sal_Int32 nDir
= XML_fromL
;
676 if (mnType
== XML_hierRoot
)
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
);
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
);
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
;
752 aChildPos
.X
+= aCurrSize
.Width
+ aCurrSize
.Width
* fSpaceWidth
;
754 nRowHeight
= std::max(nRowHeight
, aCurrSize
.Height
);
756 if (nSecDir
== XML_fromT
&& nIdx
% 2 == 1)
759 aChildPos
.Y
+= nRowHeight
+ aChildSize
.Height
* fSpaceHeight
;
771 // spread children evenly across one axis, stretch across second
773 if (rShape
->getChildren().empty() || rShape
->getSize().Width
== 0 || rShape
->getSize().Height
== 0)
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;
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())
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);
824 aCurrPos
.X
= rShape
->getSize().Width
- aChildSize
.Width
;
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
;
837 aSize
.Width
= oWidth
.get();
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
;
864 aSize
.Width
= oWidth
.get();
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
874 aCurrPos
.Y
= (rShape
->getSize().Height
- aSize
.Height
) / 2;
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
);
893 if (rShape
->getChildren().empty() || rShape
->getSize().Width
== 0 || rShape
->getSize().Height
== 0)
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
);
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)
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
;
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
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.
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
)
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
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
)
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);
1006 aCurrPos
.X
= rShape
->getSize().Width
- aChildSize
.Width
;
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
;
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;
1039 // if not last row, positions first child of that row
1040 aCurrPos
.X
= nStartX
;
1041 aCurrPos
.Y
+= nIncY
* (aChildSize
.Height
+ fSpace
*aChildSize
.Height
);
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
));
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
);
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)
1092 aCurrPos
.X
+= (nIncX
* (aChildSize
.Width
+ fSpace
*aChildSize
.Width
));
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());
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())
1120 sal_Int32 nProperty
= getPropertyFromConstraint(rConstr
.mnType
);
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())
1140 // adjust text size to fit shape
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
;
1163 if (nShapeRot
>= 315 * PER_DEGREE
)
1165 else if (nShapeRot
> 225 * PER_DEGREE
)
1167 else if (nShapeRot
>= 135 * PER_DEGREE
)
1169 else if (nShapeRot
> 45 * PER_DEGREE
)
1171 pTextBody
->getTextProperties().moRotation
= n90x
* 90 * PER_DEGREE
;
1176 if (nShapeRot
> (90 * PER_DEGREE
) && nShapeRot
< (270 * PER_DEGREE
))
1177 pTextBody
->getTextProperties().moRotation
= -180 * PER_DEGREE
;
1184 const sal_Int32 atxAnchorVert
= maMap
.count(XML_txAnchorVert
) ? maMap
.find(XML_txAnchorVert
)->second
: XML_mid
;
1186 switch(atxAnchorVert
)
1189 pTextBody
->getTextProperties().meVA
= css::drawing::TextVerticalAdjust_TOP
;
1192 pTextBody
->getTextProperties().meVA
= css::drawing::TextVerticalAdjust_BOTTOM
;
1195 // text centered vertically by default
1197 pTextBody
->getTextProperties().meVA
= css::drawing::TextVerticalAdjust_CENTER
;
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:
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
);
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
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())
1304 if (!aDataNode2
->second
->mpShape
)
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() );
1323 "Custom shape with preset type "
1324 << (rShape
->getCustomShapeProperties()
1325 ->getShapePresetType())
1326 << " added for layout node named \"" << msName
1329 else if (rItem
.mnDepth
== nMinDepth
)
1331 // If no real topmost element, then take properties from the one that's the closest
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();
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())
1366 const TextBodyPtr
& rBody
= aDataNode2
->second
->mpShape
->getTextBody();
1367 rPara
.getProperties().apply(rBody
->getParagraphs().front()->getProperties());
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
;
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
1427 const LayoutNode
* LayoutNode::getParentLayoutNode() const
1429 for (LayoutAtomPtr pAtom
= getParent(); pAtom
; pAtom
= pAtom
->getParent())
1431 auto pLayoutNode
= dynamic_cast<LayoutNode
*>(pAtom
.get());
1439 void ShapeAtom::accept( LayoutAtomVisitor
& rVisitor
)
1441 rVisitor
.visit(*this);
1446 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */