/src/libreoffice/oox/source/drawingml/connectorhelper.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | */ |
9 | | |
10 | | #include <drawingml/connectorhelper.hxx> |
11 | | |
12 | | #include <sal/config.h> |
13 | | |
14 | | #include <com/sun/star/beans/XPropertySet.hpp> |
15 | | #include <com/sun/star/container/XIdentifierContainer.hpp> |
16 | | #include <com/sun/star/drawing/XGluePointsSupplier.hpp> |
17 | | |
18 | | #include <basegfx/curve/b2dcubicbezier.hxx> |
19 | | #include <basegfx/matrix/b2dhommatrix.hxx> |
20 | | #include <basegfx/point/b2dpoint.hxx> |
21 | | #include <basegfx/polygon/b2dpolypolygontools.hxx> |
22 | | #include <basegfx/vector/b2dvector.hxx> |
23 | | #include <drawingml/customshapeproperties.hxx> |
24 | | #include <oox/drawingml/drawingmltypes.hxx> |
25 | | #include <oox/drawingml/shape.hxx> |
26 | | #include <rtl/ustring.hxx> |
27 | | #include <svl/itempool.hxx> |
28 | | #include <svx/svdmodel.hxx> |
29 | | #include <svx/svdoedge.hxx> |
30 | | #include <svx/svdobj.hxx> |
31 | | #include <tools/mapunit.hxx> |
32 | | #include <tools/UnitConversion.hxx> |
33 | | |
34 | | #include <map> |
35 | | #include <set> |
36 | | #include <string_view> |
37 | | #include <vector> |
38 | | |
39 | | using namespace ::com::sun::star; |
40 | | |
41 | | // These shapes have no gluepoints defined in their mso_CustomShape struct, thus the gluepoint |
42 | | // adaption to default gluepoints will be done. Other shapes having no gluepoint defined in the |
43 | | // mso_CustomShape struct, have gluepoints in order top-left-bottom-right in OOXML. But the shapes |
44 | | // below have order right-bottom-left-top. Adding gluepoints to mso_CustomShape structs does not |
45 | | // solve the problem because MS binary gluepoints and OOXML gluepoints are different. |
46 | | |
47 | | bool ConnectorHelper::hasClockwiseCxn(const OUString& rShapeType) |
48 | 26 | { |
49 | 26 | static const std::set<OUString> aWithClockwiseCxnSet({ u"accentBorderCallout1"_ustr, |
50 | 26 | u"accentBorderCallout2"_ustr, |
51 | 26 | u"accentBorderCallout3"_ustr, |
52 | 26 | u"accentCallout1"_ustr, |
53 | 26 | u"accentCallout2"_ustr, |
54 | 26 | u"accentCallout3"_ustr, |
55 | 26 | u"actionButtonBackPrevious"_ustr, |
56 | 26 | u"actionButtonBeginning"_ustr, |
57 | 26 | u"actionButtonBlank"_ustr, |
58 | 26 | u"actionButtonDocument"_ustr, |
59 | 26 | u"actionButtonEnd"_ustr, |
60 | 26 | u"actionButtonForwardNext"_ustr, |
61 | 26 | u"actionButtonHelp"_ustr, |
62 | 26 | u"actionButtonHome"_ustr, |
63 | 26 | u"actionButtonInformation"_ustr, |
64 | 26 | u"actionButtonMovie"_ustr, |
65 | 26 | u"actionButtonReturn"_ustr, |
66 | 26 | u"actionButtonSound"_ustr, |
67 | 26 | u"borderCallout1"_ustr, |
68 | 26 | u"borderCallout2"_ustr, |
69 | 26 | u"borderCallout3"_ustr, |
70 | 26 | u"callout1"_ustr, |
71 | 26 | u"callout2"_ustr, |
72 | 26 | u"callout3"_ustr, |
73 | 26 | u"cloud"_ustr, |
74 | 26 | u"corner"_ustr, |
75 | 26 | u"diagStripe"_ustr, |
76 | 26 | u"flowChartOfflineStorage"_ustr, |
77 | 26 | u"halfFrame"_ustr, |
78 | 26 | u"mathDivide"_ustr, |
79 | 26 | u"mathMinus"_ustr, |
80 | 26 | u"mathPlus"_ustr, |
81 | 26 | u"nonIsoscelesTrapezoid"_ustr, |
82 | 26 | u"pie"_ustr, |
83 | 26 | u"round2DiagRect"_ustr, |
84 | 26 | u"round2SameRect"_ustr, |
85 | 26 | u"snip1Rect"_ustr, |
86 | 26 | u"snip2DiagRect"_ustr, |
87 | 26 | u"snip2SameRect"_ustr, |
88 | 26 | u"snipRoundRect"_ustr }); |
89 | 26 | return aWithClockwiseCxnSet.contains(rShapeType); |
90 | 26 | } |
91 | | |
92 | | basegfx::B2DHomMatrix |
93 | | ConnectorHelper::getConnectorTransformMatrix(const oox::drawingml::ShapePtr& pConnector) |
94 | 0 | { |
95 | 0 | basegfx::B2DHomMatrix aTransform; // ctor generates unit matrix |
96 | 0 | if (!pConnector) |
97 | 0 | return aTransform; |
98 | 0 | if (pConnector->getFlipH()) |
99 | 0 | aTransform.scale(-1.0, 1.0); |
100 | 0 | if (pConnector->getFlipV()) |
101 | 0 | aTransform.scale(1.0, -1.0); |
102 | 0 | if (pConnector->getRotation() == 0) |
103 | 0 | return aTransform; |
104 | | |
105 | 0 | if (pConnector->getRotation() == 5400000 || pConnector->getRotation() == -16200000) |
106 | 0 | aTransform *= basegfx::B2DHomMatrix(0, -1, 0, 1, 0, 0); |
107 | 0 | else if (pConnector->getRotation() == 10800000 || pConnector->getRotation() == -10800000) |
108 | 0 | aTransform *= basegfx::B2DHomMatrix(-1, 0, 0, 0, -1, 0); |
109 | 0 | else if (pConnector->getRotation() == 16200000 || pConnector->getRotation() == -5400000) |
110 | 0 | aTransform *= basegfx::B2DHomMatrix(0, 1, 0, -1, 0, 0); |
111 | 0 | else |
112 | 0 | SAL_WARN("oox", "tdf#157888 LibreOffice cannot handle such connector rotation"); |
113 | 0 | return aTransform; |
114 | 0 | } |
115 | | |
116 | | void ConnectorHelper::getOOXHandlePositionsHmm(const oox::drawingml::ShapePtr& pConnector, |
117 | | std::vector<basegfx::B2DPoint>& rHandlePositions) |
118 | 0 | { |
119 | 0 | rHandlePositions.clear(); |
120 | |
|
121 | 0 | if (!pConnector) |
122 | 0 | return; |
123 | | |
124 | 0 | if (pConnector->getConnectorName() == u"bentConnector2"_ustr |
125 | 0 | || pConnector->getConnectorName() == u"curvedConnector2"_ustr) |
126 | 0 | return; // These have no handles. |
127 | | |
128 | | // Convert string attribute to number. Set default 50000 if missing. |
129 | 0 | std::vector<sal_Int32> aAdjustmentOOXVec; // 1/100000 of shape size |
130 | 0 | for (size_t i = 0; i < 3; i++) |
131 | 0 | { |
132 | 0 | if (i < pConnector->getConnectorAdjustments().size()) |
133 | 0 | aAdjustmentOOXVec.push_back(pConnector->getConnectorAdjustments()[i].toInt32()); |
134 | 0 | else |
135 | 0 | aAdjustmentOOXVec.push_back(50000); |
136 | 0 | } |
137 | | |
138 | | // Handle positions depend on EdgeKind and ShapeSize. bendConnector and curvedConnector use the |
139 | | // same handle positions. The formulas here correspond to guides in the bendConnector in |
140 | | // presetShapeDefinitions.xml. |
141 | 0 | const double fWidth = pConnector->getSize().Width; // EMU |
142 | 0 | const double fHeight = pConnector->getSize().Height; // EMU |
143 | 0 | const double fPosX = pConnector->getPosition().X; // EMU |
144 | 0 | const double fPosY = pConnector->getPosition().Y; // EMU |
145 | |
|
146 | 0 | if (pConnector->getConnectorName() == u"bentConnector3"_ustr |
147 | 0 | || pConnector->getConnectorName() == u"curvedConnector3"_ustr) |
148 | 0 | { |
149 | 0 | double fAdj1 = aAdjustmentOOXVec[0]; |
150 | 0 | double fX1 = fAdj1 / 100000.0 * fWidth; |
151 | 0 | double fY1 = fHeight / 2.0; |
152 | 0 | rHandlePositions.push_back({ fX1, fY1 }); |
153 | 0 | } |
154 | 0 | else if (pConnector->getConnectorName() == u"bentConnector4"_ustr |
155 | 0 | || pConnector->getConnectorName() == u"curvedConnector4"_ustr) |
156 | 0 | { |
157 | 0 | double fAdj1 = aAdjustmentOOXVec[0]; |
158 | 0 | double fAdj2 = aAdjustmentOOXVec[1]; |
159 | 0 | double fX1 = fAdj1 / 100000.0 * fWidth; |
160 | 0 | double fX2 = (fX1 + fWidth) / 2.0; |
161 | 0 | double fY2 = fAdj2 / 100000.0 * fHeight; |
162 | 0 | double fY1 = fY2 / 2.0; |
163 | 0 | rHandlePositions.push_back({ fX1, fY1 }); |
164 | 0 | rHandlePositions.push_back({ fX2, fY2 }); |
165 | 0 | } |
166 | 0 | else if (pConnector->getConnectorName() == u"bentConnector5"_ustr |
167 | 0 | || pConnector->getConnectorName() == u"curvedConnector5"_ustr) |
168 | 0 | { |
169 | 0 | double fAdj1 = aAdjustmentOOXVec[0]; |
170 | 0 | double fAdj2 = aAdjustmentOOXVec[1]; |
171 | 0 | double fAdj3 = aAdjustmentOOXVec[2]; |
172 | 0 | double fX1 = fAdj1 / 100000.0 * fWidth; |
173 | 0 | double fX3 = fAdj3 / 100000.0 * fWidth; |
174 | 0 | double fX2 = (fX1 + fX3) / 2.0; |
175 | 0 | double fY2 = fAdj2 / 100000.0 * fHeight; |
176 | 0 | double fY1 = fY2 / 2.0; |
177 | 0 | double fY3 = (fHeight + fY2) / 2.0; |
178 | 0 | rHandlePositions.push_back({ fX1, fY1 }); |
179 | 0 | rHandlePositions.push_back({ fX2, fY2 }); |
180 | 0 | rHandlePositions.push_back({ fX3, fY3 }); |
181 | 0 | } |
182 | | |
183 | | // The presetGeometry has the first segment horizontal and start point left/top with |
184 | | // coordinates (0|0). Other layouts are done by flipping and rotating. |
185 | 0 | basegfx::B2DHomMatrix aTransform; |
186 | 0 | const basegfx::B2DPoint aB2DCenter(fWidth / 2.0, fHeight / 2.0); |
187 | 0 | aTransform.translate(-aB2DCenter); |
188 | 0 | aTransform *= getConnectorTransformMatrix(pConnector); |
189 | 0 | aTransform.translate(aB2DCenter); |
190 | | |
191 | | // Make coordinates absolute |
192 | 0 | aTransform.translate(fPosX, fPosY); |
193 | | |
194 | | // Actually transform the handle coordinates |
195 | 0 | for (auto& rElem : rHandlePositions) |
196 | 0 | rElem *= aTransform; |
197 | | |
198 | | // Convert EMU -> Hmm |
199 | 0 | for (auto& rElem : rHandlePositions) |
200 | 0 | rElem /= 360.0; |
201 | 0 | } |
202 | | |
203 | | void ConnectorHelper::getLOBentHandlePositionsHmm(const oox::drawingml::ShapePtr& pConnector, |
204 | | std::vector<basegfx::B2DPoint>& rHandlePositions) |
205 | 0 | { |
206 | | // This method is intended for Edgekind css::drawing::ConnectorType_STANDARD. Those connectors |
207 | | // correspond to OOX bentConnector, aka "ElbowConnector". |
208 | 0 | rHandlePositions.clear(); |
209 | |
|
210 | 0 | if (!pConnector) |
211 | 0 | return; |
212 | 0 | uno::Reference<drawing::XShape> xConnector(pConnector->getXShape()); |
213 | 0 | if (!xConnector.is()) |
214 | 0 | return; |
215 | | |
216 | | // Get the EdgeTrack polygon. We cannot use UNO "PolyPolygonBezier" because that includes |
217 | | // the yet not known anchor position in Writer. Thus get the polygon directly from the object. |
218 | 0 | SdrEdgeObj* pEdgeObj = dynamic_cast<SdrEdgeObj*>(SdrObject::getSdrObjectFromXShape(xConnector)); |
219 | 0 | if (!pEdgeObj) |
220 | 0 | return; |
221 | 0 | basegfx::B2DPolyPolygon aB2DPolyPolygon(pEdgeObj->GetEdgeTrackPath()); |
222 | 0 | if (aB2DPolyPolygon.count() == 0) |
223 | 0 | return; |
224 | | |
225 | 0 | basegfx::B2DPolygon aEdgePolygon = aB2DPolyPolygon.getB2DPolygon(0); |
226 | 0 | if (aEdgePolygon.count() < 4 || aEdgePolygon.areControlPointsUsed()) |
227 | 0 | return; |
228 | | |
229 | | // We need Hmm, the polygon might be e.g. in Twips, in Writer for example |
230 | 0 | MapUnit eMapUnit = pEdgeObj->getSdrModelFromSdrObject().GetItemPool().GetMetric(0); |
231 | 0 | if (eMapUnit != MapUnit::Map100thMM) |
232 | 0 | { |
233 | 0 | const auto eFrom = MapToO3tlLength(eMapUnit); |
234 | 0 | if (eFrom == o3tl::Length::invalid) |
235 | 0 | return; |
236 | 0 | const double fConvert(o3tl::convert(1.0, eFrom, o3tl::Length::mm100)); |
237 | 0 | aEdgePolygon.transform(basegfx::B2DHomMatrix(fConvert, 0.0, 0.0, 0.0, fConvert, 0.0)); |
238 | 0 | } |
239 | | |
240 | | // LO has the handle in the middle of a segment, but not for first and last segment. |
241 | 0 | for (sal_uInt32 i = 1; i < aEdgePolygon.count() - 2; i++) |
242 | 0 | { |
243 | 0 | const basegfx::B2DPoint aBeforePt(aEdgePolygon.getB2DPoint(i)); |
244 | 0 | const basegfx::B2DPoint aAfterPt(aEdgePolygon.getB2DPoint(i + 1)); |
245 | 0 | rHandlePositions.push_back((aBeforePt + aAfterPt) / 2.0); |
246 | 0 | } |
247 | 0 | } |
248 | | |
249 | | void ConnectorHelper::getLOCurvedHandlePositionsHmm( |
250 | | const oox::drawingml::ShapePtr& pConnector, std::vector<basegfx::B2DPoint>& rHandlePositions) |
251 | 0 | { |
252 | | // This method is intended for Edgekind css::drawing::ConnectorType_Curve for which OoXML |
253 | | // compatible routing is enabled. |
254 | 0 | rHandlePositions.clear(); |
255 | |
|
256 | 0 | if (!pConnector) |
257 | 0 | return; |
258 | 0 | uno::Reference<drawing::XShape> xConnector(pConnector->getXShape()); |
259 | 0 | if (!xConnector.is()) |
260 | 0 | return; |
261 | | |
262 | | // Get the EdgeTrack polygon. We cannot use UNO "PolyPolygonBezier" because that includes |
263 | | // the yet not known anchor position in Writer. Thus get the polygon directly from the object. |
264 | 0 | SdrEdgeObj* pEdgeObj = dynamic_cast<SdrEdgeObj*>(SdrObject::getSdrObjectFromXShape(xConnector)); |
265 | 0 | if (!pEdgeObj) |
266 | 0 | return; |
267 | 0 | basegfx::B2DPolyPolygon aB2DPolyPolygon(pEdgeObj->GetEdgeTrackPath()); |
268 | 0 | if (aB2DPolyPolygon.count() == 0) |
269 | 0 | return; |
270 | | |
271 | 0 | basegfx::B2DPolygon aEdgePolygon = aB2DPolyPolygon.getB2DPolygon(0); |
272 | 0 | if (aEdgePolygon.count() < 3 || !aEdgePolygon.areControlPointsUsed()) |
273 | 0 | return; |
274 | | |
275 | | // We need Hmm, the polygon might be e.g. in Twips, in Writer for example |
276 | 0 | MapUnit eMapUnit = pEdgeObj->getSdrModelFromSdrObject().GetItemPool().GetMetric(0); |
277 | 0 | if (eMapUnit != MapUnit::Map100thMM) |
278 | 0 | { |
279 | 0 | const auto eFrom = MapToO3tlLength(eMapUnit); |
280 | 0 | if (eFrom == o3tl::Length::invalid) |
281 | 0 | return; |
282 | 0 | const double fConvert(o3tl::convert(1.0, eFrom, o3tl::Length::mm100)); |
283 | 0 | aEdgePolygon.transform(basegfx::B2DHomMatrix(fConvert, 0.0, 0.0, 0.0, fConvert, 0.0)); |
284 | 0 | } |
285 | | |
286 | | // The OOXML compatible routing has the handles as polygon points, but not start or |
287 | | // end point. |
288 | 0 | for (sal_uInt32 i = 1; i < aEdgePolygon.count() - 1; i++) |
289 | 0 | { |
290 | 0 | rHandlePositions.push_back(aEdgePolygon.getB2DPoint(i)); |
291 | 0 | } |
292 | 0 | } |
293 | | |
294 | | void ConnectorHelper::applyConnections(const oox::drawingml::ShapePtr& pConnector, |
295 | | oox::drawingml::ShapeIdMap& rShapeMap) |
296 | 133 | { |
297 | 133 | uno::Reference<drawing::XShape> xConnector(pConnector->getXShape()); |
298 | 133 | if (!xConnector.is()) |
299 | 0 | return; |
300 | 133 | uno::Reference<beans::XPropertySet> xPropSet(xConnector, uno::UNO_QUERY); |
301 | 133 | if (!xPropSet.is()) |
302 | 0 | return; |
303 | | |
304 | | // MS Office allows route between shapes with small distance. LO default is 5mm. |
305 | 133 | xPropSet->setPropertyValue(u"EdgeNode1HorzDist"_ustr, uno::Any(sal_Int32(0))); |
306 | 133 | xPropSet->setPropertyValue(u"EdgeNode1VertDist"_ustr, uno::Any(sal_Int32(0))); |
307 | 133 | xPropSet->setPropertyValue(u"EdgeNode2HorzDist"_ustr, uno::Any(sal_Int32(0))); |
308 | 133 | xPropSet->setPropertyValue(u"EdgeNode2VertDist"_ustr, uno::Any(sal_Int32(0))); |
309 | | |
310 | | // A OOXML curvedConnector uses a routing method which is basically incompatible with the |
311 | | // traditional way of LibreOffice. A compatible way was added and needs to be enabled before |
312 | | // connections are set, so that the method is used in the default routing. |
313 | 133 | xPropSet->setPropertyValue(u"EdgeOOXMLCurve"_ustr, uno::Any(true)); |
314 | | |
315 | 133 | oox::drawingml::ConnectorShapePropertiesList aConnectorShapeProperties |
316 | 133 | = pConnector->getConnectorShapeProperties(); |
317 | | // It contains maximal two items, each a struct with mbStartShape, maDestShapeId, mnDestGlueId |
318 | 133 | for (const auto& aIt : aConnectorShapeProperties) |
319 | 26 | { |
320 | 26 | const auto pItem = rShapeMap.find(aIt.maDestShapeId); |
321 | 26 | if (pItem == rShapeMap.end()) |
322 | 0 | continue; |
323 | | |
324 | 26 | uno::Reference<drawing::XShape> xShape(pItem->second->getXShape(), uno::UNO_QUERY); |
325 | 26 | if (xShape.is()) |
326 | 26 | { |
327 | | // Connect to the found shape. |
328 | 26 | if (aIt.mbStartShape) |
329 | 13 | xPropSet->setPropertyValue(u"StartShape"_ustr, uno::Any(xShape)); |
330 | 13 | else |
331 | 13 | xPropSet->setPropertyValue(u"EndShape"_ustr, uno::Any(xShape)); |
332 | | |
333 | | // The first four glue points are the default glue points, which are set by LibreOffice. |
334 | | // They do not belong to the preset geometry of the shape. |
335 | | // Adapt gluepoint index to LibreOffice |
336 | 26 | uno::Reference<drawing::XGluePointsSupplier> xSupplier(xShape, uno::UNO_QUERY); |
337 | 26 | css::uno::Reference<css::container::XIdentifierContainer> xGluePoints( |
338 | 26 | xSupplier->getGluePoints(), uno::UNO_QUERY); |
339 | 26 | sal_Int32 nCountGluePoints = xGluePoints->getIdentifiers().getLength(); |
340 | 26 | sal_Int32 nGlueId = aIt.mnDestGlueId; |
341 | | |
342 | 26 | if (nCountGluePoints > 4) |
343 | 0 | nGlueId += 4; |
344 | 26 | else |
345 | 26 | { |
346 | | // In these cases the mso_CustomShape struct defines no gluepoints (Why not?), thus |
347 | | // our default gluepoints are used. The order of the default gluepoints might differ |
348 | | // from the order of the OOXML gluepoints. We try to change nGlueId so, that the |
349 | | // connector attaches to a default gluepoint at the same side as it attaches in OOXML. |
350 | 26 | const OUString sShapeType |
351 | 26 | = pItem->second->getCustomShapeProperties()->getShapePresetTypeName(); |
352 | 26 | if (ConnectorHelper::hasClockwiseCxn(sShapeType)) |
353 | 0 | nGlueId = (nGlueId + 1) % 4; |
354 | 26 | else |
355 | 26 | { |
356 | 26 | bool bFlipH = pItem->second->getFlipH(); |
357 | 26 | bool bFlipV = pItem->second->getFlipV(); |
358 | 26 | if (bFlipH == bFlipV) |
359 | 26 | { |
360 | | // change id of the left and right glue points of the bounding box (1 <-> 3) |
361 | 26 | if (nGlueId == 1) |
362 | 6 | nGlueId = 3; // Right |
363 | 20 | else if (nGlueId == 3) |
364 | 20 | nGlueId = 1; // Left |
365 | 26 | } |
366 | 26 | } |
367 | 26 | } |
368 | | |
369 | 26 | if (aIt.mbStartShape) |
370 | 13 | xPropSet->setPropertyValue(u"StartGluePointIndex"_ustr, uno::Any(nGlueId)); |
371 | 13 | else |
372 | 13 | xPropSet->setPropertyValue(u"EndGluePointIndex"_ustr, uno::Any(nGlueId)); |
373 | 26 | } |
374 | 26 | } |
375 | 133 | } |
376 | | |
377 | | void ConnectorHelper::applyBentHandleAdjustments(oox::drawingml::ShapePtr pConnector) |
378 | 0 | { |
379 | 0 | uno::Reference<drawing::XShape> xConnector(pConnector->getXShape(), uno::UNO_QUERY); |
380 | 0 | if (!xConnector.is()) |
381 | 0 | return; |
382 | 0 | uno::Reference<beans::XPropertySet> xPropSet(xConnector, uno::UNO_QUERY); |
383 | 0 | if (!xPropSet.is()) |
384 | 0 | return; |
385 | | |
386 | 0 | std::vector<basegfx::B2DPoint> aOOXMLHandles; |
387 | 0 | ConnectorHelper::getOOXHandlePositionsHmm(pConnector, aOOXMLHandles); |
388 | 0 | std::vector<basegfx::B2DPoint> aLODefaultHandles; |
389 | 0 | ConnectorHelper::getLOBentHandlePositionsHmm(pConnector, aLODefaultHandles); |
390 | |
|
391 | 0 | if (aOOXMLHandles.size() == aLODefaultHandles.size()) |
392 | 0 | { |
393 | 0 | bool bUseYforHori |
394 | 0 | = basegfx::fTools::equalZero(getConnectorTransformMatrix(pConnector).get(0, 0)); |
395 | 0 | for (size_t i = 0; i < aOOXMLHandles.size(); i++) |
396 | 0 | { |
397 | 0 | basegfx::B2DVector aDiff(aOOXMLHandles[i] - aLODefaultHandles[i]); |
398 | 0 | sal_Int32 nDiff; |
399 | 0 | if ((i == 1 && !bUseYforHori) || (i != 1 && bUseYforHori)) |
400 | 0 | nDiff = basegfx::fround(aDiff.getY()); |
401 | 0 | else |
402 | 0 | nDiff = basegfx::fround(aDiff.getX()); |
403 | 0 | xPropSet->setPropertyValue("EdgeLine" + OUString::number(i + 1) + "Delta", |
404 | 0 | uno::Any(nDiff)); |
405 | 0 | } |
406 | 0 | } |
407 | 0 | } |
408 | | |
409 | | void ConnectorHelper::applyCurvedHandleAdjustments(oox::drawingml::ShapePtr pConnector) |
410 | 0 | { |
411 | 0 | uno::Reference<drawing::XShape> xConnector(pConnector->getXShape(), uno::UNO_QUERY); |
412 | 0 | if (!xConnector.is()) |
413 | 0 | return; |
414 | 0 | uno::Reference<beans::XPropertySet> xPropSet(xConnector, uno::UNO_QUERY); |
415 | 0 | if (!xPropSet.is()) |
416 | 0 | return; |
417 | | |
418 | 0 | std::vector<basegfx::B2DPoint> aOOXMLHandles; |
419 | 0 | ConnectorHelper::getOOXHandlePositionsHmm(pConnector, aOOXMLHandles); |
420 | 0 | std::vector<basegfx::B2DPoint> aLODefaultHandles; |
421 | 0 | ConnectorHelper::getLOCurvedHandlePositionsHmm(pConnector, aLODefaultHandles); |
422 | |
|
423 | 0 | if (aOOXMLHandles.size() == aLODefaultHandles.size()) |
424 | 0 | { |
425 | 0 | bool bUseYforHori |
426 | 0 | = basegfx::fTools::equalZero(getConnectorTransformMatrix(pConnector).get(0, 0)); |
427 | 0 | for (size_t i = 0; i < aOOXMLHandles.size(); i++) |
428 | 0 | { |
429 | 0 | basegfx::B2DVector aDiff(aOOXMLHandles[i] - aLODefaultHandles[i]); |
430 | 0 | sal_Int32 nDiff; |
431 | 0 | if ((i == 1 && !bUseYforHori) || (i != 1 && bUseYforHori)) |
432 | 0 | nDiff = basegfx::fround(aDiff.getY()); |
433 | 0 | else |
434 | 0 | nDiff = basegfx::fround(aDiff.getX()); |
435 | 0 | xPropSet->setPropertyValue("EdgeLine" + OUString::number(i + 1) + "Delta", |
436 | 0 | uno::Any(nDiff)); |
437 | 0 | } |
438 | 0 | } |
439 | 0 | } |
440 | | |
441 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |