/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * 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/.
*/
// These shapes have no gluepoints defined in their mso_CustomShape struct, thus the gluepoint // adaption to default gluepoints will be done. Other shapes having no gluepoint defined in the // mso_CustomShape struct, have gluepoints in order top-left-bottom-right in OOXML. But the shapes // below have order right-bottom-left-top. Adding gluepoints to mso_CustomShape structs does not // solve the problem because MS binary gluepoints and OOXML gluepoints are different.
if (pConnector->getConnectorName() == u"bentConnector2"_ustr
|| pConnector->getConnectorName() == u"curvedConnector2"_ustr) return; // These have no handles.
// Convert string attribute to number. Set default 50000 if missing.
std::vector<sal_Int32> aAdjustmentOOXVec; // 1/100000 of shape size for (size_t i = 0; i < 3; i++)
{ if (i < pConnector->getConnectorAdjustments().size())
aAdjustmentOOXVec.push_back(pConnector->getConnectorAdjustments()[i].toInt32()); else
aAdjustmentOOXVec.push_back(50000);
}
// Handle positions depend on EdgeKind and ShapeSize. bendConnector and curvedConnector use the // same handle positions. The formulas here correspond to guides in the bendConnector in // presetShapeDefinitions.xml. constdouble fWidth = pConnector->getSize().Width; // EMU constdouble fHeight = pConnector->getSize().Height; // EMU constdouble fPosX = pConnector->getPosition().X; // EMU constdouble fPosY = pConnector->getPosition().Y; // EMU
// The presetGeometry has the first segment horizontal and start point left/top with // coordinates (0|0). Other layouts are done by flipping and rotating.
basegfx::B2DHomMatrix aTransform; const basegfx::B2DPoint aB2DCenter(fWidth / 2.0, fHeight / 2.0);
aTransform.translate(-aB2DCenter);
aTransform *= getConnectorTransformMatrix(pConnector);
aTransform.translate(aB2DCenter);
// Make coordinates absolute
aTransform.translate(fPosX, fPosY);
// Actually transform the handle coordinates for (auto& rElem : rHandlePositions)
rElem *= aTransform;
void ConnectorHelper::getLOBentHandlePositionsHmm(const oox::drawingml::ShapePtr& pConnector,
std::vector<basegfx::B2DPoint>& rHandlePositions)
{ // This method is intended for Edgekind css::drawing::ConnectorType_STANDARD. Those connectors // correspond to OOX bentConnector, aka "ElbowConnector".
rHandlePositions.clear();
if (!pConnector) return;
uno::Reference<drawing::XShape> xConnector(pConnector->getXShape()); if (!xConnector.is()) return;
// Get the EdgeTrack polygon. We cannot use UNO "PolyPolygonBezier" because that includes // the yet not known anchor position in Writer. Thus get the polygon directly from the object.
SdrEdgeObj* pEdgeObj = dynamic_cast<SdrEdgeObj*>(SdrObject::getSdrObjectFromXShape(xConnector)); if (!pEdgeObj) return;
basegfx::B2DPolyPolygon aB2DPolyPolygon(pEdgeObj->GetEdgeTrackPath()); if (aB2DPolyPolygon.count() == 0) return;
// We need Hmm, the polygon might be e.g. in Twips, in Writer for example
MapUnit eMapUnit = pEdgeObj->getSdrModelFromSdrObject().GetItemPool().GetMetric(0); if (eMapUnit != MapUnit::Map100thMM)
{ constauto eFrom = MapToO3tlLength(eMapUnit); if (eFrom == o3tl::Length::invalid) return; constdouble fConvert(o3tl::convert(1.0, eFrom, o3tl::Length::mm100));
aEdgePolygon.transform(basegfx::B2DHomMatrix(fConvert, 0.0, 0.0, 0.0, fConvert, 0.0));
}
// LO has the handle in the middle of a segment, but not for first and last segment. for (sal_uInt32 i = 1; i < aEdgePolygon.count() - 2; i++)
{ const basegfx::B2DPoint aBeforePt(aEdgePolygon.getB2DPoint(i)); const basegfx::B2DPoint aAfterPt(aEdgePolygon.getB2DPoint(i + 1));
rHandlePositions.push_back((aBeforePt + aAfterPt) / 2.0);
}
}
void ConnectorHelper::getLOCurvedHandlePositionsHmm( const oox::drawingml::ShapePtr& pConnector, std::vector<basegfx::B2DPoint>& rHandlePositions)
{ // This method is intended for Edgekind css::drawing::ConnectorType_Curve for which OoXML // compatible routing is enabled.
rHandlePositions.clear();
if (!pConnector) return;
uno::Reference<drawing::XShape> xConnector(pConnector->getXShape()); if (!xConnector.is()) return;
// Get the EdgeTrack polygon. We cannot use UNO "PolyPolygonBezier" because that includes // the yet not known anchor position in Writer. Thus get the polygon directly from the object.
SdrEdgeObj* pEdgeObj = dynamic_cast<SdrEdgeObj*>(SdrObject::getSdrObjectFromXShape(xConnector)); if (!pEdgeObj) return;
basegfx::B2DPolyPolygon aB2DPolyPolygon(pEdgeObj->GetEdgeTrackPath()); if (aB2DPolyPolygon.count() == 0) return;
// We need Hmm, the polygon might be e.g. in Twips, in Writer for example
MapUnit eMapUnit = pEdgeObj->getSdrModelFromSdrObject().GetItemPool().GetMetric(0); if (eMapUnit != MapUnit::Map100thMM)
{ constauto eFrom = MapToO3tlLength(eMapUnit); if (eFrom == o3tl::Length::invalid) return; constdouble fConvert(o3tl::convert(1.0, eFrom, o3tl::Length::mm100));
aEdgePolygon.transform(basegfx::B2DHomMatrix(fConvert, 0.0, 0.0, 0.0, fConvert, 0.0));
}
// The OOXML compatible routing has the handles as polygon points, but not start or // end point. for (sal_uInt32 i = 1; i < aEdgePolygon.count() - 1; i++)
{
rHandlePositions.push_back(aEdgePolygon.getB2DPoint(i));
}
}
void ConnectorHelper::applyConnections(const oox::drawingml::ShapePtr& pConnector,
oox::drawingml::ShapeIdMap& rShapeMap)
{
uno::Reference<drawing::XShape> xConnector(pConnector->getXShape()); if (!xConnector.is()) return;
uno::Reference<beans::XPropertySet> xPropSet(xConnector, uno::UNO_QUERY); if (!xPropSet.is()) return;
// MS Office allows route between shapes with small distance. LO default is 5mm.
xPropSet->setPropertyValue(u"EdgeNode1HorzDist"_ustr, uno::Any(sal_Int32(0)));
xPropSet->setPropertyValue(u"EdgeNode1VertDist"_ustr, uno::Any(sal_Int32(0)));
xPropSet->setPropertyValue(u"EdgeNode2HorzDist"_ustr, uno::Any(sal_Int32(0)));
xPropSet->setPropertyValue(u"EdgeNode2VertDist"_ustr, uno::Any(sal_Int32(0)));
// A OOXML curvedConnector uses a routing method which is basically incompatible with the // traditional way of LibreOffice. A compatible way was added and needs to be enabled before // connections are set, so that the method is used in the default routing.
xPropSet->setPropertyValue(u"EdgeOOXMLCurve"_ustr, uno::Any(true));
oox::drawingml::ConnectorShapePropertiesList aConnectorShapeProperties
= pConnector->getConnectorShapeProperties(); // It contains maximal two items, each a struct with mbStartShape, maDestShapeId, mnDestGlueId for (constauto& aIt : aConnectorShapeProperties)
{ constauto pItem = rShapeMap.find(aIt.maDestShapeId); if (pItem == rShapeMap.end()) continue;
uno::Reference<drawing::XShape> xShape(pItem->second->getXShape(), uno::UNO_QUERY); if (xShape.is())
{ // Connect to the found shape. if (aIt.mbStartShape)
xPropSet->setPropertyValue(u"StartShape"_ustr, uno::Any(xShape)); else
xPropSet->setPropertyValue(u"EndShape"_ustr, uno::Any(xShape));
// The first four glue points are the default glue points, which are set by LibreOffice. // They do not belong to the preset geometry of the shape. // Adapt gluepoint index to LibreOffice
uno::Reference<drawing::XGluePointsSupplier> xSupplier(xShape, uno::UNO_QUERY);
css::uno::Reference<css::container::XIdentifierContainer> xGluePoints(
xSupplier->getGluePoints(), uno::UNO_QUERY);
sal_Int32 nCountGluePoints = xGluePoints->getIdentifiers().getLength();
sal_Int32 nGlueId = aIt.mnDestGlueId;
if (nCountGluePoints > 4)
nGlueId += 4; else
{ // In these cases the mso_CustomShape struct defines no gluepoints (Why not?), thus // our default gluepoints are used. The order of the default gluepoints might differ // from the order of the OOXML gluepoints. We try to change nGlueId so, that the // connector attaches to a default gluepoint at the same side as it attaches in OOXML. const OUString sShapeType
= pItem->second->getCustomShapeProperties()->getShapePresetTypeName(); if (ConnectorHelper::hasClockwiseCxn(sShapeType))
nGlueId = (nGlueId + 1) % 4; else
{ bool bFlipH = pItem->second->getFlipH(); bool bFlipV = pItem->second->getFlipV(); if (bFlipH == bFlipV)
{ // change id of the left and right glue points of the bounding box (1 <-> 3) if (nGlueId == 1)
nGlueId = 3; // Right elseif (nGlueId == 3)
nGlueId = 1; // Left
}
}
}
if (aIt.mbStartShape)
xPropSet->setPropertyValue(u"StartGluePointIndex"_ustr, uno::Any(nGlueId)); else
xPropSet->setPropertyValue(u"EndGluePointIndex"_ustr, uno::Any(nGlueId));
}
}
}
void ConnectorHelper::applyBentHandleAdjustments(oox::drawingml::ShapePtr pConnector)
{
uno::Reference<drawing::XShape> xConnector(pConnector->getXShape(), uno::UNO_QUERY); if (!xConnector.is()) return;
uno::Reference<beans::XPropertySet> xPropSet(xConnector, uno::UNO_QUERY); if (!xPropSet.is()) return;
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.