Bump version to 6.4-15
[LibreOffice.git] / svx / source / customshapes / EnhancedCustomShape2d.cxx
blobfdec9dbdebf31dbf3dee0274a43a2de4af172110
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <svx/EnhancedCustomShape2d.hxx>
23 #include <svx/EnhancedCustomShapeGeometry.hxx>
24 #include <svx/EnhancedCustomShapeTypeNames.hxx>
25 #include <svx/svdoashp.hxx>
26 #include <svx/svdtrans.hxx>
27 #include <svx/svdocirc.hxx>
28 #include <svx/svdogrp.hxx>
29 #include <svx/svdopath.hxx>
30 #include <svx/svdocapt.hxx>
31 #include <svx/svdpage.hxx>
32 #include <svx/xflclit.hxx>
33 #include <svx/svdmodel.hxx>
34 #include <rtl/crc.h>
35 #include <rtl/math.hxx>
36 #include <svx/xfillit0.hxx>
37 #include <svx/xlineit0.hxx>
38 #include <svx/xlnstit.hxx>
39 #include <svx/xlnedit.hxx>
40 #include <svx/xlnstwit.hxx>
41 #include <svx/xlnedwit.hxx>
42 #include <svx/xlnstcit.hxx>
43 #include <svx/xlnedcit.hxx>
44 #include <svx/xflgrit.hxx>
45 #include <svx/xflhtit.hxx>
46 #include <svx/xbtmpit.hxx>
47 #include <svx/xgrad.hxx>
48 #include <svx/xhatch.hxx>
49 #include <svx/sdshitm.hxx>
50 #include <com/sun/star/awt/Size.hpp>
51 #include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
52 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
53 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
54 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
55 #include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
56 #include <basegfx/numeric/ftools.hxx>
57 #include <basegfx/color/bcolortools.hxx>
58 #include <basegfx/polygon/b2dpolygon.hxx>
59 #include <basegfx/polygon/b2dpolygontools.hxx>
60 #include <basegfx/matrix/b2dhommatrixtools.hxx>
61 #include <rtl/strbuf.hxx>
62 #include <sal/log.hxx>
64 #include <algorithm>
65 #include <math.h>
66 #include <unordered_set>
68 using namespace ::com::sun::star;
69 using namespace ::com::sun::star::uno;
70 using namespace ::com::sun::star::drawing;
71 using namespace ::com::sun::star::drawing::EnhancedCustomShapeSegmentCommand;
73 void EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( EnhancedCustomShapeParameter& rParameter, const sal_Int32 nValue )
75 sal_uInt32 nDat = static_cast<sal_uInt32>(nValue);
76 sal_Int32 nNewValue = nValue;
78 // check if this is a special point
79 if ( ( nDat >> 16 ) == 0x8000 )
81 nNewValue = static_cast<sal_uInt16>(nDat);
82 rParameter.Type = EnhancedCustomShapeParameterType::EQUATION;
84 else
85 rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
86 rParameter.Value <<= nNewValue;
89 OUString EnhancedCustomShape2d::GetEquation( const sal_uInt16 nFlags, sal_Int32 nP1, sal_Int32 nP2, sal_Int32 nP3 )
91 OUString aEquation;
92 bool b1Special = ( nFlags & 0x2000 ) != 0;
93 bool b2Special = ( nFlags & 0x4000 ) != 0;
94 bool b3Special = ( nFlags & 0x8000 ) != 0;
95 switch( nFlags & 0xff )
97 case 0 :
98 case 14 :
100 sal_Int32 nOptimize = 0;
101 if ( nP1 )
102 nOptimize |= 1;
103 if ( nP2 )
104 nOptimize |= 2;
105 if ( b1Special )
106 nOptimize |= 4;
107 if ( b2Special )
108 nOptimize |= 8;
109 switch( nOptimize )
111 case 0 :
112 break;
113 case 1 :
114 case 4 :
115 case 5 :
116 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
117 break;
118 case 2 :
119 case 8 :
120 case 10:
121 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
122 break;
123 default :
125 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
126 aEquation += "+";
127 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
129 break;
131 if ( b3Special || nP3 )
133 aEquation += "-";
134 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
137 break;
138 case 1 :
140 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
141 if ( b2Special || ( nP2 != 1 ) )
143 aEquation += "*";
144 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
146 if ( b3Special || ( ( nP3 != 1 ) && ( nP3 != 0 ) ) )
148 aEquation += "/";
149 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
152 break;
153 case 2 :
155 aEquation += "(";
156 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
157 aEquation += "+";
158 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
159 aEquation += ")/2";
161 break;
162 case 3 :
164 aEquation += "abs(";
165 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
166 aEquation += ")";
168 break;
169 case 4 :
171 aEquation += "min(";
172 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
173 aEquation += ",";
174 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
175 aEquation += ")";
177 break;
178 case 5 :
180 aEquation += "max(";
181 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
182 aEquation += ",";
183 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
184 aEquation += ")";
186 break;
187 case 6 :
189 aEquation += "if(";
190 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
191 aEquation += ",";
192 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
193 aEquation += ",";
194 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
195 aEquation += ")";
197 break;
198 case 7 :
200 aEquation += "sqrt(";
201 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
202 aEquation += "*";
203 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
204 aEquation += "+";
205 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
206 aEquation += "*";
207 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
208 aEquation += "+";
209 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
210 aEquation += "*";
211 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
212 aEquation += ")";
214 break;
215 case 8 :
217 aEquation += "atan2(";
218 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
219 aEquation += ",";
220 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
221 aEquation += ")/(pi/180)";
223 break;
224 case 9 :
226 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
227 aEquation += "*sin(";
228 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
229 aEquation += "*(pi/180))";
231 break;
232 case 10 :
234 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
235 aEquation += "*cos(";
236 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
237 aEquation += "*(pi/180))";
239 break;
240 case 11 :
242 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
243 aEquation += "*cos(atan2(";
244 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
245 aEquation += ",";
246 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
247 aEquation += "))";
249 break;
250 case 12 :
252 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
253 aEquation += "*sin(atan2(";
254 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
255 aEquation += ",";
256 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
257 aEquation += "))";
259 break;
260 case 13 :
262 aEquation += "sqrt(";
263 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
264 aEquation += ")";
266 break;
267 case 15 :
269 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
270 aEquation += "*sqrt(1-(";
271 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
272 aEquation += "/";
273 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
274 aEquation += ")"
275 "*(";
276 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
277 aEquation += "/";
278 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
279 aEquation += "))";
281 break;
282 case 16 :
284 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
285 aEquation += "*tan(";
286 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
287 aEquation += ")";
289 break;
290 case 0x80 :
292 aEquation += "sqrt(";
293 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
294 aEquation += "*";
295 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
296 aEquation += "-";
297 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
298 aEquation += "*";
299 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
300 aEquation += ")";
302 break;
303 case 0x81 :
305 aEquation += "(cos(";
306 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
307 aEquation += "*(pi/180))*(";
308 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
309 aEquation += "-10800)+sin(";
310 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
311 aEquation += "*(pi/180))*(";
312 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
313 aEquation += "-10800))+10800";
315 break;
316 case 0x82 :
318 aEquation += "-(sin(";
319 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
320 aEquation += "*(pi/180))*(";
321 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
322 aEquation += "-10800)-cos(";
323 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
324 aEquation += "*(pi/180))*(";
325 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
326 aEquation += "-10800))+10800";
328 break;
330 return aEquation;
333 void EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( OUString& rParameter, const sal_Int32 nPara, const bool bIsSpecialValue )
335 if ( bIsSpecialValue )
337 if ( nPara & 0x400 )
339 rParameter += "?";
340 rParameter += OUString::number( ( nPara & 0xff ) );
341 rParameter += " ";
343 else
345 switch( nPara )
347 case DFF_Prop_adjustValue :
348 case DFF_Prop_adjust2Value :
349 case DFF_Prop_adjust3Value :
350 case DFF_Prop_adjust4Value :
351 case DFF_Prop_adjust5Value :
352 case DFF_Prop_adjust6Value :
353 case DFF_Prop_adjust7Value :
354 case DFF_Prop_adjust8Value :
355 case DFF_Prop_adjust9Value :
356 case DFF_Prop_adjust10Value :
358 rParameter += "$";
359 rParameter += OUString::number( ( nPara - DFF_Prop_adjustValue ) );
360 rParameter += " ";
362 break;
363 case DFF_Prop_geoLeft :
365 rParameter += "left";
367 break;
368 case DFF_Prop_geoTop :
370 rParameter += "top";
372 break;
373 case DFF_Prop_geoRight :
375 rParameter += "right";
377 break;
378 case DFF_Prop_geoBottom :
380 rParameter += "bottom";
382 break;
386 else
388 rParameter += OUString::number( nPara );
392 void EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( EnhancedCustomShapeParameter& rParameter, const sal_Int32 nPara, const bool bIsSpecialValue, bool bHorz )
394 sal_Int32 nValue = 0;
395 if ( bIsSpecialValue )
397 if ( ( nPara >= 0x100 ) && ( nPara <= 0x107 ) )
399 nValue = nPara & 0xff;
400 rParameter.Type = EnhancedCustomShapeParameterType::ADJUSTMENT;
402 else if ( ( nPara >= 3 ) && ( nPara <= 0x82 ) )
404 nValue = nPara - 3;
405 rParameter.Type = EnhancedCustomShapeParameterType::EQUATION;
407 else if ( nPara == 0 )
409 nValue = 0;
410 if ( bHorz )
411 rParameter.Type = EnhancedCustomShapeParameterType::LEFT;
412 else
413 rParameter.Type = EnhancedCustomShapeParameterType::TOP;
415 else if ( nPara == 1 )
417 nValue = 0;
418 if ( bHorz )
419 rParameter.Type = EnhancedCustomShapeParameterType::RIGHT;
420 else
421 rParameter.Type = EnhancedCustomShapeParameterType::BOTTOM;
423 else if ( nPara == 2 ) // means to be centered, but should not be
424 { // used in our implementation
425 nValue = 5600;
426 rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
428 else
430 nValue = nPara;
431 rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
434 else
436 nValue = nPara;
437 rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
439 rParameter.Value <<= nValue;
442 bool EnhancedCustomShape2d::ConvertSequenceToEnhancedCustomShape2dHandle(
443 const css::beans::PropertyValues& rHandleProperties,
444 EnhancedCustomShape2d::Handle& rDestinationHandle )
446 bool bRetValue = false;
447 if ( rHandleProperties.hasElements() )
449 rDestinationHandle.nFlags = HandleFlags::NONE;
450 for ( const css::beans::PropertyValue& rPropVal : rHandleProperties )
452 if ( rPropVal.Name == "Position" )
454 if ( rPropVal.Value >>= rDestinationHandle.aPosition )
455 bRetValue = true;
457 else if ( rPropVal.Name == "MirroredX" )
459 bool bMirroredX;
460 if ( rPropVal.Value >>= bMirroredX )
462 if ( bMirroredX )
463 rDestinationHandle.nFlags |= HandleFlags::MIRRORED_X;
466 else if ( rPropVal.Name == "MirroredY" )
468 bool bMirroredY;
469 if ( rPropVal.Value >>= bMirroredY )
471 if ( bMirroredY )
472 rDestinationHandle.nFlags |= HandleFlags::MIRRORED_Y;
475 else if ( rPropVal.Name == "Switched" )
477 bool bSwitched;
478 if ( rPropVal.Value >>= bSwitched )
480 if ( bSwitched )
481 rDestinationHandle.nFlags |= HandleFlags::SWITCHED;
484 else if ( rPropVal.Name == "Polar" )
486 if ( rPropVal.Value >>= rDestinationHandle.aPolar )
487 rDestinationHandle.nFlags |= HandleFlags::POLAR;
489 else if ( rPropVal.Name == "RefX" )
491 if ( rPropVal.Value >>= rDestinationHandle.nRefX )
492 rDestinationHandle.nFlags |= HandleFlags::REFX;
494 else if ( rPropVal.Name == "RefY" )
496 if ( rPropVal.Value >>= rDestinationHandle.nRefY )
497 rDestinationHandle.nFlags |= HandleFlags::REFY;
499 else if ( rPropVal.Name == "RefAngle" )
501 if ( rPropVal.Value >>= rDestinationHandle.nRefAngle )
502 rDestinationHandle.nFlags |= HandleFlags::REFANGLE;
504 else if ( rPropVal.Name == "RefR" )
506 if ( rPropVal.Value >>= rDestinationHandle.nRefR )
507 rDestinationHandle.nFlags |= HandleFlags::REFR;
509 else if ( rPropVal.Name == "RadiusRangeMinimum" )
511 if ( rPropVal.Value >>= rDestinationHandle.aRadiusRangeMinimum )
512 rDestinationHandle.nFlags |= HandleFlags::RADIUS_RANGE_MINIMUM;
514 else if ( rPropVal.Name == "RadiusRangeMaximum" )
516 if ( rPropVal.Value >>= rDestinationHandle.aRadiusRangeMaximum )
517 rDestinationHandle.nFlags |= HandleFlags::RADIUS_RANGE_MAXIMUM;
519 else if ( rPropVal.Name == "RangeXMinimum" )
521 if ( rPropVal.Value >>= rDestinationHandle.aXRangeMinimum )
522 rDestinationHandle.nFlags |= HandleFlags::RANGE_X_MINIMUM;
524 else if ( rPropVal.Name == "RangeXMaximum" )
526 if ( rPropVal.Value >>= rDestinationHandle.aXRangeMaximum )
527 rDestinationHandle.nFlags |= HandleFlags::RANGE_X_MAXIMUM;
529 else if ( rPropVal.Name == "RangeYMinimum" )
531 if ( rPropVal.Value >>= rDestinationHandle.aYRangeMinimum )
532 rDestinationHandle.nFlags |= HandleFlags::RANGE_Y_MINIMUM;
534 else if ( rPropVal.Name == "RangeYMaximum" )
536 if ( rPropVal.Value >>= rDestinationHandle.aYRangeMaximum )
537 rDestinationHandle.nFlags |= HandleFlags::RANGE_Y_MAXIMUM;
541 return bRetValue;
544 void EnhancedCustomShape2d::ApplyShapeAttributes( const SdrCustomShapeGeometryItem& rGeometryItem )
546 // AdjustmentValues
547 const Any* pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( "AdjustmentValues" );
548 if ( pAny )
549 *pAny >>= seqAdjustmentValues;
552 // Coordsize
553 const Any* pViewBox = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( "ViewBox" );
554 css::awt::Rectangle aViewBox;
555 if ( pViewBox && (*pViewBox >>= aViewBox ) )
557 nCoordLeft = aViewBox.X;
558 nCoordTop = aViewBox.Y;
559 nCoordWidthG = labs( aViewBox.Width );
560 nCoordHeightG = labs( aViewBox.Height);
562 const OUString sPath( "Path" );
565 // Path/Coordinates
566 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, "Coordinates" );
567 if ( pAny )
568 *pAny >>= seqCoordinates;
571 // Path/GluePoints
572 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, "GluePoints" );
573 if ( pAny )
574 *pAny >>= seqGluePoints;
577 // Path/Segments
578 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, "Segments" );
579 if ( pAny )
580 *pAny >>= seqSegments;
583 // Path/SubViewSize
584 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, "SubViewSize" );
585 if ( pAny )
586 *pAny >>= seqSubViewSize;
589 // Path/StretchX
590 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, "StretchX" );
591 if ( pAny )
593 sal_Int32 nStretchX = 0;
594 if ( *pAny >>= nStretchX )
595 nXRef = nStretchX;
599 // Path/StretchY
600 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, "StretchY" );
601 if ( pAny )
603 sal_Int32 nStretchY = 0;
604 if ( *pAny >>= nStretchY )
605 nYRef = nStretchY;
609 // Path/TextFrames
610 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, "TextFrames" );
611 if ( pAny )
612 *pAny >>= seqTextFrames;
615 // Equations
616 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( "Equations" );
617 if ( pAny )
618 *pAny >>= seqEquations;
621 // Handles
622 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( "Handles" );
623 if ( pAny )
624 *pAny >>= seqHandles;
627 EnhancedCustomShape2d::~EnhancedCustomShape2d()
631 void EnhancedCustomShape2d::SetPathSize( sal_Int32 nIndex )
633 sal_Int32 nWidth = 0;
634 sal_Int32 nHeight = 0;
636 if ( seqSubViewSize.hasElements() && nIndex < seqSubViewSize.getLength() ) {
637 nWidth = seqSubViewSize[ nIndex ].Width;
638 nHeight = seqSubViewSize[ nIndex ].Height;
639 SAL_INFO(
640 "svx",
641 "set subpath " << nIndex << " size: " << nWidth << " x "
642 << nHeight);
645 if ( nWidth && nHeight ) {
646 nCoordWidth = nWidth;
647 nCoordHeight = nHeight;
648 } else {
649 nCoordWidth = nCoordWidthG;
650 nCoordHeight = nCoordHeightG;
653 fXScale = nCoordWidth == 0 ? 0.0 : static_cast<double>(aLogicRect.GetWidth()) / static_cast<double>(nCoordWidth);
654 fYScale = nCoordHeight == 0 ? 0.0 : static_cast<double>(aLogicRect.GetHeight()) / static_cast<double>(nCoordHeight);
655 if ( bOOXMLShape )
657 SAL_INFO(
658 "svx",
659 "ooxml shape, path width: " << nCoordWidth << " height: "
660 << nCoordHeight);
662 // Try to set up scale separately, if given only width or height
663 // This is possible case in OOXML when only width or height is non-zero
664 if ( nCoordWidth == 0 )
666 if ( nWidth )
667 fXScale = static_cast<double>(aLogicRect.GetWidth()) / static_cast<double>(nWidth);
668 else
669 fXScale = 1.0;
671 if ( nCoordHeight == 0 )
673 if ( nHeight )
674 fYScale = static_cast<double>(aLogicRect.GetHeight()) / static_cast<double>(nHeight);
675 else
676 fYScale = 1.0;
679 if ( static_cast<sal_uInt32>(nXRef) != 0x80000000 && aLogicRect.GetHeight() )
681 fXRatio = static_cast<double>(aLogicRect.GetWidth()) / static_cast<double>(aLogicRect.GetHeight());
682 if ( fXRatio > 1 )
683 fXScale /= fXRatio;
684 else
685 fXRatio = 1.0;
687 else
688 fXRatio = 1.0;
689 if ( static_cast<sal_uInt32>(nYRef) != 0x80000000 && aLogicRect.GetWidth() )
691 fYRatio = static_cast<double>(aLogicRect.GetHeight()) / static_cast<double>(aLogicRect.GetWidth());
692 if ( fYRatio > 1 )
693 fYScale /= fYRatio;
694 else
695 fYRatio = 1.0;
697 else
698 fYRatio = 1.0;
701 EnhancedCustomShape2d::EnhancedCustomShape2d(SdrObjCustomShape& rSdrObjCustomShape)
702 : SfxItemSet ( rSdrObjCustomShape.GetMergedItemSet() ),
703 mrSdrObjCustomShape ( rSdrObjCustomShape ),
704 eSpType ( mso_sptNil ),
705 nCoordLeft ( 0 ),
706 nCoordTop ( 0 ),
707 nCoordWidthG ( 21600 ),
708 nCoordHeightG ( 21600 ),
709 bOOXMLShape ( false ),
710 nXRef ( 0x80000000 ),
711 nYRef ( 0x80000000 ),
712 nColorData ( 0 ),
713 bFilled ( rSdrObjCustomShape.GetMergedItem( XATTR_FILLSTYLE ).GetValue() != drawing::FillStyle_NONE ),
714 bStroked ( rSdrObjCustomShape.GetMergedItem( XATTR_LINESTYLE ).GetValue() != drawing::LineStyle_NONE ),
715 bFlipH ( false ),
716 bFlipV ( false )
718 // bTextFlow needs to be set before clearing the TextDirection Item
720 ClearItem( SDRATTR_TEXTDIRECTION ); //SJ: vertical writing is not required, by removing this item no outliner is created
722 // #i105323# For 2D AutoShapes, the shadow attribute does not need to be applied to any
723 // of the constructed helper SdrObjects. This would lead to problems since the shadow
724 // of one helper object would fall on one helper object behind it (e.g. with the
725 // eyes of the smiley shape). This is not wanted; instead a single shadow 'behind'
726 // the AutoShape visualisation is wanted. This is done with primitive functionality
727 // now in SdrCustomShapePrimitive2D::create2DDecomposition, but only for 2D objects
728 // (see there and in EnhancedCustomShape3d::Create3DObject to read more).
729 // This exception may be removed later when AutoShapes will create primitives directly.
730 // So, currently remove the ShadowAttribute from the ItemSet to not apply it to any
731 // 2D helper shape.
732 ClearItem(SDRATTR_SHADOW);
734 Point aP( mrSdrObjCustomShape.GetSnapRect().Center() );
735 Size aS( mrSdrObjCustomShape.GetLogicRect().GetSize() );
736 aP.AdjustX( -(aS.Width() / 2) );
737 aP.AdjustY( -(aS.Height() / 2) );
738 aLogicRect = tools::Rectangle( aP, aS );
740 OUString sShapeType;
741 const SdrCustomShapeGeometryItem& rGeometryItem(mrSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
742 const Any* pAny = rGeometryItem.GetPropertyValueByName( "Type" );
743 if ( pAny ) {
744 *pAny >>= sShapeType;
745 bOOXMLShape = sShapeType.startsWith("ooxml-");
746 SAL_INFO("svx", "shape type: " << sShapeType << " " << bOOXMLShape);
748 eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
750 pAny = rGeometryItem.GetPropertyValueByName( "MirroredX" );
751 if ( pAny )
752 *pAny >>= bFlipH;
753 pAny = rGeometryItem.GetPropertyValueByName( "MirroredY" );
754 if ( pAny )
755 *pAny >>= bFlipV;
757 nRotateAngle = static_cast<sal_Int32>(mrSdrObjCustomShape.GetObjectRotation() * 100.0);
759 /*const sal_Int32* pDefData =*/ ApplyShapeAttributes( rGeometryItem );
760 SetPathSize();
762 switch( eSpType )
764 case mso_sptCan : nColorData = 0x20400000; break;
765 case mso_sptCube : nColorData = 0x302e0000; break;
766 case mso_sptActionButtonBlank : nColorData = 0x502ce400; break;
767 case mso_sptActionButtonHome : nColorData = 0x702ce4ce; break;
768 case mso_sptActionButtonHelp : nColorData = 0x602ce4c0; break;
769 case mso_sptActionButtonInformation : nColorData = 0x702ce4c5; break;
770 case mso_sptActionButtonBackPrevious : nColorData = 0x602ce4c0; break;
771 case mso_sptActionButtonForwardNext : nColorData = 0x602ce4c0; break;
772 case mso_sptActionButtonBeginning : nColorData = 0x602ce4c0; break;
773 case mso_sptActionButtonEnd : nColorData = 0x602ce4c0; break;
774 case mso_sptActionButtonReturn : nColorData = 0x602ce4c0; break;
775 case mso_sptActionButtonDocument : nColorData = 0x702ce4ec; break;
776 case mso_sptActionButtonSound : nColorData = 0x602ce4c0; break;
777 case mso_sptActionButtonMovie : nColorData = 0x602ce4c0; break;
778 case mso_sptBevel : nColorData = 0x502ce400; break;
779 case mso_sptFoldedCorner : nColorData = 0x20e00000; break;
780 case mso_sptSmileyFace : nColorData = 0x20e00000; break;
781 case mso_sptNil :
783 if( sShapeType.getLength() > 4 &&
784 sShapeType.match( "col-" ))
786 nColorData = sShapeType.copy( 4 ).toUInt32( 16 );
789 break;
790 case mso_sptCurvedLeftArrow :
791 case mso_sptCurvedRightArrow :
792 case mso_sptCurvedUpArrow :
793 case mso_sptCurvedDownArrow : nColorData = 0x20d00000; break;
794 case mso_sptRibbon2 : nColorData = 0x30ee0000; break;
795 case mso_sptRibbon : nColorData = 0x30ee0000; break;
797 case mso_sptEllipseRibbon2 : nColorData = 0x30ee0000; break;
798 case mso_sptEllipseRibbon : nColorData = 0x30ee0000; break;
800 case mso_sptVerticalScroll : nColorData = 0x30ee0000; break;
801 case mso_sptHorizontalScroll : nColorData = 0x30ee0000; break;
802 default:
803 break;
806 sal_Int32 nLength = seqEquations.getLength();
808 if ( nLength )
810 vNodesSharedPtr.resize( nLength );
811 vEquationResults.resize( nLength );
812 for ( sal_Int32 i = 0; i < nLength; i++ )
814 vEquationResults[ i ].bReady = false;
817 vNodesSharedPtr[ i ] = EnhancedCustomShape::FunctionParser::parseFunction( seqEquations[ i ], *this );
819 catch ( EnhancedCustomShape::ParseError& )
821 SAL_INFO(
822 "svx",
823 "error: equation number: " << i << ", parser failed ("
824 << seqEquations[i] << ")");
830 using EnhancedCustomShape::ExpressionFunct;
832 double EnhancedCustomShape2d::GetEnumFunc( const ExpressionFunct eFunc ) const
834 double fRet = 0.0;
835 switch( eFunc )
837 case ExpressionFunct::EnumPi : fRet = F_PI; break;
838 case ExpressionFunct::EnumLeft : fRet = static_cast<double>(nCoordLeft); break;
839 case ExpressionFunct::EnumTop : fRet = static_cast<double>(nCoordTop); break;
840 case ExpressionFunct::EnumRight : fRet = static_cast<double>(nCoordLeft + nCoordWidth) * fXRatio; break;
841 case ExpressionFunct::EnumBottom : fRet = static_cast<double>(nCoordTop + nCoordHeight) * fYRatio; break;
842 case ExpressionFunct::EnumXStretch : fRet = nXRef; break;
843 case ExpressionFunct::EnumYStretch : fRet = nYRef; break;
844 case ExpressionFunct::EnumHasStroke : fRet = bStroked ? 1.0 : 0.0; break;
845 case ExpressionFunct::EnumHasFill : fRet = bFilled ? 1.0 : 0.0; break;
846 case ExpressionFunct::EnumWidth : fRet = nCoordWidth; break;
847 case ExpressionFunct::EnumHeight : fRet = nCoordHeight; break;
848 case ExpressionFunct::EnumLogWidth : fRet = aLogicRect.GetWidth(); break;
849 case ExpressionFunct::EnumLogHeight : fRet = aLogicRect.GetHeight(); break;
850 default: break;
852 return fRet;
854 double EnhancedCustomShape2d::GetAdjustValueAsDouble( const sal_Int32 nIndex ) const
856 double fNumber = 0.0;
857 if ( nIndex < seqAdjustmentValues.getLength() )
859 if ( seqAdjustmentValues[ nIndex ].Value.getValueTypeClass() == TypeClass_DOUBLE )
860 seqAdjustmentValues[ nIndex ].Value >>= fNumber;
861 else
863 sal_Int32 nNumber = 0;
864 seqAdjustmentValues[ nIndex ].Value >>= nNumber;
865 fNumber = static_cast<double>(nNumber);
868 return fNumber;
870 double EnhancedCustomShape2d::GetEquationValueAsDouble( const sal_Int32 nIndex ) const
872 double fNumber = 0.0;
873 static sal_uInt32 nLevel = 0;
874 if ( nIndex < static_cast<sal_Int32>(vNodesSharedPtr.size()) )
876 if ( vNodesSharedPtr[ nIndex ].get() ) {
877 nLevel ++;
880 if ( vEquationResults[ nIndex ].bReady )
881 fNumber = vEquationResults[ nIndex ].fValue;
882 else {
883 // cast to non const, so that we can optimize by caching
884 // equation results, without changing all the const in the stack
885 struct EquationResult &aResult = const_cast<EnhancedCustomShape2d*>(this)->vEquationResults[ nIndex ];
887 fNumber = aResult.fValue = (*vNodesSharedPtr[ nIndex ])();
888 aResult.bReady = true;
890 SAL_INFO("svx", "equation " << nLevel << " (level: " << seqEquations[nIndex] << "): "
891 << fNumber << " --> " << 180.0*fNumber/10800000.0);
893 if ( !rtl::math::isFinite( fNumber ) )
894 fNumber = 0.0;
896 catch ( ... )
898 SAL_WARN("svx", "EnhancedCustomShape2d::GetEquationValueAsDouble failed");
900 nLevel --;
902 SAL_INFO(
903 "svx",
904 "?" << nIndex << " --> " << fNumber << " (angle: "
905 << 180.0*fNumber/10800000.0 << ")");
908 return fNumber;
911 bool EnhancedCustomShape2d::SetAdjustValueAsDouble( const double& rValue, const sal_Int32 nIndex )
913 bool bRetValue = false;
914 if ( nIndex < seqAdjustmentValues.getLength() )
916 // updating our local adjustment sequence
917 seqAdjustmentValues[ nIndex ].Value <<= rValue;
918 seqAdjustmentValues[ nIndex ].State = css::beans::PropertyState_DIRECT_VALUE;
919 bRetValue = true;
921 return bRetValue;
924 basegfx::B2DPoint EnhancedCustomShape2d::GetPointAsB2DPoint( const css::drawing::EnhancedCustomShapeParameterPair& rPair,
925 const bool bScale, const bool bReplaceGeoSize ) const
927 double fValX, fValY;
928 // width
929 GetParameter(fValX, rPair.First, bReplaceGeoSize, false);
930 fValX -= nCoordLeft;
931 if (bScale)
933 fValX *= fXScale;
935 // height
936 GetParameter(fValY, rPair.Second, false, bReplaceGeoSize);
937 fValY -= nCoordTop;
938 if (bScale)
940 fValY *= fYScale;
942 return basegfx::B2DPoint(fValX,fValY);
945 Point EnhancedCustomShape2d::GetPoint( const css::drawing::EnhancedCustomShapeParameterPair& rPair,
946 const bool bScale, const bool bReplaceGeoSize ) const
948 basegfx::B2DPoint aPoint(GetPointAsB2DPoint(rPair, bScale, bReplaceGeoSize));
949 return Point(static_cast<long>(aPoint.getX()), static_cast<long>(aPoint.getY()));
952 void EnhancedCustomShape2d::GetParameter( double& rRetValue, const EnhancedCustomShapeParameter& rParameter,
953 const bool bReplaceGeoWidth, const bool bReplaceGeoHeight ) const
955 rRetValue = 0.0;
956 switch ( rParameter.Type )
958 case EnhancedCustomShapeParameterType::ADJUSTMENT :
960 sal_Int32 nAdjustmentIndex = 0;
961 if ( rParameter.Value >>= nAdjustmentIndex )
963 rRetValue = GetAdjustValueAsDouble( nAdjustmentIndex );
966 break;
967 case EnhancedCustomShapeParameterType::EQUATION :
969 sal_Int32 nEquationIndex = 0;
970 if ( rParameter.Value >>= nEquationIndex )
972 rRetValue = GetEquationValueAsDouble( nEquationIndex );
975 break;
976 case EnhancedCustomShapeParameterType::NORMAL :
978 if ( rParameter.Value.getValueTypeClass() == TypeClass_DOUBLE )
980 double fValue(0.0);
981 if ( rParameter.Value >>= fValue )
983 rRetValue = fValue;
986 else
988 sal_Int32 nValue = 0;
989 if ( rParameter.Value >>= nValue )
991 rRetValue = nValue;
992 if ( bReplaceGeoWidth && ( nValue == nCoordWidth ) )
993 rRetValue *= fXRatio;
994 else if ( bReplaceGeoHeight && ( nValue == nCoordHeight ) )
995 rRetValue *= fYRatio;
999 break;
1000 case EnhancedCustomShapeParameterType::LEFT :
1002 rRetValue = 0.0;
1004 break;
1005 case EnhancedCustomShapeParameterType::TOP :
1007 rRetValue = 0.0;
1009 break;
1010 case EnhancedCustomShapeParameterType::RIGHT :
1012 rRetValue = nCoordWidth;
1014 break;
1015 case EnhancedCustomShapeParameterType::BOTTOM :
1017 rRetValue = nCoordHeight;
1019 break;
1023 // nLumDat 28-31 = number of luminance entries in nLumDat
1024 // nLumDat 27-24 = nLumDatEntry 0
1025 // nLumDat 23-20 = nLumDatEntry 1 ...
1026 // each 4bit entry is to be interpreted as a 10 percent signed luminance changing
1027 sal_Int32 EnhancedCustomShape2d::GetLuminanceChange( sal_uInt32 nIndex ) const
1029 const sal_uInt32 nCount = nColorData >> 28;
1030 if ( !nCount )
1031 return 0;
1033 if ( nIndex >= nCount )
1034 nIndex = nCount - 1;
1036 const sal_Int32 nLumDat = nColorData << ( ( 1 + nIndex ) << 2 );
1037 return ( nLumDat >> 28 ) * 10;
1040 Color EnhancedCustomShape2d::GetColorData( const Color& rFillColor, sal_uInt32 nIndex, double dBrightness ) const
1042 if ( bOOXMLShape || ( mso_sptMin == eSpType /* ODF "non-primitive" */ ) )
1043 { //do LibreOffice way, using dBrightness
1044 if ( dBrightness == 0.0)
1046 return rFillColor;
1048 else
1050 if (dBrightness >=0.0)
1051 { //lighten, blending with white
1052 return Color( static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetRed() * (1.0-dBrightness) + dBrightness * 255.0, 0.0, 255.0) )),
1053 static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetGreen() * (1.0-dBrightness) + dBrightness * 255.0, 0.0, 255.0) )),
1054 static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetBlue() * (1.0-dBrightness) + dBrightness * 255.0, 0.0, 255.0) )) );
1056 else
1057 { //darken (indicated by negative sign), blending with black
1058 return Color( static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetRed() * (1.0+dBrightness), 0.0, 255.0) )),
1059 static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetGreen() * (1.0+dBrightness), 0.0, 255.0) )),
1060 static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetBlue() * (1.0+dBrightness), 0.0, 255.0) )) );
1064 else
1065 { //do OpenOffice way, using nColorData
1066 const sal_Int32 nLuminance = GetLuminanceChange(nIndex);
1067 if( !nLuminance )
1068 return rFillColor;
1070 basegfx::BColor aHSVColor=
1071 basegfx::utils::rgb2hsv(
1072 basegfx::BColor(rFillColor.GetRed()/255.0,
1073 rFillColor.GetGreen()/255.0,
1074 rFillColor.GetBlue()/255.0));
1076 if( nLuminance > 0 )
1078 aHSVColor.setGreen(
1079 aHSVColor.getGreen() * (1.0-nLuminance/100.0));
1080 aHSVColor.setBlue(
1081 nLuminance/100.0 +
1082 (1.0-nLuminance/100.0)*aHSVColor.getBlue());
1084 else if( nLuminance < 0 )
1086 aHSVColor.setBlue(
1087 (1.0+nLuminance/100.0)*aHSVColor.getBlue());
1090 aHSVColor = basegfx::utils::hsv2rgb(aHSVColor);
1091 return Color( static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(aHSVColor.getRed(),0.0,1.0) * 255.0 + 0.5 )),
1092 static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(aHSVColor.getGreen(),0.0,1.0) * 255.0 + 0.5 )),
1093 static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(aHSVColor.getBlue(),0.0,1.0) * 255.0 + 0.5 )) );
1097 tools::Rectangle EnhancedCustomShape2d::GetTextRect() const
1099 if ( !seqTextFrames.hasElements() )
1100 return aLogicRect;
1101 sal_Int32 nIndex = 0;
1102 Point aTopLeft( GetPoint( seqTextFrames[ nIndex ].TopLeft, !bOOXMLShape, true ) );
1103 Point aBottomRight( GetPoint( seqTextFrames[ nIndex ].BottomRight, !bOOXMLShape, true ) );
1104 tools::Rectangle aRect( aTopLeft, aBottomRight );
1105 if ( bFlipH )
1107 aRect.SetLeft(aLogicRect.GetWidth() - 1 - aBottomRight.X());
1108 aRect.SetRight( aLogicRect.GetWidth() - 1 - aTopLeft.X());
1110 if ( bFlipV )
1112 aRect.SetTop(aLogicRect.GetHeight() - 1 - aBottomRight.Y());
1113 aRect.SetBottom(aLogicRect.GetHeight() - 1 - aTopLeft.Y());
1115 SAL_INFO("svx", aRect.GetWidth() << " x " << aRect.GetHeight());
1116 if( aRect.GetWidth() <= 1 || aRect.GetHeight() <= 1 )
1117 return aLogicRect;
1118 aRect.Move( aLogicRect.Left(), aLogicRect.Top() );
1119 aRect.Justify();
1120 return aRect;
1123 sal_uInt32 EnhancedCustomShape2d::GetHdlCount() const
1125 return seqHandles.getLength();
1128 bool EnhancedCustomShape2d::GetHandlePosition( const sal_uInt32 nIndex, Point& rReturnPosition ) const
1130 bool bRetValue = false;
1131 if ( nIndex < GetHdlCount() )
1133 Handle aHandle;
1134 if ( ConvertSequenceToEnhancedCustomShape2dHandle( seqHandles[ nIndex ], aHandle ) )
1136 if ( aHandle.nFlags & HandleFlags::POLAR )
1138 Point aReferencePoint( GetPoint( aHandle.aPolar ) );
1140 double fAngle;
1141 double fRadius;
1142 GetParameter( fRadius, aHandle.aPosition.First, false, false );
1143 GetParameter( fAngle, aHandle.aPosition.Second, false, false );
1145 double a = basegfx::deg2rad(360.0 - fAngle);
1146 double dx = fRadius * fXScale;
1147 double fX = dx * cos( a );
1148 double fY =-dx * sin( a );
1149 rReturnPosition =
1150 Point(
1151 FRound( fX + aReferencePoint.X() ),
1152 basegfx::fTools::equalZero(fXScale) ? aReferencePoint.Y() :
1153 FRound( ( fY * fYScale ) / fXScale + aReferencePoint.Y() ) );
1155 else
1157 if ( aHandle.nFlags & HandleFlags::SWITCHED )
1159 if ( aLogicRect.GetHeight() > aLogicRect.GetWidth() )
1161 css::drawing::EnhancedCustomShapeParameter aFirst = aHandle.aPosition.First;
1162 css::drawing::EnhancedCustomShapeParameter aSecond = aHandle.aPosition.Second;
1163 aHandle.aPosition.First = aSecond;
1164 aHandle.aPosition.Second = aFirst;
1167 if (bOOXMLShape)
1168 rReturnPosition = GetPoint(aHandle.aPosition, false /*bScale*/);
1169 else
1170 rReturnPosition = GetPoint(aHandle.aPosition, true /*bScale*/);
1172 const GeoStat aGeoStat(mrSdrObjCustomShape.GetGeoStat());
1173 if ( aGeoStat.nShearAngle )
1175 double nTan = aGeoStat.nTan;
1176 if (bFlipV != bFlipH)
1177 nTan = -nTan;
1178 ShearPoint( rReturnPosition, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), nTan );
1180 if ( nRotateAngle )
1182 double a = nRotateAngle * F_PI18000;
1183 RotatePoint( rReturnPosition, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), sin( a ), cos( a ) );
1185 if ( bFlipH )
1186 rReturnPosition.setX( aLogicRect.GetWidth() - rReturnPosition.X() );
1187 if ( bFlipV )
1188 rReturnPosition.setY( aLogicRect.GetHeight() - rReturnPosition.Y() );
1189 rReturnPosition.Move( aLogicRect.Left(), aLogicRect.Top() );
1190 bRetValue = true;
1193 return bRetValue;
1196 static double lcl_getXAdjustmentValue(const OUString& rShapeType, const sal_uInt32 nHandleIndex,
1197 const double fX, const double fW, const double fH)
1199 // degenerated shapes are not worth to calculate special case for each shape type
1200 if (fW <= 0.0 || fH <= 0.0)
1201 return 50000;
1203 // pattern (w - x) / ss * 100000 or (r - x) / ss * 100000
1204 if ((rShapeType == "ooxml-bentArrow" && nHandleIndex == 2) || (rShapeType == "ooxml-chevron")
1205 || (rShapeType == "ooxml-curvedRightArrow") || (rShapeType == "ooxml-foldedCorner")
1206 || (rShapeType == "ooxml-homePlate") || (rShapeType == "ooxml-notchedRightArrow")
1207 || (rShapeType == "ooxml-nonIsoscelesTrapezoid" && nHandleIndex == 1)
1208 || (rShapeType == "ooxml-rightArrow")
1209 || (rShapeType == "ooxml-rightArrowCallout" && nHandleIndex == 2)
1210 || (rShapeType == "ooxml-round1Rect")
1211 || (rShapeType == "ooxml-round2DiagRect" && nHandleIndex == 1)
1212 || (rShapeType == "ooxml-round2SameRect" && nHandleIndex == 0)
1213 || (rShapeType == "ooxml-snip1Rect")
1214 || (rShapeType == "ooxml-snip2DiagRect" && nHandleIndex == 1)
1215 || (rShapeType == "ooxml-snip2SameRect" && nHandleIndex == 0)
1216 || (rShapeType == "ooxml-snipRoundRect" && nHandleIndex == 1)
1217 || (rShapeType == "ooxml-swooshArrow") || (rShapeType == "ooxml-stripedRightArrow"))
1218 return (fW - fX) / std::min(fW, fH) * 100000.0;
1220 // pattern x / ss * 100000 or (x - l) / ss * 100000
1221 if ((rShapeType == "ooxml-bentArrow" && nHandleIndex == 0)
1222 || (rShapeType == "ooxml-bentArrow" && nHandleIndex == 3) || (rShapeType == "ooxml-corner")
1223 || (rShapeType == "ooxml-curvedDownArrow") || (rShapeType == "ooxml-curvedLeftArrow")
1224 || (rShapeType == "ooxml-curvedUpArrow") || (rShapeType == "ooxml-leftArrow")
1225 || (rShapeType == "ooxml-leftArrowCallout" && nHandleIndex == 2)
1226 || (rShapeType == "ooxml-leftRightArrow")
1227 || (rShapeType == "ooxml-leftRightArrowCallout" && nHandleIndex == 2)
1228 || (rShapeType == "ooxml-leftRightRibbon")
1229 || (rShapeType == "ooxml-nonIsoscelesTrapezoid" && nHandleIndex == 0)
1230 || (rShapeType == "ooxml-parallelogram")
1231 || (rShapeType == "ooxml-round2DiagRect" && nHandleIndex == 0)
1232 || (rShapeType == "ooxml-round2SameRect" && nHandleIndex == 1)
1233 || (rShapeType == "ooxml-roundRect")
1234 || (rShapeType == "ooxml-snip2DiagRect" && nHandleIndex == 0)
1235 || (rShapeType == "ooxml-snip2SameRect" && nHandleIndex == 1)
1236 || (rShapeType == "ooxml-snipRoundRect" && nHandleIndex == 0)
1237 || (rShapeType == "ooxml-uturnArrow" && nHandleIndex == 0)
1238 || (rShapeType == "ooxml-uturnArrow" && nHandleIndex == 3))
1239 return fX / std::min(fW, fH) * 100000.0;
1241 // pattern (hc - x) / ss * 200000
1242 if ((rShapeType == "ooxml-downArrowCallout" && nHandleIndex == 0)
1243 || (rShapeType == "ooxml-leftRightUpArrow" && nHandleIndex == 0)
1244 || (rShapeType == "ooxml-quadArrow" && nHandleIndex == 0)
1245 || (rShapeType == "ooxml-quadArrowCallout" && nHandleIndex == 0)
1246 || (rShapeType == "ooxml-upArrowCallout" && nHandleIndex == 0)
1247 || (rShapeType == "ooxml-upDownArrowCallout" && nHandleIndex == 0))
1248 return (fW / 2.0 - fX) / std::min(fW, fH) * 200000.0;
1250 // pattern (hc - x) / ss * 100000
1251 if ((rShapeType == "ooxml-downArrowCallout" && nHandleIndex == 1)
1252 || (rShapeType == "ooxml-leftRightUpArrow" && nHandleIndex == 1)
1253 || (rShapeType == "ooxml-quadArrow" && nHandleIndex == 1)
1254 || (rShapeType == "ooxml-quadArrowCallout" && nHandleIndex == 1)
1255 || (rShapeType == "ooxml-upArrowCallout" && nHandleIndex == 1)
1256 || (rShapeType == "ooxml-upDownArrowCallout" && nHandleIndex == 1))
1257 return (fW / 2.0 - fX) / std::min(fW, fH) * 100000.0;
1259 // pattern (w - x) / ss * 50000 or (r - x) / ss * 50000
1260 if ((rShapeType == "ooxml-bentUpArrow") || (rShapeType == "ooxml-leftUpArrow")
1261 || (rShapeType == "ooxml-uturnArrow" && nHandleIndex == 1))
1262 return (fW - fX) / std::min(fW, fH) * 50000.0;
1264 // pattern x / ss * 200000
1265 if (rShapeType == "ooxml-nonIsoscelesTrapezoid" && nHandleIndex == 0)
1266 return fX / std::min(fW, fH) * 200000.0;
1268 // pattern (hc - x) / w * 200000
1269 if ((rShapeType == "ooxml-downArrow" && nHandleIndex == 0)
1270 || (rShapeType == "ooxml-ellipseRibbon") || (rShapeType == "ooxml-ellipseRibbon2")
1271 || (rShapeType == "ooxml-leftRightArrowCallout" && nHandleIndex == 3)
1272 || (rShapeType == "ooxml-ribbon") || (rShapeType == "ooxml-ribbon2")
1273 || (rShapeType == "ooxml-upArrow" && nHandleIndex == 0)
1274 || (rShapeType == "ooxml-upDownArrow" && nHandleIndex == 0))
1275 return (fW / 2.0 - fX) / fW * 200000.0;
1277 // pattern (x - hc) / w * 100000
1278 if ((rShapeType == "ooxml-cloudCallout") || (rShapeType == "ooxml-doubleWave")
1279 || (rShapeType == "ooxml-wave") || (rShapeType == "ooxml-wedgeEllipseCallout")
1280 || (rShapeType == "ooxml-wedgeRectCallout")
1281 || (rShapeType == "ooxml-wedgeRoundRectCallout"))
1282 return (fX - fW / 2.0) / fW * 100000.0;
1284 // pattern (x - hc) / w * 200000
1285 if (rShapeType == "ooxml-teardrop")
1286 return (fX - fW / 2.0) / fW * 200000.0;
1288 // pattern (w - x) / w * 100000 or (r - x) / w * 100000
1289 if (rShapeType == "ooxml-leftArrowCallout" && nHandleIndex == 3)
1290 return (fW - fX) / fW * 100000.0;
1292 // pattern (hc - x) / h * 100000
1293 if (rShapeType == "ooxml-mathDivide")
1294 return (fW / 2.0 - fX) / fH * 100000.0;
1296 // pattern x / w * 100000, simple scaling
1297 if (rShapeType.startsWith("ooxml-"))
1298 return fX / fW * 100000.0;
1300 return fX; // method is unknown
1303 static double lcl_getYAdjustmentValue(const OUString& rShapeType, const sal_uInt32 nHandleIndex,
1304 const double fY, const double fW, const double fH)
1306 // degenerated shapes are not worth to calculate a special case for each shape type
1307 if (fW <= 0.0 || fH <= 0.0)
1308 return 50000;
1310 // pattern (vc - y) / ss * 100000
1311 if ((rShapeType == "ooxml-leftArrowCallout" && nHandleIndex == 1)
1312 || (rShapeType == "ooxml-leftRightArrowCallout" && nHandleIndex == 1)
1313 || (rShapeType == "ooxml-rightArrowCallout" && nHandleIndex == 1))
1314 return (fH / 2.0 - fY) / std::min(fW, fH) * 100000.0;
1316 // pattern (vc - y) / ss * 200000
1317 if ((rShapeType == "ooxml-curvedLeftArrow") || (rShapeType == "ooxml-curvedRightArrow")
1318 || (rShapeType == "ooxml-leftArrowCallout" && nHandleIndex == 0)
1319 || (rShapeType == "ooxml-leftRightArrowCallout" && nHandleIndex == 0)
1320 || (rShapeType == "ooxml-mathPlus")
1321 || (rShapeType == "ooxml-rightArrowCallout" && nHandleIndex == 0))
1322 return (fH / 2.0 - fY) / std::min(fW, fH) * 200000.0;
1324 // pattern (h - y) / ss * 100000 or (b - y) / ss * 100000
1325 if ((rShapeType == "ooxml-bentUpArrow" && nHandleIndex == 0) || (rShapeType == "ooxml-corner")
1326 || (rShapeType == "ooxml-curvedDownArrow") || (rShapeType == "ooxml-downArrow")
1327 || (rShapeType == "ooxml-downArrowCallout" && nHandleIndex == 2)
1328 || (rShapeType == "ooxml-uturnArrow" && nHandleIndex == 2))
1329 return (fH - fY) / std::min(fW, fH) * 100000.0;
1331 // pattern (h - y) / ss * 200000 or (b - y) / ss * 200000
1332 if (rShapeType == "ooxml-leftUpArrow" && nHandleIndex == 0) // - adj2 * 2 outside
1333 return (fH - fY) / std::min(fW, fH) * 200000.0;
1335 // pattern y / ss * 100000 or (y - t) / ss * 100000
1336 if ((rShapeType == "ooxml-bentUpArrow" && nHandleIndex == 2)
1337 || (rShapeType == "ooxml-bracePair") || (rShapeType == "ooxml-bracketPair")
1338 || (rShapeType == "ooxml-can") || (rShapeType == "ooxml-cube")
1339 || (rShapeType == "ooxml-curvedUpArrow") || (rShapeType == "ooxml-halfFrame")
1340 || (rShapeType == "ooxml-leftBrace" && nHandleIndex == 0)
1341 || (rShapeType == "ooxml-leftBracket") || (rShapeType == "ooxml-leftRightUpArrow")
1342 || (rShapeType == "ooxml-leftUpArrow" && nHandleIndex == 2)
1343 || (rShapeType == "ooxml-mathMultiply") || (rShapeType == "ooxml-quadArrow")
1344 || (rShapeType == "ooxml-quadArrowCallout" && nHandleIndex == 2)
1345 || (rShapeType == "ooxml-rightBrace" && nHandleIndex == 0)
1346 || (rShapeType == "ooxml-rightBracket") || (rShapeType == "ooxml-upArrow")
1347 || (rShapeType == "ooxml-upArrowCallout" && nHandleIndex == 2)
1348 || (rShapeType == "ooxml-upDownArrow")
1349 || (rShapeType == "ooxml-upDownArrowCallout" && nHandleIndex == 2)
1350 || (rShapeType == "ooxml-verticalScroll"))
1351 return fY / std::min(fW, fH) * 100000.0;
1353 // pattern y / ss * 50000
1354 if (rShapeType == "ooxml-bentArrow")
1355 return fY / std::min(fW, fH) * 50000.0;
1357 // pattern (vc - y) / h * 100000
1358 if ((rShapeType == "ooxml-mathDivide" && nHandleIndex == 1) // -adj1 / 2 - adj3 outside
1359 || (rShapeType == "ooxml-mathEqual" && nHandleIndex == 0) // -adj2 / 2 outside
1360 || (rShapeType == "ooxml-mathNotEqual" && nHandleIndex == 0) // -adj3 / 2 outside
1361 || (rShapeType == "ooxml-star4") || (rShapeType == "ooxml-star6")
1362 || (rShapeType == "ooxml-star8") || (rShapeType == "ooxml-star10")
1363 || (rShapeType == "ooxml-star12") || (rShapeType == "ooxml-star16")
1364 || (rShapeType == "ooxml-star24") || (rShapeType == "ooxml-star32"))
1365 return (fH / 2.0 - fY) / fH * 100000.0;
1367 // pattern (vc - y) / h * 200000
1368 if ((rShapeType == "ooxml-leftArrow") || (rShapeType == "ooxml-leftRightArrow")
1369 || (rShapeType == "ooxml-mathDivide" && nHandleIndex == 0)
1370 || (rShapeType == "ooxml-mathEqual" && nHandleIndex == 1)
1371 || (rShapeType == "ooxml-mathMinus") || (rShapeType == "ooxml-notchedRightArrow")
1372 || (rShapeType == "ooxml-mathNotEqual" && nHandleIndex == 2)
1373 || (rShapeType == "ooxml-quadArrowCallout" && nHandleIndex == 3)
1374 || (rShapeType == "ooxml-rightArrow") || (rShapeType == "ooxml-stripedRightArrow")
1375 || (rShapeType == "ooxml-upDownArrowCallout" && nHandleIndex == 3))
1376 return (fH / 2.0 - fY) / fH * 200000.0;
1378 // pattern (y - vc) / h * 100000
1379 if ((rShapeType == "ooxml-cloudCallout") || (rShapeType == "ooxml-wedgeEllipseCallout")
1380 || (rShapeType == "ooxml-wedgeRectCallout")
1381 || (rShapeType == "ooxml-wedgeRoundRectCallout"))
1382 return (fY - fH / 2.0) / fH * 100000.0;
1384 // pattern (h - y) / h * 100000 or (b - y) / h * 100000
1385 if ((rShapeType == "ooxml-ellipseRibbon" && nHandleIndex == 2)
1386 || (rShapeType == "ooxml-ellipseRibbon2" && nHandleIndex == 0)
1387 || (rShapeType == "ooxml-ribbon2")
1388 || (rShapeType == "ooxml-upArrowCallout" && nHandleIndex == 3))
1389 return (fH - fY) / fH * 100000.0;
1391 // special pattern smiley
1392 if (rShapeType == "ooxml-smileyFace")
1393 return (fY - fH * 16515.0 / 21600.0) / fH * 100000.0;
1395 // special pattern for star with odd number of tips, because center of star not center of shape
1396 if (rShapeType == "ooxml-star5")
1397 return (fH / 2.0 - fY * 100000.0 / 110557.0) / fH * 100000.0;
1398 if (rShapeType == "ooxml-star7")
1399 return (fH / 2.0 - fY * 100000.0 / 105210.0) / fH * 100000.0;
1401 // special pattern swooshArrow
1402 if (rShapeType == "ooxml-swooshArrow")
1403 return (fY - std::min(fW, fH) / 8.0) / fH * 100000.0;
1405 // special pattern leftRightRibbon
1406 if (rShapeType == "ooxml-leftRightRibbon")
1407 return fY / fH * 200000 - 100000;
1409 // pattern y / h * 100000, simple scaling
1410 if (rShapeType.startsWith("ooxml-"))
1411 return fY / fH * 100000.0;
1413 return fY; // method is unknown
1416 static double lcl_getAngleInOOXMLUnit(double fDY, double fDX)
1418 if (fDX != 0.0 || fDY != 0.0)
1420 double fAngleRad(atan2(fDY, fDX));
1421 double fAngle = basegfx::rad2deg(fAngleRad);
1422 // atan2 returns angle in ]-pi; pi], OOXML preset shapes use [0;360[.
1423 if (fAngle < 0.0)
1424 fAngle += 360.0;
1425 // OOXML uses angle unit 1/60000 degree.
1426 fAngle *= 60000.0;
1427 return fAngle;
1429 return 0.0; // no angle defined for origin in polar coordinate system
1432 static double lcl_getRadiusDistance(double fWR, double fHR, double fX, double fY)
1434 // Get D so, that point (fX|fY) is on the ellipse, that has width fWR-D and
1435 // height fHR-D and center in origin.
1436 // Get solution of ellipse equation (fX/(fWR-D))^2 + (fY/(fHR-D)^2 = 1 by solving
1437 // fX^2*(fHR-D)^2 + fY^2*(fWR-D)^2 - (fWR-D)^2 * (fHR-D)^2 = 0 with Newton-method.
1438 if (fX == 0.0)
1439 return std::min(fHR - fY, fWR);
1440 else if (fY == 0.0)
1441 return std::min(fWR - fX, fHR);
1443 double fD = std::min(fWR, fHR) - sqrt(fX * fX + fY * fY); // iteration start value
1444 sal_uInt8 nIter(0);
1445 bool bFound(false);
1448 ++nIter;
1449 const double fOldD(fD);
1450 const double fWRmD(fWR - fD);
1451 const double fHRmD(fHR - fD);
1452 double fNumerator
1453 = fX * fX * fHRmD * fHRmD + fY * fY * fWRmD * fWRmD - fWRmD * fWRmD * fHRmD * fHRmD;
1454 double fDenominator
1455 = 2.0 * (fHRmD * (fWRmD * fWRmD - fX * fX) + fWRmD * (fHRmD * fHRmD - fY * fY));
1456 if (fDenominator != 0.0)
1458 fD = fD - fNumerator / fDenominator;
1459 bFound = fabs(fOldD - fD) < 1.0E-12;
1461 else
1462 fD = fD * 0.9; // new start value
1463 } while (nIter < 50 && !bFound);
1464 return fD;
1467 bool EnhancedCustomShape2d::SetHandleControllerPosition( const sal_uInt32 nIndex, const css::awt::Point& rPosition )
1469 // The method name is misleading. Essentially it calculates the adjustment values from a given
1470 // handle position.
1472 // For ooxml-foo shapes, the way to calculate the adjustment value from the handle position depends on
1473 // the type of the shape, therefore need 'Type'.
1474 OUString sShapeType("non-primitive"); // default for ODF
1475 const SdrCustomShapeGeometryItem& rGeometryItem(mrSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
1476 const Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
1477 if (pAny)
1478 *pAny >>= sShapeType;
1480 bool bRetValue = false;
1481 if ( nIndex < GetHdlCount() )
1483 Handle aHandle;
1484 if ( ConvertSequenceToEnhancedCustomShape2dHandle( seqHandles[ nIndex ], aHandle ) )
1486 Point aP( rPosition.X, rPosition.Y );
1487 // apply the negative object rotation to the controller position
1489 aP.Move( -aLogicRect.Left(), -aLogicRect.Top() );
1490 if ( bFlipH )
1491 aP.setX( aLogicRect.GetWidth() - aP.X() );
1492 if ( bFlipV )
1493 aP.setY( aLogicRect.GetHeight() - aP.Y() );
1494 if ( nRotateAngle )
1496 double a = -nRotateAngle * F_PI18000;
1497 RotatePoint( aP, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), sin( a ), cos( a ) );
1499 const GeoStat aGeoStat(mrSdrObjCustomShape.GetGeoStat());
1500 if ( aGeoStat.nShearAngle )
1502 double nTan = -aGeoStat.nTan;
1503 if (bFlipV != bFlipH)
1504 nTan = -nTan;
1505 ShearPoint( aP, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), nTan );
1508 double fPos1 = aP.X(); //( bFlipH ) ? aLogicRect.GetWidth() - aP.X() : aP.X();
1509 double fPos2 = aP.Y(); //( bFlipV ) ? aLogicRect.GetHeight() -aP.Y() : aP.Y();
1510 fPos1 = !basegfx::fTools::equalZero(fXScale) ? (fPos1 / fXScale) : SAL_MAX_INT32;
1511 fPos2 = !basegfx::fTools::equalZero(fYScale) ? (fPos2 / fYScale) : SAL_MAX_INT32;
1512 // revert -nCoordLeft and -nCoordTop aus GetPoint()
1513 fPos1 += nCoordLeft;
1514 fPos2 += nCoordTop;
1516 // Used for scaling the adjustment values based on handle positions
1517 double fWidth;
1518 double fHeight;
1520 if ( nCoordWidth || nCoordHeight )
1522 fWidth = nCoordWidth;
1523 fHeight = nCoordHeight;
1525 else
1527 fWidth = aLogicRect.GetWidth();
1528 fHeight = aLogicRect.GetHeight();
1531 if ( aHandle.nFlags & HandleFlags::SWITCHED )
1533 if ( aLogicRect.GetHeight() > aLogicRect.GetWidth() )
1535 double fX = fPos1;
1536 double fY = fPos2;
1537 double fTmp = fWidth;
1538 fPos1 = fY;
1539 fPos2 = fX;
1540 fHeight = fWidth;
1541 fWidth = fTmp;
1545 sal_Int32 nFirstAdjustmentValue = -1, nSecondAdjustmentValue = -1;
1547 // ODF shapes are expected to use a direct binding between position and adjustment
1548 // values. OOXML preset shapes use known formulas. These are calculated backward to
1549 // get the adjustment values. So far we do not have a general method to calculate
1550 // the adjustment values for any shape from the handle position.
1551 if ( aHandle.aPosition.First.Type == EnhancedCustomShapeParameterType::ADJUSTMENT )
1552 aHandle.aPosition.First.Value >>= nFirstAdjustmentValue;
1553 if ( aHandle.aPosition.Second.Type == EnhancedCustomShapeParameterType::ADJUSTMENT )
1554 aHandle.aPosition.Second.Value>>= nSecondAdjustmentValue;
1556 if ( aHandle.nFlags & ( HandleFlags::POLAR | HandleFlags::REFR | HandleFlags::REFANGLE))
1557 { // Polar-Handle
1559 if (aHandle.nFlags & HandleFlags::REFR)
1560 nFirstAdjustmentValue = aHandle.nRefR;
1561 if (aHandle.nFlags & HandleFlags::REFANGLE)
1562 nSecondAdjustmentValue = aHandle.nRefAngle;
1564 double fAngle(0.0);
1565 double fRadius(0.0);
1566 // 'then' treats only shapes of type "ooxml-foo", fontwork shapes have been mapped
1567 // to MS binary import and will be treated in 'else'.
1568 if (bOOXMLShape)
1570 // DrawingML polar handles set REFR or REFANGLE instead of POLAR
1571 // use the shape center instead.
1572 double fDX = fPos1 - fWidth / 2.0;
1573 double fDY = fPos2 - fHeight / 2.0;
1575 // There exists no common pattern. 'radius' or 'angle' might have special meaning.
1576 if (sShapeType == "ooxml-blockArc" && nIndex == 1)
1578 // usual angle, special radius
1579 fAngle = lcl_getAngleInOOXMLUnit(fDY, fDX);
1580 // The value connected to REFR is the _difference_ between the outer
1581 // ellipse given by shape width and height and the inner ellipse through
1582 // the handle position.
1583 double fRadiusDifference
1584 = lcl_getRadiusDistance(fWidth / 2.0, fHeight / 2.0, fDX, fDY);
1585 double fss(std::min(fWidth, fHeight));
1586 if (fss != 0)
1587 fRadius = fRadiusDifference * 100000.0 / fss;
1589 else if (sShapeType == "ooxml-donut" || sShapeType == "ooxml-noSmoking")
1591 // no angle adjustment, radius bound to x-coordinate of handle
1592 double fss(std::min(fWidth, fHeight));
1593 if (fss != 0.0)
1594 fRadius = fPos1 * 100000.0 / fss;
1596 else if ((sShapeType == "ooxml-circularArrow"
1597 || sShapeType == "ooxml-leftRightCircularArrow"
1598 || sShapeType == "ooxml-leftCircularArrow")
1599 && nIndex == 0)
1601 // The value adj2 is the increase compared to the angle in adj3
1602 double fHandleAngle = lcl_getAngleInOOXMLUnit(fDY, fDX);
1603 if (sShapeType == "ooxml-leftCircularArrow")
1604 fAngle = GetAdjustValueAsDouble(2) - fHandleAngle;
1605 else
1606 fAngle = fHandleAngle - GetAdjustValueAsDouble(2);
1607 if (fAngle < 0.0) // 0deg to 360deg cut
1608 fAngle += 21600000.0;
1609 // no REFR
1611 else if ((sShapeType == "ooxml-circularArrow"
1612 || sShapeType == "ooxml-leftCircularArrow"
1613 || sShapeType == "ooxml-leftRightCircularArrow")
1614 && nIndex == 2)
1616 // The value adj1 connected to REFR is the thickness of the arc. The adjustvalue adj5
1617 // has the _difference_ between the outer ellipse given by shape width and height
1618 // and the middle ellipse of the arc. The handle is on the outer side of the
1619 // arc. So we calculate the difference between the ellipse through the handle
1620 // and the outer ellipse and subtract then.
1621 double fRadiusDifferenceHandle
1622 = lcl_getRadiusDistance(fWidth / 2.0, fHeight / 2.0, fDX, fDY);
1623 double fadj5(GetAdjustValueAsDouble(4));
1624 double fss(std::min(fWidth, fHeight));
1625 if (fss != 0.0)
1627 fadj5 = fadj5 * fss / 100000.0;
1628 fRadius = 2.0 * (fadj5 - fRadiusDifferenceHandle);
1629 fRadius = fRadius * 100000.0 / fss;
1631 // ToDo: Get angle adj3 exact. Use approximation for now
1632 fAngle = lcl_getAngleInOOXMLUnit(fDY, fDX);
1634 else if ((sShapeType == "ooxml-circularArrow"
1635 || sShapeType == "ooxml-leftCircularArrow"
1636 || sShapeType == "ooxml-leftRightCircularArrow")
1637 && nIndex == 3)
1639 // ToDo: Getting handle position from adjustment value adj5 is complex.
1640 // Analytical or numerical solution for backward calculation is missing.
1641 // Approximation for now, using a line from center through handle position.
1642 double fAngleRad(0.0);
1643 if (fDX != 0.0 || fDY != 0.0)
1644 fAngleRad = atan2(fDY, fDX);
1645 double fHelpX = cos(fAngleRad) * fHeight / 2.0;
1646 double fHelpY = sin(fAngleRad) * fWidth / 2.0;
1647 if (fHelpX != 0.0 || fHelpY != 0.0)
1649 double fHelpAngle = atan2(fHelpY, fHelpX);
1650 double fOuterX = fWidth / 2.0 * cos(fHelpAngle);
1651 double fOuterY = fHeight / 2.0 * sin(fHelpAngle);
1652 double fOuterRadius = sqrt(fOuterX * fOuterX + fOuterY * fOuterY);
1653 double fHandleRadius = sqrt(fDX * fDX + fDY * fDY);
1654 fRadius = (fOuterRadius - fHandleRadius) / 2.0;
1655 double fss(std::min(fWidth, fHeight));
1656 if (fss != 0.0)
1657 fRadius = fRadius * 100000.0 / fss;
1659 // no REFANGLE
1661 else if (sShapeType == "ooxml-mathNotEqual" && nIndex == 1)
1663 double fadj1(GetAdjustValueAsDouble(0));
1664 double fadj3(GetAdjustValueAsDouble(2));
1665 fadj1 = fadj1 * fHeight / 100000.0;
1666 fadj3 = fadj3 * fHeight / 100000.0;
1667 double fDYRefHorizBar = fDY + fadj1 + fadj3;
1668 if (fDX != 0.0 || fDYRefHorizBar != 0.0)
1670 double fRawAngleDeg = basegfx::rad2deg(atan2(fDYRefHorizBar, fDX));
1671 fAngle = (fRawAngleDeg + 180.0) * 60000.0;
1673 // no REFR
1675 else
1677 // no special meaning of radius or angle, suitable for "ooxml-arc",
1678 // "ooxml-chord", "ooxml-pie" and circular arrows value adj4.
1679 fAngle = lcl_getAngleInOOXMLUnit(fDY, fDX);
1680 fRadius = sqrt(fDX * fDX + fDY * fDY);
1681 double fss(std::min(fWidth, fHeight));
1682 if (fss != 0.0)
1683 fRadius = fRadius * 100000.0 / fss;
1686 else // e.g. shapes from ODF, MS binary import or shape type "fontwork-foo"
1688 double fXRef, fYRef;
1689 if (aHandle.nFlags & HandleFlags::POLAR)
1691 GetParameter(fXRef, aHandle.aPolar.First, false, false);
1692 GetParameter(fYRef, aHandle.aPolar.Second, false, false);
1694 else
1696 fXRef = fWidth / 2.0;
1697 fYRef = fHeight / 2.0;
1699 const double fDX = fPos1 - fXRef;
1700 const double fDY = fPos2 - fYRef;
1701 // ToDo: MS binary uses fixed-point number for the angle. Make sure conversion
1702 // to double is done in import and export.
1703 // ToDo: Angle unit is degree, but range ]-180;180] or [0;360[? Assume ]-180;180].
1704 if (fDX != 0.0 || fDY != 0.0)
1706 fRadius = sqrt(fDX * fDX + fDY * fDY);
1707 fAngle = basegfx::rad2deg(atan2(fDY, fDX));
1711 // All formats can restrict the radius to a range
1712 if ( aHandle.nFlags & HandleFlags::RADIUS_RANGE_MINIMUM )
1714 double fMin;
1715 GetParameter( fMin, aHandle.aRadiusRangeMinimum, false, false );
1716 if ( fRadius < fMin )
1717 fRadius = fMin;
1719 if ( aHandle.nFlags & HandleFlags::RADIUS_RANGE_MAXIMUM )
1721 double fMax;
1722 GetParameter( fMax, aHandle.aRadiusRangeMaximum, false, false );
1723 if ( fRadius > fMax )
1724 fRadius = fMax;
1727 if ( nFirstAdjustmentValue >= 0 )
1728 SetAdjustValueAsDouble( fRadius, nFirstAdjustmentValue );
1729 if ( nSecondAdjustmentValue >= 0 )
1730 SetAdjustValueAsDouble( fAngle, nSecondAdjustmentValue );
1732 else // XY-Handle
1734 // Calculating the adjustment values follows in most cases some patterns, which only
1735 // need width and height of the shape and handle position. These patterns are calculated
1736 // in the static, local methods. More complex calculations or additional steps are
1737 // done here.
1738 // Values for corner cases like 'root(negative)' or 'div zero' are meaningless dummies.
1739 // Identifiers often refer to guide names in OOXML shape definitions.
1740 double fAdjustX = fPos1;
1741 double fAdjustY = fPos2;
1742 if (aHandle.nFlags & HandleFlags::REFX)
1744 nFirstAdjustmentValue = aHandle.nRefX;
1745 if ((sShapeType == "ooxml-gear6") || (sShapeType == "ooxml-gear9"))
1747 // special, needs angle calculations
1748 double fss(std::min(fWidth, fHeight));
1749 double fadj1(GetAdjustValueAsDouble(0)); // from point D6 or D9
1750 double fth(fadj1 * fss / 100000.0); // radius difference
1751 double frw(fWidth / 2.0 - fth); // inner ellipse
1752 double frh(fHeight / 2.0 - fth);
1753 double fDX(fPos1 - fWidth / 2.0);
1754 double fDY(fPos2 - fHeight / 2.0);
1755 double fbA(-1.7); // effective angle for point A6 or A9, dummy value
1756 if (fDX != 0.0 || fDY != 0.0)
1757 fbA = atan2(fDY, fDX);
1758 double faA(fbA); // corresponding circle angle, dummy value
1759 double ftmpX(frh * cos(fbA));
1760 double ftmpY(frw * sin(fbA));
1761 if (ftmpX != 0.0 || ftmpY != 0.0)
1762 faA = atan2(ftmpY, ftmpX); // range ]-pi..pi], here -pi < faA < -pi/2
1763 // screen 270 deg = mathematic coordinate system -pi/2
1764 double fha(-F_PI2 - faA); // positive circle angle difference to 270 deg
1765 if (abs(fha) == F_PI2) // should not happen, but ensure no tan(90deg)
1766 fha = 0.12; // dummy value
1767 double flFD(2 * std::min(frw, frh) * tan(fha) - fth);
1768 if (fss != 0.0)
1769 fAdjustX = flFD / fss * 100000.0;
1771 else
1773 fAdjustX
1774 = lcl_getXAdjustmentValue(sShapeType, nIndex, fPos1, fWidth, fHeight);
1775 if ((sShapeType == "ooxml-curvedDownArrow")
1776 || (sShapeType == "ooxml-curvedUpArrow"))
1778 double fss(std::min(fWidth, fHeight));
1779 if (fss != 0.0)
1781 double fadj3(GetAdjustValueAsDouble(2));
1782 double fHScaled(100000.0 * fHeight / fss);
1783 double fRadicand(fHScaled * fHScaled - fadj3 * fadj3);
1784 double fSqrt = fRadicand >= 0.0 ? sqrt(fRadicand) : 0.0;
1785 double fPart(200000.0 * fWidth / fss * (fSqrt + fHScaled));
1786 fAdjustX = fPart - 4.0 * fHScaled * fAdjustX;
1787 if (nIndex == 0)
1789 // calculate adj1
1790 double fadj2(GetAdjustValueAsDouble(1));
1791 fAdjustX = fAdjustX - fadj2 * (fSqrt + fHScaled);
1792 double fDenominator(fSqrt - 3.0 * fHScaled);
1793 fAdjustX /= fDenominator != 0.0 ? fDenominator : 1.0;
1795 else
1797 // nIndex == 1, calculate adj2
1798 double fadj1(GetAdjustValueAsDouble(0));
1799 fAdjustX = fAdjustX - fadj1 * (fSqrt - fHScaled);
1800 double fDenominator(fSqrt + 3.0 * fHScaled);
1801 fAdjustX /= fDenominator != 0.0 ? fDenominator : 1.0;
1808 if (aHandle.nFlags & HandleFlags::REFY)
1810 nSecondAdjustmentValue = aHandle.nRefY;
1811 if ((sShapeType == "ooxml-gear6") || (sShapeType == "ooxml-gear9"))
1813 // special, acts more like a polar handle radius
1814 double fDX = fPos1 - fWidth / 2.0;
1815 double fDY = fPos2 - fHeight / 2.0;
1816 double fRadiusDifference
1817 = lcl_getRadiusDistance(fWidth / 2.0, fHeight / 2.0, fDX, fDY);
1818 double fss(std::min(fWidth, fHeight));
1819 if (fss != 0)
1820 fAdjustY = fRadiusDifference / fss * 100000.0;
1822 else
1824 fAdjustY
1825 = lcl_getYAdjustmentValue(sShapeType, nIndex, fPos2, fWidth, fHeight);
1826 if (sShapeType == "ooxml-mathDivide" && nIndex == 1)
1827 fAdjustY = fAdjustY - GetAdjustValueAsDouble(0) / 2.0
1828 - GetAdjustValueAsDouble(2);
1829 else if (sShapeType == "ooxml-mathEqual" && nIndex == 0)
1830 fAdjustY -= GetAdjustValueAsDouble(1) / 2.0;
1831 else if (sShapeType == "ooxml-mathNotEqual" && nIndex == 0)
1832 fAdjustY -= GetAdjustValueAsDouble(2) / 2.0;
1833 else if (sShapeType == "ooxml-leftUpArrow" && nIndex == 0)
1834 fAdjustY -= GetAdjustValueAsDouble(1) * 2.0;
1835 else if ((sShapeType == "ooxml-curvedRightArrow")
1836 || (sShapeType == "ooxml-curvedLeftArrow"))
1838 double fss(std::min(fWidth, fHeight));
1839 if (fss != 0.0)
1841 double fadj3(GetAdjustValueAsDouble(2));
1842 double fWScaled(100000.0 * fWidth / fss);
1843 double fRadicand(fWScaled * fWScaled - fadj3 * fadj3);
1844 double fSqrt = fRadicand >= 0.0 ? sqrt(fRadicand) : 0.0;
1845 if (nIndex == 0)
1847 // calculate adj1
1848 double fadj2(GetAdjustValueAsDouble(1));
1849 fAdjustY = fWScaled * (2.0 * fAdjustY - fadj2);
1850 fAdjustY += (200000.0 / fss * fHeight - fadj2) * fSqrt;
1851 double fDenominator(fSqrt + fWScaled);
1852 fAdjustY /= fDenominator != 0.0 ? fDenominator : 1.0;
1854 else
1856 // nIndex == 1, calculate adj2
1857 double fadj1(GetAdjustValueAsDouble(0));
1858 fAdjustY = fWScaled * (2.0 * fAdjustY + fadj1);
1859 fAdjustY += (200000.0 / fss * fHeight - fadj1) * fSqrt;
1860 double fDenominator(fSqrt + 3.0 * fWScaled);
1861 fAdjustY /= fDenominator != 0.0 ? fDenominator : 1.0;
1865 else if (sShapeType == "ooxml-uturnArrow" && nIndex == 2)
1867 double fss(std::min(fWidth, fHeight));
1868 if (fss != 0.0)
1870 double fadj5(GetAdjustValueAsDouble(4));
1871 fAdjustY += fHeight / fss * (fadj5 - 100000.0);
1874 else if (sShapeType == "ooxml-leftRightRibbon")
1876 if (nIndex == 0)
1877 fAdjustY = GetAdjustValueAsDouble(2) - fAdjustY;
1878 else // nIndex == 2
1879 fAdjustY = GetAdjustValueAsDouble(0) + fAdjustY;
1884 if ( nFirstAdjustmentValue >= 0 )
1886 if ( aHandle.nFlags & HandleFlags::RANGE_X_MINIMUM ) // check if horizontal handle needs to be within a range
1888 double fXMin;
1889 GetParameter( fXMin, aHandle.aXRangeMinimum, false, false );
1890 if (fAdjustX < fXMin)
1891 fAdjustX = fXMin;
1893 if ( aHandle.nFlags & HandleFlags::RANGE_X_MAXIMUM ) // check if horizontal handle needs to be within a range
1895 double fXMax;
1896 GetParameter( fXMax, aHandle.aXRangeMaximum, false, false );
1897 if (fAdjustX > fXMax)
1898 fAdjustX = fXMax;
1900 SetAdjustValueAsDouble(fAdjustX, nFirstAdjustmentValue);
1902 if ( nSecondAdjustmentValue >= 0 )
1904 if ( aHandle.nFlags & HandleFlags::RANGE_Y_MINIMUM ) // check if vertical handle needs to be within a range
1906 double fYMin;
1907 GetParameter( fYMin, aHandle.aYRangeMinimum, false, false );
1908 if (fAdjustY < fYMin)
1909 fAdjustY = fYMin;
1911 if ( aHandle.nFlags & HandleFlags::RANGE_Y_MAXIMUM ) // check if vertical handle needs to be within a range
1913 double fYMax;
1914 GetParameter( fYMax, aHandle.aYRangeMaximum, false, false );
1915 if (fAdjustY > fYMax)
1916 fAdjustY = fYMax;
1918 SetAdjustValueAsDouble(fAdjustY, nSecondAdjustmentValue);
1921 // and writing them back into the GeometryItem
1922 SdrCustomShapeGeometryItem aGeometryItem(mrSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
1923 css::beans::PropertyValue aPropVal;
1924 aPropVal.Name = "AdjustmentValues";
1925 aPropVal.Value <<= seqAdjustmentValues;
1926 aGeometryItem.SetPropertyValue( aPropVal );
1927 mrSdrObjCustomShape.SetMergedItem( aGeometryItem );
1928 bRetValue = true;
1931 return bRetValue;
1934 void EnhancedCustomShape2d::SwapStartAndEndArrow( SdrObject* pObj ) //#108274
1936 XLineStartItem aLineStart;
1937 aLineStart.SetLineStartValue(pObj->GetMergedItem( XATTR_LINEEND ).GetLineEndValue());
1938 XLineStartWidthItem aLineStartWidth(pObj->GetMergedItem( XATTR_LINEENDWIDTH ).GetValue());
1939 XLineStartCenterItem aLineStartCenter(pObj->GetMergedItem( XATTR_LINEENDCENTER ).GetValue());
1941 XLineEndItem aLineEnd;
1942 aLineEnd.SetLineEndValue(pObj->GetMergedItem( XATTR_LINESTART ).GetLineStartValue());
1943 XLineEndWidthItem aLineEndWidth(pObj->GetMergedItem( XATTR_LINESTARTWIDTH ).GetValue());
1944 XLineEndCenterItem aLineEndCenter(pObj->GetMergedItem( XATTR_LINESTARTCENTER ).GetValue());
1946 pObj->SetMergedItem( aLineStart );
1947 pObj->SetMergedItem( aLineStartWidth );
1948 pObj->SetMergedItem( aLineStartCenter );
1949 pObj->SetMergedItem( aLineEnd );
1950 pObj->SetMergedItem( aLineEndWidth );
1951 pObj->SetMergedItem( aLineEndCenter );
1954 static basegfx::B2DPolygon CreateArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd, const bool bClockwise, bool bFullCircle = false )
1956 tools::Rectangle aRect( rRect );
1957 Point aStart( rStart );
1958 Point aEnd( rEnd );
1960 sal_Int32 bSwapStartEndAngle = 0;
1962 if ( aRect.Left() > aRect.Right() )
1963 bSwapStartEndAngle ^= 0x01;
1964 if ( aRect.Top() > aRect.Bottom() )
1965 bSwapStartEndAngle ^= 0x11;
1966 if ( bSwapStartEndAngle )
1968 aRect.Justify();
1969 if ( bSwapStartEndAngle & 1 )
1971 Point aTmp( aStart );
1972 aStart = aEnd;
1973 aEnd = aTmp;
1977 tools::Polygon aTempPoly( aRect, aStart, aEnd, PolyStyle::Arc, bFullCircle );
1978 basegfx::B2DPolygon aRetval;
1980 if ( bClockwise )
1982 for ( sal_uInt16 j = aTempPoly.GetSize(); j--; )
1984 aRetval.append(basegfx::B2DPoint(aTempPoly[ j ].X(), aTempPoly[ j ].Y()));
1987 else
1989 for ( sal_uInt16 j = 0; j < aTempPoly.GetSize(); j++ )
1991 aRetval.append(basegfx::B2DPoint(aTempPoly[ j ].X(), aTempPoly[ j ].Y()));
1995 return aRetval;
1998 static double lcl_getNormalizedCircleAngleRad(const double fWR, const double fHR, const double fEllipseAngleDeg)
2000 double fRet(0.0);
2001 double fEAngleDeg(fmod(fEllipseAngleDeg, 360.0));
2002 if (fEAngleDeg < 0.0)
2003 fEAngleDeg += 360.0;
2004 const double fX(fHR * cos(basegfx::deg2rad(fEAngleDeg)));
2005 const double fY(fWR * sin(basegfx::deg2rad(fEAngleDeg)));
2006 if (fX != 0.0 || fY != 0.0)
2008 fRet = atan2(fY, fX);
2009 if (fRet < 0.0)
2010 fRet += F_2PI;
2012 return fRet;
2015 static double lcl_getNormalizedAngleRad(const double fCircleAngleDeg)
2017 double fRet(fmod(fCircleAngleDeg, 360.0));
2018 if (fRet < 0.0)
2019 fRet += 360.0;
2020 return basegfx::deg2rad(fRet);
2023 void EnhancedCustomShape2d::CreateSubPath(
2024 sal_Int32& rSrcPt,
2025 sal_Int32& rSegmentInd,
2026 std::vector< std::pair< SdrPathObj*, double> >& rObjectList,
2027 const bool bLineGeometryNeededOnly,
2028 const bool bSortFilledObjectsToBack,
2029 sal_Int32 nIndex)
2031 bool bNoFill = false;
2032 bool bNoStroke = false;
2033 double dBrightness = 0.0; //no blending
2035 basegfx::B2DPolyPolygon aNewB2DPolyPolygon;
2036 basegfx::B2DPolygon aNewB2DPolygon;
2038 SetPathSize( nIndex );
2040 sal_Int32 nSegInfoSize = seqSegments.getLength();
2041 if ( !nSegInfoSize )
2043 for ( const EnhancedCustomShapeParameterPair& rCoordinate : std::as_const(seqCoordinates) )
2045 const Point aTempPoint(GetPoint( rCoordinate, true, true ));
2046 aNewB2DPolygon.append(basegfx::B2DPoint(aTempPoint.X(), aTempPoint.Y()));
2049 aNewB2DPolygon.setClosed(true);
2051 else
2053 sal_Int32 nCoordSize = seqCoordinates.getLength();
2054 for ( ;rSegmentInd < nSegInfoSize; )
2056 sal_Int16 nCommand = seqSegments[ rSegmentInd ].Command;
2057 sal_Int16 nPntCount= seqSegments[ rSegmentInd++ ].Count;
2059 switch ( nCommand )
2061 case NOFILL :
2062 bNoFill = true;
2063 break;
2064 case NOSTROKE :
2065 bNoStroke = true;
2066 break;
2067 case DARKEN :
2068 dBrightness = -0.4; //use sign to distinguish DARKEN from LIGHTEN
2069 break;
2070 case DARKENLESS :
2071 dBrightness = -0.2;
2072 break;
2073 case LIGHTEN :
2074 dBrightness = 0.4;
2075 break;
2076 case LIGHTENLESS :
2077 dBrightness = 0.2;
2078 break;
2079 case MOVETO :
2081 if(aNewB2DPolygon.count() > 1)
2083 // #i76201# Add conversion to closed polygon when first and last points are equal
2084 basegfx::utils::checkClosed(aNewB2DPolygon);
2085 aNewB2DPolyPolygon.append(aNewB2DPolygon);
2088 aNewB2DPolygon.clear();
2090 if ( rSrcPt < nCoordSize )
2092 const Point aTempPoint(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2093 SAL_INFO(
2094 "svx",
2095 "moveTo: " << aTempPoint.X() << ","
2096 << aTempPoint.Y());
2097 aNewB2DPolygon.append(basegfx::B2DPoint(aTempPoint.X(), aTempPoint.Y()));
2100 break;
2101 case ENDSUBPATH :
2102 break;
2103 case CLOSESUBPATH :
2105 if(aNewB2DPolygon.count())
2107 if(aNewB2DPolygon.count() > 1)
2109 aNewB2DPolygon.setClosed(true);
2110 aNewB2DPolyPolygon.append(aNewB2DPolygon);
2113 aNewB2DPolygon.clear();
2116 break;
2117 case CURVETO :
2119 for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( ( rSrcPt + 2 ) < nCoordSize ); i++ )
2121 const Point aControlA(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2122 const Point aControlB(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2123 const Point aEnd(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2125 DBG_ASSERT(aNewB2DPolygon.count(), "EnhancedCustomShape2d::CreateSubPath: Error in adding control point (!)");
2126 aNewB2DPolygon.appendBezierSegment(
2127 basegfx::B2DPoint(aControlA.X(), aControlA.Y()),
2128 basegfx::B2DPoint(aControlB.X(), aControlB.Y()),
2129 basegfx::B2DPoint(aEnd.X(), aEnd.Y()));
2132 break;
2134 case ANGLEELLIPSE: // command U
2135 case ANGLEELLIPSETO: // command T
2137 // Some shapes will need special handling, decide on property 'Type'.
2138 OUString sShpType;
2139 SdrCustomShapeGeometryItem& rGeometryItem = const_cast<SdrCustomShapeGeometryItem&>(mrSdrObjCustomShape.GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY));
2140 Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
2141 if (pAny)
2142 *pAny >>= sShpType;
2143 // User defined shapes in MS binary format, which contain command U or T after import
2144 // in LibreOffice, starts with "mso".
2145 const bool bIsFromBinaryImport(sShpType.startsWith("mso"));
2146 // The only own or imported preset shapes with U command are those listed below.
2147 // Command T is not used in preset shapes.
2148 const std::unordered_set<OUString> aPresetShapesWithU =
2149 { "ellipse", "ring", "smiley", "sun", "forbidden", "flowchart-connector",
2150 "flowchart-summing-junction", "flowchart-or", "cloud-callout"};
2151 std::unordered_set<OUString>::const_iterator aIter = aPresetShapesWithU.find(sShpType);
2152 const bool bIsPresetShapeWithU(aIter != aPresetShapesWithU.end());
2154 for (sal_uInt16 i = 0; (i < nPntCount) && ((rSrcPt + 2) < nCoordSize); i++)
2156 // ANGLEELLIPSE is the same as ANGLEELLIPSETO, only that it
2157 // makes an implicit MOVETO. That ends the previous subpath.
2158 if (ANGLEELLIPSE == nCommand)
2160 if (aNewB2DPolygon.count() > 1)
2162 // #i76201# Add conversion to closed polygon when first and last points are equal
2163 basegfx::utils::checkClosed(aNewB2DPolygon);
2164 aNewB2DPolyPolygon.append(aNewB2DPolygon);
2166 aNewB2DPolygon.clear();
2169 // Read all parameters, but do not finally handle them.
2170 basegfx::B2DPoint aCenter(GetPointAsB2DPoint(seqCoordinates[ rSrcPt ], true, true));
2171 double fWR; // horizontal ellipse radius
2172 double fHR; // vertical ellipse radius
2173 GetParameter(fWR, seqCoordinates[rSrcPt + 1].First, true, false);
2174 GetParameter(fHR, seqCoordinates[rSrcPt + 1].Second, false, true);
2175 double fStartAngle;
2176 GetParameter(fStartAngle, seqCoordinates[rSrcPt + 2].First, false, false);
2177 double fEndAngle;
2178 GetParameter(fEndAngle, seqCoordinates[rSrcPt + 2].Second, false, false);
2179 // Increasing here allows flat case differentiation tree by using 'continue'.
2180 rSrcPt += 3;
2182 double fScaledWR(fWR * fXScale);
2183 double fScaledHR(fHR * fYScale);
2184 if (fScaledWR == 0.0 && fScaledHR == 0.0)
2186 // degenerated ellipse, add center point
2187 aNewB2DPolygon.append(aCenter);
2188 continue;
2191 if (bIsFromBinaryImport)
2193 // If a shape comes from MS binary ('escher') import, the angles are in degrees*2^16
2194 // and the second angle is not an end angle, but a swing angle.
2195 // MS Word shows this behavior: 0deg right, 90deg top, 180deg left and 270deg
2196 // bottom. Third and forth parameter are horizontal and vertical radius, not width
2197 // and height as noted in VML spec. A positive swing angle goes counter-clock
2198 // wise (in user view). The swing angle might go several times around in case
2199 // abs(swing angle) >= 360deg. Stroke accumulates, so that e.g. dash-dot might fill the
2200 // gaps of previous turn. Fill does not accumulate but uses even-odd rule, semi-transparent
2201 // fill does not become darker. The start and end points of the arc are calculated by
2202 // using the angles on a circle and then scaling the circle to the ellipse. Caution, that
2203 // is different from angle handling in ARCANGLETO and ODF.
2204 // The following implementation generates such rendering. It is only for rendering legacy
2205 // MS shapes and independent of the meaning of commands U and T in ODF specification.
2207 // The WordArt shape 'RingOutside' has already angles in degree, all other need
2208 // conversion from fixed-point number.
2209 double fSwingAngle = fEndAngle;
2210 if (sShpType != "mso-spt143")
2212 fStartAngle /= 65536.0;
2213 fSwingAngle = fEndAngle / 65536.0;
2215 // Convert orientation
2216 fStartAngle = -fStartAngle;
2217 fSwingAngle = -fSwingAngle;
2219 fEndAngle = fStartAngle + fSwingAngle;
2220 if (fSwingAngle < 0.0)
2221 std::swap(fStartAngle, fEndAngle);
2222 double fFrom(fStartAngle);
2223 double fTo(fFrom + 180.0);
2224 basegfx::B2DPolygon aTempB2DPolygon;
2225 double fS; // fFrom in radians in [0..2Pi[
2226 double fE; // fTo or fEndAngle in radians in [0..2PI[
2227 while (fTo < fEndAngle)
2229 fS = lcl_getNormalizedAngleRad(fFrom);
2230 fE = lcl_getNormalizedAngleRad(fTo);
2231 aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR, fS,fE));
2232 fFrom = fTo;
2233 fTo += 180.0;
2235 fS = lcl_getNormalizedAngleRad(fFrom);
2236 fE = lcl_getNormalizedAngleRad(fEndAngle);
2237 aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR,fS, fE));
2238 if (fSwingAngle < 0)
2239 aTempB2DPolygon.flip();
2240 aNewB2DPolygon.append(aTempB2DPolygon);
2241 continue;
2244 // The not yet handled shapes are own preset shapes, or preset shapes from MS binary import, or user
2245 // defined shapes, or foreign shapes. Shapes from OOXML import do not use ANGLEELLIPSE or
2246 // ANGLEELLIPSETO, but use ARCANGLETO.
2247 if (bIsPresetShapeWithU)
2249 // Besides "cloud-callout" all preset shapes have angle values '0 360'.
2250 // The imported "cloud-callout" has angle values '0 360' too, only our own "cloud-callout"
2251 // has values '0 23592960'. But that is fixedfloat and means 360*2^16. Thus all these shapes
2252 // have a full ellipse with start at 0deg.
2253 aNewB2DPolygon.append(basegfx::utils::createPolygonFromEllipse(aCenter, fScaledWR, fScaledHR));
2254 continue;
2257 // In all other cases, full ODF conform handling is necessary. ODF rules:
2258 // Third and forth parameter are horizontal and vertical radius.
2259 // An angle determines the start or end point of the segment by intersection of the second angle
2260 // leg with the ellipse. The first angle leg is always the positive x-axis. For the position
2261 // of the intersection points the angle is used modulo 360deg in range [0deg..360deg[.
2262 // The position of range [0deg..360deg[ is the same as in command ARCANGLETO, with 0deg right,
2263 // 90deg bottom, 180deg left and 270deg top. Only if abs(end angle - start angle) == 360 deg,
2264 // a full ellipse is drawn. The segment is always drawn clock wise (in user view) from start
2265 // point to end point. The end point of the segment becomes the new "current" point.
2267 if (fabs(fabs(fEndAngle - fStartAngle) - 360.0) < 1.0E-15)
2269 // draw full ellipse
2270 // Because createPolygonFromEllipseSegment cannot create full ellipse and
2271 // createPolygonFromEllipse has no varying starts, we use two half ellipses.
2272 const double fS(lcl_getNormalizedCircleAngleRad(fWR, fHR, fStartAngle));
2273 const double fH(lcl_getNormalizedCircleAngleRad(fWR, fHR, fStartAngle + 180.0));
2274 const double fE(lcl_getNormalizedCircleAngleRad(fWR, fHR, fEndAngle));
2275 aNewB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR, fS, fH));
2276 aNewB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR, fH, fE));
2277 continue;
2280 // remaining cases with central segment angle < 360
2281 double fS(lcl_getNormalizedCircleAngleRad(fWR, fHR, fStartAngle));
2282 double fE(lcl_getNormalizedCircleAngleRad(fWR, fHR, fEndAngle));
2283 aNewB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR, fS, fE));
2284 } // end for
2285 } // end case
2286 break;
2288 case QUADRATICCURVETO :
2290 for ( sal_Int32 i(0); ( i < nPntCount ) && ( rSrcPt + 1 < nCoordSize ); i++ )
2292 DBG_ASSERT(aNewB2DPolygon.count(), "EnhancedCustomShape2d::CreateSubPath: Error no previous point for Q (!)");
2293 if (aNewB2DPolygon.count() > 0)
2295 const basegfx::B2DPoint aPreviousEndPoint(aNewB2DPolygon.getB2DPoint(aNewB2DPolygon.count()-1));
2296 const basegfx::B2DPoint aControlQ(GetPointAsB2DPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2297 const basegfx::B2DPoint aEnd(GetPointAsB2DPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2298 const basegfx::B2DPoint aControlA((aPreviousEndPoint + (aControlQ * 2)) / 3);
2299 const basegfx::B2DPoint aControlB(((aControlQ * 2) + aEnd) / 3);
2300 aNewB2DPolygon.appendBezierSegment(aControlA, aControlB, aEnd);
2302 else // no previous point; ill structured path, but try to draw as much as possible
2304 rSrcPt++; // skip control point
2305 const basegfx::B2DPoint aEnd(GetPointAsB2DPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2306 aNewB2DPolygon.append(aEnd);
2310 break;
2312 case LINETO :
2314 for ( sal_Int32 i(0); ( i < nPntCount ) && ( rSrcPt < nCoordSize ); i++ )
2316 const Point aTempPoint(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2317 SAL_INFO(
2318 "svx",
2319 "lineTo: " << aTempPoint.X() << ","
2320 << aTempPoint.Y());
2321 aNewB2DPolygon.append(basegfx::B2DPoint(aTempPoint.X(), aTempPoint.Y()));
2324 break;
2326 case ARC :
2327 case CLOCKWISEARC :
2329 if(aNewB2DPolygon.count() > 1)
2331 // #i76201# Add conversion to closed polygon when first and last points are equal
2332 basegfx::utils::checkClosed(aNewB2DPolygon);
2333 aNewB2DPolyPolygon.append(aNewB2DPolygon);
2336 aNewB2DPolygon.clear();
2338 [[fallthrough]];
2340 case ARCTO :
2341 case CLOCKWISEARCTO :
2343 bool bClockwise = ( nCommand == CLOCKWISEARC ) || ( nCommand == CLOCKWISEARCTO );
2344 sal_uInt32 nXor = bClockwise ? 3 : 2;
2345 for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( ( rSrcPt + 3 ) < nCoordSize ); i++ )
2347 tools::Rectangle aRect( GetPoint( seqCoordinates[ rSrcPt ], true, true ), GetPoint( seqCoordinates[ rSrcPt + 1 ], true, true ) );
2348 if ( aRect.GetWidth() && aRect.GetHeight() )
2350 Point aStart( GetPoint( seqCoordinates[ static_cast<sal_uInt16>( rSrcPt + nXor ) ], true, true ) );
2351 Point aEnd( GetPoint( seqCoordinates[ static_cast<sal_uInt16>( rSrcPt + ( nXor ^ 1 ) ) ], true, true ) );
2352 aNewB2DPolygon.append(CreateArc( aRect, aStart, aEnd, bClockwise));
2354 rSrcPt += 4;
2357 break;
2359 case ARCANGLETO :
2361 double fWR, fHR, fStartAngle, fSwingAngle;
2363 for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( rSrcPt + 1 < nCoordSize ); i++ )
2365 GetParameter ( fWR, seqCoordinates[ static_cast<sal_uInt16>(rSrcPt) ].First, true, false );
2366 GetParameter ( fHR, seqCoordinates[ static_cast<sal_uInt16>(rSrcPt) ].Second, false, true );
2368 GetParameter ( fStartAngle, seqCoordinates[ static_cast<sal_uInt16>( rSrcPt + 1) ].First, false, false );
2369 GetParameter ( fSwingAngle, seqCoordinates[ static_cast<sal_uInt16>( rSrcPt + 1 ) ].Second, false, false );
2371 // Convert angles to radians, but don't do any scaling / translation yet.
2373 fStartAngle = basegfx::deg2rad(fStartAngle);
2374 fSwingAngle = basegfx::deg2rad(fSwingAngle);
2376 SAL_INFO("svx", "ARCANGLETO scale: " << fWR << "x" << fHR << " angles: " << fStartAngle << "," << fSwingAngle);
2378 bool bClockwise = fSwingAngle >= 0.0;
2380 if (aNewB2DPolygon.count() > 0)
2382 basegfx::B2DPoint aStartPointB2D( aNewB2DPolygon.getB2DPoint(aNewB2DPolygon.count() - 1 ) );
2383 Point aStartPoint( 0, 0 );
2385 double fT = atan2((fWR*sin(fStartAngle)), (fHR*cos(fStartAngle)));
2386 double fTE = atan2((fWR*sin(fStartAngle + fSwingAngle)), fHR*cos(fStartAngle + fSwingAngle));
2388 SAL_INFO("svx", "ARCANGLETO angles: " << fStartAngle << ", " << fSwingAngle
2389 << " --> parameters: " << fT <<", " << fTE );
2391 fWR *= fXScale;
2392 fHR *= fYScale;
2394 tools::Rectangle aRect ( Point ( aStartPoint.getX() - fWR*cos(fT) - fWR, aStartPoint.getY() - fHR*sin(fT) - fHR ),
2395 Point ( aStartPoint.getX() - fWR*cos(fT) + fWR, aStartPoint.getY() - fHR*sin(fT) + fHR) );
2397 Point aEndPoint ( aStartPoint.getX() - fWR*(cos(fT) - cos(fTE)), aStartPoint.getY() - fHR*(sin(fT) - sin(fTE)) );
2399 SAL_INFO(
2400 "svx",
2401 "ARCANGLETO rect: " << aRect.Left() << ", "
2402 << aRect.Top() << " x " << aRect.Right()
2403 << ", " << aRect.Bottom() << " start: "
2404 << aStartPoint.X() << ", "
2405 << aStartPoint.Y() << " end: "
2406 << aEndPoint.X() << ", " << aEndPoint.Y()
2407 << " clockwise: " << int(bClockwise));
2408 basegfx::B2DPolygon aArc = CreateArc( aRect, bClockwise ? aEndPoint : aStartPoint, bClockwise ? aStartPoint : aEndPoint, bClockwise, aStartPoint == aEndPoint && ((bClockwise && fSwingAngle > F_PI) || (!bClockwise && fSwingAngle < -F_PI)));
2409 // Now that we have the arc, move it to aStartPointB2D.
2410 basegfx::B2DHomMatrix aMatrix = basegfx::utils::createTranslateB2DHomMatrix(aStartPointB2D.getX(), aStartPointB2D.getY());
2411 aArc.transform(aMatrix);
2412 aNewB2DPolygon.append(aArc);
2415 rSrcPt += 2;
2418 break;
2420 case ELLIPTICALQUADRANTX :
2421 case ELLIPTICALQUADRANTY :
2423 if (nPntCount && (rSrcPt < nCoordSize))
2425 // The arc starts at the previous point and ends at the point given in the parameter.
2426 basegfx::B2DPoint aStart;
2427 basegfx::B2DPoint aEnd;
2428 sal_uInt16 i = 0;
2429 if (rSrcPt)
2431 aStart = GetPointAsB2DPoint(seqCoordinates[rSrcPt - 1], true, true);
2433 else
2434 { // no previous point, path is ill-structured. But we want to show as much as possible.
2435 // Thus make a moveTo to the point given as parameter and continue from there.
2436 aStart = GetPointAsB2DPoint(seqCoordinates[static_cast<sal_uInt16>(rSrcPt)], true, true);
2437 aNewB2DPolygon.append(aStart);
2438 rSrcPt++;
2439 i++;
2441 // If there are several points, then the direction changes with every point.
2442 bool bIsXDirection(nCommand == ELLIPTICALQUADRANTX);
2443 basegfx::B2DPolygon aArc;
2444 for ( ; ( i < nPntCount ) && ( rSrcPt < nCoordSize ); i++ )
2446 aEnd = GetPointAsB2DPoint(seqCoordinates[rSrcPt], true, true);
2447 basegfx::B2DPoint aCenter;
2448 double fRadiusX = fabs(aEnd.getX() - aStart.getX());
2449 double fRadiusY = fabs(aEnd.getY() - aStart.getY());
2450 if (bIsXDirection)
2452 aCenter = basegfx::B2DPoint(aStart.getX(),aEnd.getY());
2453 if (aEnd.getX()<aStart.getX())
2455 if (aEnd.getY()<aStart.getY()) // left, up
2457 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, F_PI2, F_PI);
2459 else // left, down
2461 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, F_PI, 1.5*F_PI);
2462 aArc.flip();
2465 else // aEnd.getX()>=aStart.getX()
2467 if (aEnd.getY()<aStart.getY()) // right, up
2469 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, 0.0, F_PI2);
2470 aArc.flip();
2472 else // right, down
2474 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, 1.5*F_PI, F_2PI);
2478 else // y-direction
2480 aCenter = basegfx::B2DPoint(aEnd.getX(),aStart.getY());
2481 if (aEnd.getX()<aStart.getX())
2483 if (aEnd.getY()<aStart.getY()) // up, left
2485 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, 1.5*F_PI, F_2PI);
2486 aArc.flip();
2488 else // down, left
2490 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, 0.0, F_PI2);
2493 else // aEnd.getX()>=aStart.getX()
2495 if (aEnd.getY()<aStart.getY()) // up, right
2497 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, F_PI, 1.5*F_PI);
2499 else // down, right
2501 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, F_PI2, F_PI);
2502 aArc.flip();
2506 aNewB2DPolygon.append(aArc);
2507 rSrcPt++;
2508 bIsXDirection = !bIsXDirection;
2509 aStart = aEnd;
2512 // else error in path syntax, do nothing
2514 break;
2516 #ifdef DBG_CUSTOMSHAPE
2517 case UNKNOWN :
2518 default :
2520 SAL_WARN( "svx", "CustomShapes::unknown PolyFlagValue :" << nCommand );
2522 break;
2523 #endif
2525 if ( nCommand == ENDSUBPATH )
2526 break;
2529 if ( rSegmentInd == nSegInfoSize )
2530 rSegmentInd++;
2532 if(aNewB2DPolygon.count() > 1)
2534 // #i76201# Add conversion to closed polygon when first and last points are equal
2535 basegfx::utils::checkClosed(aNewB2DPolygon);
2536 aNewB2DPolyPolygon.append(aNewB2DPolygon);
2539 if(aNewB2DPolyPolygon.count())
2541 // #i37011#
2542 bool bForceCreateTwoObjects(false);
2544 if(!bSortFilledObjectsToBack && !aNewB2DPolyPolygon.isClosed() && !bNoStroke)
2546 bForceCreateTwoObjects = true;
2549 if(bLineGeometryNeededOnly)
2551 bForceCreateTwoObjects = true;
2552 bNoFill = true;
2553 bNoStroke = false;
2556 if(bForceCreateTwoObjects || bSortFilledObjectsToBack)
2558 if(bFilled && !bNoFill)
2560 basegfx::B2DPolyPolygon aClosedPolyPolygon(aNewB2DPolyPolygon);
2561 aClosedPolyPolygon.setClosed(true);
2562 SdrPathObj* pFill = new SdrPathObj(
2563 mrSdrObjCustomShape.getSdrModelFromSdrObject(),
2564 OBJ_POLY,
2565 aClosedPolyPolygon);
2566 SfxItemSet aTempSet(*this);
2567 aTempSet.Put(makeSdrShadowItem(false));
2568 aTempSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
2569 pFill->SetMergedItemSet(aTempSet);
2570 rObjectList.push_back(std::pair< SdrPathObj*, double >(pFill, dBrightness));
2573 if(!bNoStroke)
2575 // there is no reason to use OBJ_PLIN here when the polygon is actually closed,
2576 // the non-fill is defined by XFILL_NONE. Since SdrPathObj::ImpForceKind() needs
2577 // to correct the polygon (here: open it) using the type, the last edge may get lost.
2578 // Thus, use a type that fits the polygon
2579 SdrPathObj* pStroke = new SdrPathObj(
2580 mrSdrObjCustomShape.getSdrModelFromSdrObject(),
2581 aNewB2DPolyPolygon.isClosed() ? OBJ_POLY : OBJ_PLIN,
2582 aNewB2DPolyPolygon);
2583 SfxItemSet aTempSet(*this);
2584 aTempSet.Put(makeSdrShadowItem(false));
2585 aTempSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
2586 pStroke->SetMergedItemSet(aTempSet);
2587 rObjectList.push_back(std::pair< SdrPathObj*, double >(pStroke, dBrightness));
2590 else
2592 SdrPathObj* pObj = nullptr;
2593 SfxItemSet aTempSet(*this);
2594 aTempSet.Put(makeSdrShadowItem(false));
2596 if(bNoFill)
2598 // see comment above about OBJ_PLIN
2599 pObj = new SdrPathObj(
2600 mrSdrObjCustomShape.getSdrModelFromSdrObject(),
2601 aNewB2DPolyPolygon.isClosed() ? OBJ_POLY : OBJ_PLIN,
2602 aNewB2DPolyPolygon);
2603 aTempSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
2605 else
2607 aNewB2DPolyPolygon.setClosed(true);
2608 pObj = new SdrPathObj(
2609 mrSdrObjCustomShape.getSdrModelFromSdrObject(),
2610 OBJ_POLY,
2611 aNewB2DPolyPolygon);
2614 if(bNoStroke)
2616 aTempSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
2619 pObj->SetMergedItemSet(aTempSet);
2620 rObjectList.push_back(std::pair< SdrPathObj*, double >(pObj, dBrightness));
2625 static void CorrectCalloutArrows(
2626 MSO_SPT eSpType,
2627 sal_uInt32 nLineObjectCount,
2628 std::vector< std::pair< SdrPathObj*, double> >& vObjectList )
2630 bool bAccent = false;
2631 switch( eSpType )
2633 case mso_sptCallout1 :
2634 case mso_sptBorderCallout1 :
2635 case mso_sptCallout90 :
2636 case mso_sptBorderCallout90 :
2637 default:
2638 break;
2640 case mso_sptAccentCallout1 :
2641 case mso_sptAccentBorderCallout1 :
2642 case mso_sptAccentCallout90 :
2643 case mso_sptAccentBorderCallout90 :
2645 sal_uInt32 nLine = 0;
2647 for ( const std::pair< SdrPathObj*, double >& rCandidate : vObjectList )
2649 SdrPathObj* pObj(rCandidate.first);
2651 if(pObj->IsLine())
2653 nLine++;
2654 if ( nLine == nLineObjectCount )
2656 pObj->ClearMergedItem( XATTR_LINESTART );
2657 pObj->ClearMergedItem( XATTR_LINEEND );
2662 break;
2664 // switch start & end
2665 case mso_sptAccentCallout2 :
2666 case mso_sptAccentBorderCallout2 :
2667 bAccent = true;
2668 [[fallthrough]];
2669 case mso_sptCallout2 :
2670 case mso_sptBorderCallout2 :
2672 sal_uInt32 nLine = 0;
2674 for ( const std::pair< SdrPathObj*, double >& rCandidate : vObjectList )
2676 SdrPathObj* pObj(rCandidate.first);
2678 if(pObj->IsLine())
2680 nLine++;
2681 if ( nLine == 1 )
2682 pObj->ClearMergedItem( XATTR_LINEEND );
2683 else if ( ( bAccent && ( nLine == nLineObjectCount - 1 ) ) || ( !bAccent && ( nLine == nLineObjectCount ) ) )
2684 pObj->ClearMergedItem( XATTR_LINESTART );
2685 else
2687 pObj->ClearMergedItem( XATTR_LINESTART );
2688 pObj->ClearMergedItem( XATTR_LINEEND );
2693 break;
2695 case mso_sptAccentCallout3 :
2696 case mso_sptAccentBorderCallout3 :
2697 case mso_sptCallout3 :
2698 case mso_sptBorderCallout3 :
2700 sal_uInt32 nLine = 0;
2702 for ( const std::pair< SdrPathObj*, double >& rCandidate : vObjectList )
2704 SdrPathObj* pObj(rCandidate.first);
2706 if(pObj->IsLine())
2708 if ( nLine )
2710 pObj->ClearMergedItem( XATTR_LINESTART );
2711 pObj->ClearMergedItem( XATTR_LINEEND );
2713 else
2714 EnhancedCustomShape2d::SwapStartAndEndArrow( pObj );
2716 nLine++;
2720 break;
2724 void EnhancedCustomShape2d::AdaptObjColor(
2725 SdrPathObj& rObj,
2726 double dBrightness,
2727 const SfxItemSet& rCustomShapeSet,
2728 sal_uInt32& nColorIndex,
2729 sal_uInt32 nColorCount)
2731 if ( !rObj.IsLine() )
2733 const drawing::FillStyle eFillStyle = rObj.GetMergedItem(XATTR_FILLSTYLE).GetValue();
2734 switch( eFillStyle )
2736 default:
2737 case drawing::FillStyle_SOLID:
2739 Color aFillColor;
2741 if ( nColorCount || 0.0 != dBrightness )
2743 aFillColor = GetColorData(
2744 rCustomShapeSet.Get( XATTR_FILLCOLOR ).GetColorValue(),
2745 std::min(nColorIndex, nColorCount-1),
2746 dBrightness );
2747 rObj.SetMergedItem( XFillColorItem( "", aFillColor ) );
2749 break;
2751 case drawing::FillStyle_GRADIENT:
2753 XGradient aXGradient(rObj.GetMergedItem(XATTR_FILLGRADIENT).GetGradientValue());
2755 if ( nColorCount || 0.0 != dBrightness )
2757 aXGradient.SetStartColor(
2758 GetColorData(
2759 aXGradient.GetStartColor(),
2760 std::min(nColorIndex, nColorCount-1),
2761 dBrightness ));
2762 aXGradient.SetEndColor(
2763 GetColorData(
2764 aXGradient.GetEndColor(),
2765 std::min(nColorIndex, nColorCount-1),
2766 dBrightness ));
2769 rObj.SetMergedItem( XFillGradientItem( "", aXGradient ) );
2770 break;
2772 case drawing::FillStyle_HATCH:
2774 XHatch aXHatch(rObj.GetMergedItem(XATTR_FILLHATCH).GetHatchValue());
2776 if ( nColorCount || 0.0 != dBrightness )
2778 aXHatch.SetColor(
2779 GetColorData(
2780 aXHatch.GetColor(),
2781 std::min(nColorIndex, nColorCount-1),
2782 dBrightness ));
2785 rObj.SetMergedItem( XFillHatchItem( "", aXHatch ) );
2786 break;
2788 case drawing::FillStyle_BITMAP:
2790 if ( nColorCount || 0.0 != dBrightness )
2792 BitmapEx aBitmap(rObj.GetMergedItem(XATTR_FILLBITMAP).GetGraphicObject().GetGraphic().GetBitmapEx());
2794 short nLuminancePercent = static_cast< short > ( GetLuminanceChange(
2795 std::min(nColorIndex, nColorCount-1)));
2796 aBitmap.Adjust( nLuminancePercent, 0, 0, 0, 0 );
2798 rObj.SetMergedItem(XFillBitmapItem(OUString(), Graphic(aBitmap)));
2801 break;
2805 if ( nColorIndex < nColorCount )
2806 nColorIndex++;
2810 SdrObject* EnhancedCustomShape2d::CreatePathObj( bool bLineGeometryNeededOnly )
2812 if ( !seqCoordinates.hasElements() )
2814 return nullptr;
2817 std::vector< std::pair< SdrPathObj*, double > > vObjectList;
2818 const bool bSortFilledObjectsToBack(SortFilledObjectsToBackByDefault(eSpType));
2819 sal_Int32 nSubPathIndex(0);
2820 sal_Int32 nSrcPt(0);
2821 sal_Int32 nSegmentInd(0);
2822 SdrObject* pRet(nullptr);
2824 while( nSegmentInd <= seqSegments.getLength() )
2826 CreateSubPath(
2827 nSrcPt,
2828 nSegmentInd,
2829 vObjectList,
2830 bLineGeometryNeededOnly,
2831 bSortFilledObjectsToBack,
2832 nSubPathIndex);
2833 nSubPathIndex++;
2836 if ( !vObjectList.empty() )
2838 const SfxItemSet& rCustomShapeSet(mrSdrObjCustomShape.GetMergedItemSet());
2839 const sal_uInt32 nColorCount(nColorData >> 28);
2840 sal_uInt32 nColorIndex(0);
2842 // #i37011# remove invisible objects
2843 std::vector< std::pair< SdrPathObj*, double> > vNewList;
2845 for ( const std::pair< SdrPathObj*, double >& rCandidate : vObjectList )
2847 SdrPathObj* pObj(rCandidate.first);
2848 const drawing::LineStyle eLineStyle(pObj->GetMergedItem(XATTR_LINESTYLE).GetValue());
2849 const drawing::FillStyle eFillStyle(pObj->GetMergedItem(XATTR_FILLSTYLE).GetValue());
2851 // #i40600# if bLineGeometryNeededOnly is set, linestyle does not matter
2852 if(!bLineGeometryNeededOnly && (drawing::LineStyle_NONE == eLineStyle) && (drawing::FillStyle_NONE == eFillStyle))
2854 // always use SdrObject::Free(...) for SdrObjects (!)
2855 SdrObject* pTemp(pObj);
2856 SdrObject::Free(pTemp);
2858 else
2860 vNewList.push_back(rCandidate);
2864 vObjectList = vNewList;
2866 if(1 == vObjectList.size())
2868 // a single object, correct some values
2869 AdaptObjColor(
2870 *vObjectList.begin()->first,
2871 vObjectList.begin()->second,
2872 rCustomShapeSet,
2873 nColorIndex,
2874 nColorCount);
2876 else
2878 sal_Int32 nLineObjectCount(0);
2880 // correct some values and collect content data
2881 for ( const std::pair< SdrPathObj*, double >& rCandidate : vObjectList )
2883 SdrPathObj* pObj(rCandidate.first);
2885 if(pObj->IsLine())
2887 nLineObjectCount++;
2889 else
2891 AdaptObjColor(
2892 *pObj,
2893 rCandidate.second,
2894 rCustomShapeSet,
2895 nColorIndex,
2896 nColorCount);
2898 // OperationSmiley: when we have access to the SdrObjCustomShape and the
2899 // CustomShape is built with more than a single filled Geometry, use it
2900 // to define that all helper geometries defined here (SdrObjects currently)
2901 // will use the same FillGeometryDefinition (from the referenced SdrObjCustomShape).
2902 // This will all same-filled objects look like filled smoothly with the same style.
2903 pObj->setFillGeometryDefiningShape(&mrSdrObjCustomShape);
2907 // #i88870# correct line arrows for callouts
2908 if ( nLineObjectCount )
2910 CorrectCalloutArrows(
2911 eSpType,
2912 nLineObjectCount,
2913 vObjectList);
2916 // sort objects so that filled ones are in front. Necessary
2917 // for some strange objects
2918 if(bSortFilledObjectsToBack)
2920 std::vector< std::pair< SdrPathObj*, double> > vTempList;
2921 vTempList.reserve(vObjectList.size());
2923 for ( const std::pair< SdrPathObj*, double >& rCandidate : vObjectList )
2925 SdrPathObj* pObj(rCandidate.first);
2927 if ( !pObj->IsLine() )
2929 vTempList.push_back(rCandidate);
2933 for ( const std::pair< SdrPathObj*, double >& rCandidate : vObjectList )
2935 SdrPathObj* pObj(rCandidate.first);
2937 if ( pObj->IsLine() )
2939 vTempList.push_back(rCandidate);
2943 vObjectList = vTempList;
2948 // #i37011#
2949 if(!vObjectList.empty())
2951 // copy remaining objects to pRet
2952 if(vObjectList.size() > 1)
2954 pRet = new SdrObjGroup(mrSdrObjCustomShape.getSdrModelFromSdrObject());
2956 for ( const std::pair< SdrPathObj*, double >& rCandidate : vObjectList )
2958 SdrPathObj* pObj(rCandidate.first);
2960 pRet->GetSubList()->NbcInsertObject(pObj);
2963 else if(1 == vObjectList.size())
2965 pRet = vObjectList.begin()->first;
2968 if(pRet)
2970 // move to target position
2971 tools::Rectangle aCurRect(pRet->GetSnapRect());
2972 aCurRect.Move(aLogicRect.Left(), aLogicRect.Top());
2973 pRet->NbcSetSnapRect(aCurRect);
2977 return pRet;
2980 SdrObject* EnhancedCustomShape2d::CreateObject( bool bLineGeometryNeededOnly )
2982 SdrObject* pRet = nullptr;
2984 if ( eSpType == mso_sptRectangle )
2986 pRet = new SdrRectObj(mrSdrObjCustomShape.getSdrModelFromSdrObject(), aLogicRect);
2987 pRet->SetMergedItemSet( *this );
2989 if ( !pRet )
2990 pRet = CreatePathObj( bLineGeometryNeededOnly );
2992 return pRet;
2995 void EnhancedCustomShape2d::ApplyGluePoints( SdrObject* pObj )
2997 if ( pObj )
2999 for ( const auto& rGluePoint : std::as_const(seqGluePoints) )
3001 SdrGluePoint aGluePoint;
3003 aGluePoint.SetPos( GetPoint( rGluePoint, true, true ) );
3004 aGluePoint.SetPercent( false );
3005 aGluePoint.SetAlign( SdrAlign::VERT_TOP | SdrAlign::HORZ_LEFT );
3006 aGluePoint.SetEscDir( SdrEscapeDirection::SMART );
3007 SdrGluePointList* pList = pObj->ForceGluePointList();
3008 if( pList )
3009 /* sal_uInt16 nId = */ pList->Insert( aGluePoint );
3014 SdrObject* EnhancedCustomShape2d::CreateLineGeometry()
3016 return CreateObject( true );
3020 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */