/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
namespace
{ /// Looks up the value of the rInternalName -> nProperty key in rProperties.
std::optional<sal_Int32> findProperty(const oox::drawingml::LayoutPropertyMap& rProperties, const OUString& rInternalName, sal_Int32 nProperty)
{
std::optional<sal_Int32> oRet;
auto it = rProperties.find(rInternalName); if (it != rProperties.end())
{ const oox::drawingml::LayoutProperty& rProperty = it->second; auto itProperty = rProperty.find(nProperty); if (itProperty != rProperty.end())
oRet = itProperty->second;
}
return oRet;
}
/** * Determines if nUnit is a font unit (measured in points) or not (measured in * millimeters).
*/ bool isFontUnit(sal_Int32 nUnit)
{ return nUnit == oox::XML_primFontSz || nUnit == oox::XML_secFontSz;
}
/// Determines which UNO property should be set for a given constraint type.
sal_Int32 getPropertyFromConstraint(sal_Int32 nConstraint)
{ switch (nConstraint)
{ case oox::XML_lMarg: return oox::PROP_TextLeftDistance; case oox::XML_rMarg: return oox::PROP_TextRightDistance; case oox::XML_tMarg: return oox::PROP_TextUpperDistance; case oox::XML_bMarg: return oox::PROP_TextLowerDistance;
}
return 0;
}
/** * Determines if pShape is (or contains) a presentation of a data node of type * nType.
*/ bool containsDataNodeType(const oox::drawingml::ShapePtr& pShape, sal_Int32 nType)
{ if (pShape->getDataNodeType() == nType) returntrue;
for (constauto& pChild : pShape->getChildren())
{ if (containsDataNodeType(pChild, nType)) returntrue;
}
// Parse constraints. double fChildAspectRatio = rShape->getChildren()[0]->getAspectRatio(); double fShapeHeight = rShape->getSize().Height; double fShapeWidth = rShape->getSize().Width; // Check if we have a child aspect ratio. If so, need to shrink one dimension to // achieve that ratio. if (fChildAspectRatio && fShapeHeight && fChildAspectRatio < (fShapeWidth / fShapeHeight))
{
fShapeWidth = fShapeHeight * fChildAspectRatio;
}
std::vector<sal_Int32> aShapeWidths(rShape->getChildren().size()); for (size_t i = 0; i < rShape->getChildren().size(); ++i)
{
ShapePtr pChild = rShape->getChildren()[i]; if (!pChild->getDataNodeType())
{ // TODO handle the case when the requirement applies by name, not by point type.
aShapeWidths[i] = fShapeWidth; continue;
}
auto itNodeType = aPropertiesByType.find(pChild->getDataNodeType()); if (itNodeType == aPropertiesByType.end())
{
aShapeWidths[i] = fShapeWidth; continue;
}
auto it = itNodeType->second.find(XML_w); if (it == itNodeType->second.end())
{
aShapeWidths[i] = fShapeWidth; continue;
}
sal_Int32 nCount = rShape->getChildren().size(); // Defaults in case not provided by constraints. double fSpace = bSpaceFromConstraints ? fSpaceFromConstraint : 0.3; double fAspectRatio = 0.54; // diagram should not spill outside, earlier it was 0.6
sal_Int32 nCol = 1;
sal_Int32 nRow = 1;
sal_Int32 nMaxRowWidth = 0; if (nCount <= fChildAspectRatio) // Child aspect ratio request (width/height) is N, and we have at most N shapes. // This means we don't need multiple columns.
nRow = nCount; else
{ for (; nRow < nCount; nRow++)
{
nCol = std::ceil(static_cast<double>(nCount) / nRow);
sal_Int32 nRowWidth = 0; for (sal_Int32 i = 0; i < nCol; ++i)
{ if (i >= nCount)
{ break;
}
sal_Int32 nWidth = rShape->getSize().Width / (nCol + (nCol - 1) * fSpace);
awt::Size aChildSize(nWidth, nWidth * fAspectRatio); if (nCol == 1 && nRow > 1)
{ // We have a single column, so count the height based on the parent height, not // based on width. // Space occurs inside children; also double amount of space is needed outside (on // both sides), if the factor comes from a constraint.
sal_Int32 nNumSpaces = -1; if (bSpaceFromConstraints)
nNumSpaces += 4;
sal_Int32 nHeight = rShape->getSize().Height / (nRow + (nRow + nNumSpaces) * fSpace);
if (fChildAspectRatio > 1)
{ // Shrink width if the aspect ratio requires it.
nWidth = std::min(rShape->getSize().Width, static_cast<sal_Int32>(nHeight * fChildAspectRatio));
aChildSize = awt::Size(nWidth, nHeight);
}
bHorizontal = false;
}
awt::Point aCurrPos(0, 0); if (nIncX == -1)
aCurrPos.X = rShape->getSize().Width - aChildSize.Width; if (nIncY == -1)
aCurrPos.Y = rShape->getSize().Height - aChildSize.Height; elseif (bSpaceFromConstraints)
{ if (!bHorizontal)
{ // Initial vertical offset to have upper spacing (outside, so double amount).
aCurrPos.Y = aChildSize.Height * fSpace * 2;
}
}
switch (aContDir)
{ case XML_sameDir:
{
sal_Int32 nRowHeight = 0; for (auto& aCurrShape : rShape->getChildren())
{
aCurrShape->setPosition(aCurrPos);
awt::Size aCurrSize(aChildSize); // aShapeWidths items are a portion of nMaxRowWidth. We want the same ratio, // based on the original parent width, ignoring the aspect ratio request. bool bWidthsFromConstraints
= nCount >= 2 && rShape->getChildren()[1]->getDataNodeType() == XML_sibTrans; if (bWidthsFromConstraints && nMaxRowWidth)
{ double fWidthFactor = static_cast<double>(aShapeWidths[index]) / nMaxRowWidth; // We can only work from constraints if spacing is represented by a real // child shape.
aCurrSize.Width = rShape->getSize().Width * fWidthFactor;
} if (fChildAspectRatio)
{
aCurrSize.Height = aCurrSize.Width / fChildAspectRatio;
// Child shapes are not allowed to leave their parent.
aCurrSize.Height = std::min<sal_Int32>(
aCurrSize.Height, rShape->getSize().Height / (nRow + (nRow - 1) * fSpace));
} if (aCurrSize.Height > nRowHeight)
{
nRowHeight = aCurrSize.Height;
}
aCurrShape->setSize(aCurrSize);
aCurrShape->setChildSize(aCurrSize);
index++; // counts index of child, helpful for positioning.
if (++nColIdx == nCol) // condition for next row
{ // if last row, then position children according to number of shapes. if ((index + 1) % nCol != 0 && (index + 1) >= 3
&& ((index + 1) / nCol + 1) == nRow && nCount != nRow * nCol)
{ // position first child of last row if (bWidthsFromConstraints)
{
aCurrPos.X = nStartX;
} else
{ // Can assume that all child shape has the same width.
aCurrPos.X
= nStartX
+ (nIncX * (aCurrSize.Width + fSpace * aCurrSize.Width)) / 2;
}
} else // if not last row, positions first child of that row
aCurrPos.X = nStartX;
aCurrPos.Y += nIncY * (nRowHeight + fSpace * nRowHeight);
nColIdx = 0;
nRowHeight = 0;
}
// positions children in the last row. if (index % nCol != 0 && index >= 3 && ((index / nCol) + 1) == nRow)
aCurrPos.X += (nIncX * (aCurrSize.Width + fSpace * aCurrSize.Width));
} break;
} case XML_revDir: for (auto& aCurrShape : rShape->getChildren())
{
aCurrShape->setPosition(aCurrPos);
aCurrShape->setSize(aChildSize);
aCurrShape->setChildSize(aChildSize);
index++; // counts index of child, helpful for positioning.
/* index%col -> tests node is at last column ((index/nCol)+1)!=nRow) -> tests node is at last row or not ((index/nCol)+1)%2!=0 -> tests node is at row which is multiple of 2, important for revDir num!=nRow*nCol -> tests how last row nodes should be spread.
*/
bool CompositeAlg::inferFromLayoutProperty(const LayoutProperty& rMap, sal_Int32 nRefType,
sal_Int32& rValue)
{ switch (nRefType)
{ case XML_r:
{ auto it = rMap.find(XML_l); if (it == rMap.end())
{ returnfalse;
}
sal_Int32 nLeft = it->second;
it = rMap.find(XML_w); if (it == rMap.end())
{ returnfalse;
}
rValue = nLeft + it->second; returntrue;
} default: break;
}
returnfalse;
}
void CompositeAlg::applyConstraintToLayout(const Constraint& rConstraint,
LayoutPropertyMap& rProperties)
{ // TODO handle the case when we have ptType="...", not forName="...". if (rConstraint.msForName.isEmpty())
{ return;
}
const LayoutPropertyMap::const_iterator aRef = rProperties.find(rConstraint.msRefForName); if (aRef == rProperties.end()) return;
const LayoutProperty::const_iterator aRefType = aRef->second.find(rConstraint.mnRefType);
sal_Int32 nInferredValue = 0; if (aRefType != aRef->second.end())
{ // Reference is found directly.
rProperties[rConstraint.msForName][rConstraint.mnType]
= aRefType->second * rConstraint.mfFactor;
} elseif (inferFromLayoutProperty(aRef->second, rConstraint.mnRefType, nInferredValue))
{ // Reference can be inferred.
rProperties[rConstraint.msForName][rConstraint.mnType]
= nInferredValue * rConstraint.mfFactor;
} else
{ // Reference not found, assume a fixed value. // Values are never in EMU, while oox::drawingml::Shape position and size are always in // EMU. constdouble fValue = o3tl::convert(rConstraint.mfValue,
isFontUnit(rConstraint.mnRefType) ? o3tl::Length::pt
: o3tl::Length::mm,
o3tl::Length::emu);
rProperties[rConstraint.msForName][rConstraint.mnType] = fValue;
}
}
// Track min/max vertical positions, so we can center everything at the end, if needed.
sal_Int32 nVertMin = std::numeric_limits<sal_Int32>::max();
sal_Int32 nVertMax = 0;
if (rAlg.getAspectRatio() != 1.0)
{
rParent[XML_w] = rShape->getSize().Width;
rParent[XML_h] = rShape->getSize().Height;
rParent[XML_l] = 0;
rParent[XML_t] = 0;
rParent[XML_r] = rShape->getSize().Width;
rParent[XML_b] = rShape->getSize().Height;
} else
{ // Shrink width to be only as large as height.
rParent[XML_w] = std::min(rShape->getSize().Width, rShape->getSize().Height);
rParent[XML_h] = rShape->getSize().Height; if (rParent[XML_w] < rShape->getSize().Width)
nParentXOffset = (rShape->getSize().Width - rParent[XML_w]) / 2;
rParent[XML_l] = nParentXOffset;
rParent[XML_t] = 0;
rParent[XML_r] = rShape->getSize().Width - rParent[XML_l];
rParent[XML_b] = rShape->getSize().Height;
}
for (constauto& rConstr : rConstraints)
{ // Apply direct constraints for all layout nodes.
applyConstraintToLayout(rConstr, aProperties);
}
for (auto& aCurrShape : rShape->getChildren())
{ // Apply constraints from the current layout node for this child shape. // Previous child shapes may have changed aProperties. for (constauto& rConstr : rConstraints)
{ if (rConstr.msForName != aCurrShape->getInternalName())
{ continue;
}
applyConstraintToLayout(rConstr, aProperties);
}
// Apply constraints from the child layout node for this child shape. // This builds on top of the own parent state + the state of previous shapes in the // same composite algorithm. const LayoutNode& rLayoutNode = rAlg.getLayoutNode(); for (constauto& pDirectChild : rLayoutNode.getChildren())
{ auto pLayoutNode = dynamic_cast<LayoutNode*>(pDirectChild.get()); if (!pLayoutNode)
{ continue;
}
if (pLayoutNode->getName() != aCurrShape->getInternalName())
{ continue;
}
for (constauto& pChild : pLayoutNode->getChildren())
{ auto pConstraintAtom = dynamic_cast<ConstraintAtom*>(pChild.get()); if (!pConstraintAtom)
{ continue;
}
const Constraint& rConstraint = pConstraintAtom->getConstraint(); if (!rConstraint.msForName.isEmpty())
{ continue;
}
if (!rConstraint.msRefForName.isEmpty())
{ continue;
}
// Either an absolute value or a factor of a property. if (rConstraint.mfValue == 0.0 && rConstraint.mnRefType == XML_none)
{ continue;
}
NamedShapePairs& rDiagramFontHeights
= rAlg.getLayoutNode().getDiagram().getDiagramFontHeights(); auto it = rDiagramFontHeights.find(aCurrShape->getInternalName()); if (it != rDiagramFontHeights.end())
{ // Internal name matches: put drawingml::Shape to the relevant group, for // synchronized font height handling.
it->second.insert({ aCurrShape, {} });
}
}
// See if all vertical space is used or we have to center the content. if (!(nVertMin >= 0 && nVertMin <= nVertMax && nVertMax <= rParent[XML_h])) return;
bool ConditionAtom::compareResult(sal_Int32 nOperator, sal_Int32 nFirst, sal_Int32 nSecond)
{ switch (nOperator)
{ case XML_equ: return nFirst == nSecond; case XML_gt: return nFirst > nSecond; case XML_gte: return nFirst >= nSecond; case XML_lt: return nFirst < nSecond; case XML_lte: return nFirst <= nSecond; case XML_neq: return nFirst != nSecond; default:
SAL_WARN("oox.drawingml", "unsupported operator: " << nOperator); returnfalse;
}
}
namespace
{ /** * Takes the connection list from rLayoutNode, navigates from rFrom on an edge * of type nType, using a direction determined by bSourceToDestination.
*/
OUString navigate(LayoutNode& rLayoutNode, svx::diagram::TypeConstant nType, std::u16string_view rFrom, bool bSourceToDestination)
{ for (constauto& rConnection : rLayoutNode.getDiagram().getData()->getConnections())
{ if (rConnection.mnXMLType != nType) continue;
if (bSourceToDestination)
{ if (rConnection.msSourceId == rFrom) return rConnection.msDestId;
} else
{ if (rConnection.msDestId == rFrom) return rConnection.msSourceId;
}
}
// HACK: special case - count children of first child if (maIter.maAxis.size() == 2 && maIter.maAxis[0] == XML_ch && maIter.maAxis[1] == XML_ch)
sNodeId = navigate(mrLayoutNode, svx::diagram::TypeConstant::XML_parOf, sNodeId, /*bSourceToDestination*/ true);
if (!sNodeId.isEmpty())
{ for (constauto& aCxn : mrLayoutNode.getDiagram().getData()->getConnections()) if (aCxn.mnXMLType == svx::diagram::TypeConstant::XML_parOf && aCxn.msSourceId == sNodeId)
nCount++;
}
return nCount;
}
bool ConditionAtom::getDecision(const svx::diagram::Point* pPresPoint) const
{ if (mIsElse) returntrue; if (!pPresPoint) returnfalse;
switch (maCond.mnFunc)
{ case XML_var:
{ if (maCond.mnArg == XML_dir) return compareResult(maCond.mnOp, pPresPoint->mnDirection, maCond.mnVal); elseif (maCond.mnArg == XML_hierBranch)
{
sal_Int32 nHierarchyBranch = pPresPoint->moHierarchyBranch.value_or(XML_std); if (!pPresPoint->moHierarchyBranch.has_value())
{ // If <dgm:hierBranch> is missing in the current presentation // point, ask the parent.
OUString aParent = navigate(mrLayoutNode, svx::diagram::TypeConstant::XML_presParOf, pPresPoint->msModelId, /*bSourceToDestination*/ false);
DiagramData::PointNameMap& rPointNameMap
= mrLayoutNode.getDiagram().getData()->getPointNameMap(); auto it = rPointNameMap.find(aParent); if (it != rPointNameMap.end())
{ const svx::diagram::Point* pParent = it->second; if (pParent->moHierarchyBranch.has_value())
nHierarchyBranch = pParent->moHierarchyBranch.value();
}
} return compareResult(maCond.mnOp, nHierarchyBranch, maCond.mnVal);
} break;
}
case XML_cnt: return compareResult(maCond.mnOp, getNodeCount(pPresPoint), maCond.msVal.toInt32());
case XML_depth: case XML_pos: case XML_revPos: case XML_posEven: case XML_posOdd: // TODO default:
SAL_WARN("oox.drawingml", "unknown function " << maCond.mnFunc); break;
}
void ConstraintAtom::parseConstraint(std::vector<Constraint>& rConstraints, bool bRequireForName) const
{ // Allowlist for cases where empty forName is handled. if (bRequireForName)
{ switch (maConstraint.mnType)
{ case XML_sp: case XML_lMarg: case XML_rMarg: case XML_tMarg: case XML_bMarg:
bRequireForName = false; break;
} switch (maConstraint.mnPointType)
{ case XML_sibTrans:
bRequireForName = false; break;
}
}
if (bRequireForName && maConstraint.msForName.isEmpty()) return;
sal_Int32 AlgAtom::getConnectorType()
{
sal_Int32 nConnRout = 0;
sal_Int32 nBegSty = 0;
sal_Int32 nEndSty = 0; if (maMap.count(oox::XML_connRout))
nConnRout = maMap.find(oox::XML_connRout)->second; if (maMap.count(oox::XML_begSty))
nBegSty = maMap.find(oox::XML_begSty)->second; if (maMap.count(oox::XML_endSty))
nEndSty = maMap.find(oox::XML_endSty)->second;
if (nConnRout == oox::XML_bend) return 0; // was oox::XML_bentConnector3 - connectors are hidden in org chart as they don't work anyway if (nBegSty == oox::XML_arr && nEndSty == oox::XML_arr) return oox::XML_leftRightArrow; if (nBegSty == oox::XML_arr) return oox::XML_leftArrow; if (nEndSty == oox::XML_arr) return oox::XML_rightArrow;
namespace
{ /// Does the first data node of this shape have customized text properties? bool HasCustomText(const ShapePtr& rShape, LayoutNode& rLayoutNode)
{ const PresPointShapeMap& rPresPointShapeMap
= rLayoutNode.getDiagram().getLayout()->getPresPointShapeMap(); const DiagramData::StringMap& rPresOfNameMap
= rLayoutNode.getDiagram().getData()->getPresOfNameMap(); const DiagramData::PointNameMap& rPointNameMap
= rLayoutNode.getDiagram().getData()->getPointNameMap(); // Get the first presentation node of the shape. const svx::diagram::Point* pPresNode = nullptr; for (constauto& rPair : rPresPointShapeMap)
{ if (rPair.second == rShape)
{
pPresNode = rPair.first; break;
}
} // Get the first data node of the presentation node.
svx::diagram::Point* pDataNode = nullptr; if (pPresNode)
{ auto itPresToData = rPresOfNameMap.find(pPresNode->msModelId); if (itPresToData != rPresOfNameMap.end())
{ for (constauto& rPair : itPresToData->second)
{ const DiagramData::SourceIdAndDepth& rItem = rPair.second; auto it = rPointNameMap.find(rItem.msSourceId); if (it != rPointNameMap.end())
{
pDataNode = it->second; break;
}
}
}
}
// If we have a data node, see if its text is customized or not. if (pDataNode)
{ return pDataNode->mbCustomText;
}
returnfalse;
}
}
void AlgAtom::layoutShape(const ShapePtr& rShape, const std::vector<Constraint>& rConstraints, const std::vector<Rule>& rRules)
{ if (mnType != XML_lin)
{ // TODO Handle spacing from constraints for non-lin algorithms as well.
std::erase_if(
rShape->getChildren(),
[](const ShapePtr& aChild) { return aChild->getServiceName() == "com.sun.star.drawing.GroupShape"
&& aChild->getChildren().empty();
});
}
switch(mnType)
{ case XML_composite:
{
CompositeAlg::layoutShapeChildren(*this, rShape, rConstraints); break;
}
case XML_conn:
{ if (rShape->getSubType() == XML_conn)
{ // There is no shape type "conn", replace it by an arrow based // on the direction of the parent linear layout.
sal_Int32 nType = getConnectorType();
if (nRotationPath == XML_alongPath)
aCurrShape->setRotation(fAngle * PER_DEGREE);
// connectors should be handled in conn, but we don't have // reference to previous and next child, so it's easier here if (aCurrShape->getSubType() == XML_conn)
aCurrShape->setRotation((nConnectorAngle + fAngle) * PER_DEGREE);
idx++;
}
} break;
}
case XML_hierChild: case XML_hierRoot:
{ if (rShape->getChildren().empty() || rShape->getSize().Width == 0 || rShape->getSize().Height == 0) break;
// hierRoot is the manager -> employees vertical linear path, // hierChild is the first employee -> last employee horizontal // linear path.
sal_Int32 nDir = XML_fromL; if (mnType == XML_hierRoot)
nDir = XML_fromT; elseif (maMap.count(XML_linDir))
nDir = maMap.find(XML_linDir)->second;
if (mnType == XML_hierChild && pChild->getSubType() == XML_conn)
{ // Connectors should not influence the position of // non-connect shapes.
pChild->setSize(aConnectorSize);
pChild->setChildSize(aConnectorSize); continue;
}
double fCount = rShape->getChildren().size();
sal_Int32 nConnectorAngle = 0; switch (nDir)
{ case XML_fromL: nConnectorAngle = 0; break; case XML_fromR: nConnectorAngle = 180; break; case XML_fromT: nConnectorAngle = 270; break; case XML_fromB: nConnectorAngle = 90; break;
}
awt::Size aSpaceSize;
// Find out which constraint is relevant for which (internal) name.
LayoutPropertyMap aProperties; for (constauto& rConstraint : rConstraints)
{ if (rConstraint.msForName.isEmpty()) continue;
if (rConstraint.mnType == XML_primFontSz && rConstraint.mnFor == XML_des
&& rConstraint.mnOperator == XML_equ)
{
NamedShapePairs& rDiagramFontHeights
= getLayoutNode().getDiagram().getDiagramFontHeights(); auto it = rDiagramFontHeights.find(rConstraint.msForName); if (it == rDiagramFontHeights.end())
{ // Start tracking all shapes with this internal name: they'll have the same // font height.
rDiagramFontHeights[rConstraint.msForName] = {};
}
}
// TODO: get values from differently named constraints as well if (rConstraint.msForName == "sp" || rConstraint.msForName == "space" || rConstraint.msForName == "sibTrans")
{ if (rConstraint.mnType == XML_w)
aSpaceSize.Width = rShape->getSize().Width * rConstraint.mfFactor; if (rConstraint.mnType == XML_h)
aSpaceSize.Height = rShape->getSize().Height * rConstraint.mfFactor;
}
}
// first approximation of children size
std::set<OUString> aChildrenToShrink; for (constauto& rRule : rRules)
{ // Consider rules: when scaling down, only change children where the rule allows // doing so.
aChildrenToShrink.insert(rRule.msForName);
}
if (nDir == XML_fromT || nDir == XML_fromB)
{ // TODO consider rules for vertical linear layout as well.
aChildrenToShrink.clear();
}
if (!aChildrenToShrink.empty())
{ // Have scaling info from rules: then only count scaled children. // Also count children which are a fraction of a scaled child.
std::set<OUString> aChildrenToShrinkDeps; for (auto& aCurrShape : rShape->getChildren())
{ if (aChildrenToShrink.find(aCurrShape->getInternalName())
== aChildrenToShrink.end())
{ if (fCount > 1.0)
{
fCount -= 1.0;
if (aChildrenToShrink.find(rConstraint.msRefForName) == aChildrenToShrink.end())
{ continue;
}
// At this point we have a child with a size which is a factor of an // other child which will be scaled.
fCount += rConstraint.mfFactor;
aChildrenToShrinkDeps.insert(aCurrShape->getInternalName());
bIsDependency = true; break;
}
// See if children requested more than 100% space in total: scale // down in that case.
awt::Size aTotalSize; for (constauto & aCurrShape : rShape->getChildren())
{
std::optional<sal_Int32> oWidth = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);
std::optional<sal_Int32> oHeight = findProperty(aProperties, aCurrShape->getInternalName(), XML_h);
awt::Size aSize = aChildSize; if (oWidth.has_value())
aSize.Width = oWidth.value(); if (oHeight.has_value())
aSize.Height = oHeight.value();
aTotalSize.Width += aSize.Width;
aTotalSize.Height += aSize.Height;
}
for (auto& aCurrShape : rShape->getChildren())
{ // Extract properties relevant for this shape from constraints.
std::optional<sal_Int32> oWidth = findProperty(aProperties, aCurrShape->getInternalName(), XML_w);
std::optional<sal_Int32> oHeight = findProperty(aProperties, aCurrShape->getInternalName(), XML_h);
awt::Size aSize = aChildSize; if (oWidth.has_value())
aSize.Width = oWidth.value(); if (oHeight.has_value())
aSize.Height = oHeight.value(); if (aChildrenToShrink.empty()
|| aChildrenToShrink.find(aCurrShape->getInternalName())
!= aChildrenToShrink.end())
{
aSize.Width *= fWidthScale;
} if (aChildrenToShrink.empty()
|| aChildrenToShrink.find(aCurrShape->getInternalName())
!= aChildrenToShrink.end())
{
aSize.Height *= fHeightScale;
}
aCurrShape->setSize(aSize);
aCurrShape->setChildSize(aSize);
// center in the other axis - probably some parameter controls it if (nIncX)
aCurrPos.Y = (rShape->getSize().Height - aSize.Height) / 2; if (nIncY)
aCurrPos.X = (rShape->getSize().Width - aSize.Width) / 2; if (aCurrPos.X < 0)
{
aCurrPos.X = 0;
} if (aCurrPos.Y < 0)
{
aCurrPos.Y = 0;
}
// connectors should be handled in conn, but we don't have // reference to previous and next child, so it's easier here if (aCurrShape->getSubType() == XML_conn)
aCurrShape->setRotation(nConnectorAngle * PER_DEGREE);
}
// Newer shapes are behind older ones by default. Reverse this if requested.
sal_Int32 nChildOrder = XML_b; const LayoutNode* pParentLayoutNode = nullptr; for (LayoutAtomPtr pAtom = getParent(); pAtom; pAtom = pAtom->getParent())
{ auto pLayoutNode = dynamic_cast<LayoutNode*>(pAtom.get()); if (pLayoutNode)
{
pParentLayoutNode = pLayoutNode; break;
}
} if (pParentLayoutNode)
{
nChildOrder = pParentLayoutNode->getChildOrder();
} if (nChildOrder == XML_t)
{
std::reverse(rShape->getChildren().begin(), rShape->getChildren().end());
}
break;
}
case XML_pyra:
{
PyraAlg::layoutShapeChildren(rShape); break;
}
case XML_snake:
{
SnakeAlg::layoutShapeChildren(*this, rShape, rConstraints); break;
}
case XML_sp:
{ // HACK: Handled one level higher. Or rather, planned to // HACK: text should appear only in tx node; we're assigning it earlier, so let's remove it here
rShape->setTextBody(TextBodyPtr()); break;
}
case XML_tx:
{ // adjust text alignment
// Parse constraints, only self margins as a start. double fFontSize = 0; for (constauto& rConstr : rConstraints)
{ if (rConstr.mnRefType == XML_w)
{ if (!rConstr.msForName.isEmpty()) continue;
sal_Int32 nProperty = getPropertyFromConstraint(rConstr.mnType); if (!nProperty) continue;
// PowerPoint takes size as points, but gives margin as MMs. double fFactor = convertPointToMms(rConstr.mfFactor);
// DrawingML works in EMUs, UNO API works in MM100s.
sal_Int32 nValue = o3tl::convert(rShape->getSize().Width * fFactor,
o3tl::Length::emu, o3tl::Length::mm100);
TextBodyPtr pTextBody = rShape->getTextBody(); if (!pTextBody || pTextBody->isEmpty()) break;
// adjust text size to fit shape if (fFontSize != 0)
{ for (auto& aParagraph : pTextBody->getParagraphs()) for (auto& aRun : aParagraph->getRuns()) if (!aRun->getTextCharacterProperties().moHeight.has_value())
aRun->getTextCharacterProperties().moHeight = fFontSize * 100;
}
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.19Angebot
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.