Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / svx / source / customshapes / EnhancedCustomShape2d.cxx
blobc4de12bbcfc31b468ab64ea4bbb8c1b590ce17b1
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 "svx/EnhancedCustomShape2d.hxx"
21 #include "svx/EnhancedCustomShapeGeometry.hxx"
22 #include "svx/EnhancedCustomShapeTypeNames.hxx"
23 #include <svx/svdoashp.hxx>
24 #include <svx/svdtrans.hxx>
25 #include <svx/svdocirc.hxx>
26 #include <svx/svdogrp.hxx>
27 #include <svx/svdopath.hxx>
28 #include <svx/svdocapt.hxx>
29 #include <svx/svdpage.hxx>
30 #include <svx/xflclit.hxx>
31 #include <svx/sdasaitm.hxx>
32 #include <svx/svdmodel.hxx>
33 #include <rtl/crc.h>
34 #include <rtl/math.hxx>
35 #include <svx/xfillit0.hxx>
36 #include <svx/xlnstit.hxx>
37 #include <svx/xlnedit.hxx>
38 #include <svx/xlnstwit.hxx>
39 #include <svx/xlnedwit.hxx>
40 #include <svx/xlnstcit.hxx>
41 #include <svx/xlnedcit.hxx>
42 #include <svx/xflgrit.hxx>
43 #include <svx/xflhtit.hxx>
44 #include <svx/xbtmpit.hxx>
45 #include <svx/xgrad.hxx>
46 #include <svx/xhatch.hxx>
47 #include <com/sun/star/awt/Size.hpp>
48 #include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
49 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
50 #include <basegfx/numeric/ftools.hxx>
51 #include <basegfx/color/bcolortools.hxx>
52 #include <basegfx/polygon/b2dpolygon.hxx>
53 #include <basegfx/polygon/b2dpolygontools.hxx>
54 #include <basegfx/matrix/b2dhommatrixtools.hxx>
55 #include <rtl/strbuf.hxx>
56 #include <math.h>
58 using namespace ::com::sun::star;
59 using namespace ::com::sun::star::uno;
60 using namespace ::com::sun::star::drawing;
61 using namespace ::com::sun::star::drawing::EnhancedCustomShapeSegmentCommand;
63 void EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( EnhancedCustomShapeParameter& rParameter, const sal_Int32 nValue )
65 sal_uInt32 nDat = (sal_uInt32)nValue;
66 sal_Int32 nNewValue = nValue;
68 // check if this is a special point
69 if ( ( nDat >> 16 ) == 0x8000 )
71 nNewValue = (sal_uInt16)nDat;
72 rParameter.Type = EnhancedCustomShapeParameterType::EQUATION;
74 else
75 rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
76 rParameter.Value <<= nNewValue;
79 OUString EnhancedCustomShape2d::GetEquation( const sal_uInt16 nFlags, sal_Int32 nP1, sal_Int32 nP2, sal_Int32 nP3 )
81 OUString aEquation;
82 bool b1Special = ( nFlags & 0x2000 ) != 0;
83 bool b2Special = ( nFlags & 0x4000 ) != 0;
84 bool b3Special = ( nFlags & 0x8000 ) != 0;
85 switch( nFlags & 0xff )
87 case 0 :
88 case 14 :
90 sal_Int32 nOptimize = 0;
91 if ( nP1 )
92 nOptimize |= 1;
93 if ( nP2 )
94 nOptimize |= 2;
95 if ( b1Special )
96 nOptimize |= 4;
97 if ( b2Special )
98 nOptimize |= 8;
99 switch( nOptimize )
101 case 0 :
102 break;
103 case 1 :
104 case 4 :
105 case 5 :
106 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
107 break;
108 case 2 :
109 case 8 :
110 case 10:
111 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
112 break;
113 default :
115 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
116 aEquation += "+";
117 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
119 break;
121 if ( b3Special || nP3 )
123 aEquation += "-";
124 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
127 break;
128 case 1 :
130 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
131 if ( b2Special || ( nP2 != 1 ) )
133 aEquation += "*";
134 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
136 if ( b3Special || ( ( nP3 != 1 ) && ( nP3 != 0 ) ) )
138 aEquation += "/";
139 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
142 break;
143 case 2 :
145 aEquation += "(";
146 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
147 aEquation += "+";
148 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
149 aEquation += ")/2";
151 break;
152 case 3 :
154 aEquation += "abs(";
155 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
156 aEquation += ")";
158 break;
159 case 4 :
161 aEquation += "min(";
162 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
163 aEquation += ",";
164 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
165 aEquation += ")";
167 break;
168 case 5 :
170 aEquation += "max(";
171 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
172 aEquation += ",";
173 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
174 aEquation += ")";
176 break;
177 case 6 :
179 aEquation += "if(";
180 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
181 aEquation += ",";
182 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
183 aEquation += ",";
184 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
185 aEquation += ")";
187 break;
188 case 7 :
190 aEquation += "sqrt(";
191 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
192 aEquation += "*";
193 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
194 aEquation += "+";
195 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
196 aEquation += "*";
197 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
198 aEquation += "+";
199 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
200 aEquation += "*";
201 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
202 aEquation += ")";
204 break;
205 case 8 :
207 aEquation += "atan2(";
208 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
209 aEquation += ",";
210 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
211 aEquation += ")/(pi/180)";
213 break;
214 case 9 :
216 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
217 aEquation += "*sin(";
218 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
219 aEquation += "*(pi/180))";
221 break;
222 case 10 :
224 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
225 aEquation += "*cos(";
226 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
227 aEquation += "*(pi/180))";
229 break;
230 case 11 :
232 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
233 aEquation += "*cos(atan2(";
234 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
235 aEquation += ",";
236 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
237 aEquation += "))";
239 break;
240 case 12 :
242 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
243 aEquation += "*sin(atan2(";
244 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
245 aEquation += ",";
246 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
247 aEquation += "))";
249 break;
250 case 13 :
252 aEquation += "sqrt(";
253 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
254 aEquation += ")";
256 break;
257 case 15 :
259 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
260 aEquation += "*sqrt(1-(";
261 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
262 aEquation += "/";
263 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
264 aEquation += ")";
265 aEquation += "*(";
266 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
267 aEquation += "/";
268 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
269 aEquation += "))";
271 break;
272 case 16 :
274 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
275 aEquation += "*tan(";
276 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
277 aEquation += ")";
279 break;
280 case 0x80 :
282 aEquation += "sqrt(";
283 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
284 aEquation += "*";
285 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
286 aEquation += "-";
287 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
288 aEquation += "*";
289 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
290 aEquation += ")";
292 break;
293 case 0x81 :
295 aEquation += "(cos(";
296 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
297 aEquation += "*(pi/180))*(";
298 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
299 aEquation += "-10800)+sin(";
300 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
301 aEquation += "*(pi/180))*(";
302 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
303 aEquation += "-10800))+10800";
305 break;
306 case 0x82 :
308 aEquation += "-(sin(";
309 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
310 aEquation += "*(pi/180))*(";
311 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
312 aEquation += "-10800)-cos(";
313 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
314 aEquation += "*(pi/180))*(";
315 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
316 aEquation += "-10800))+10800";
318 break;
320 return aEquation;
323 void EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( OUString& rParameter, const sal_Int32 nPara, const bool bIsSpecialValue )
325 if ( bIsSpecialValue )
327 if ( nPara & 0x400 )
329 rParameter += "?";
330 rParameter += OUString::number( ( nPara & 0xff ) );
331 rParameter += " ";
333 else
335 switch( nPara )
337 case DFF_Prop_adjustValue :
338 case DFF_Prop_adjust2Value :
339 case DFF_Prop_adjust3Value :
340 case DFF_Prop_adjust4Value :
341 case DFF_Prop_adjust5Value :
342 case DFF_Prop_adjust6Value :
343 case DFF_Prop_adjust7Value :
344 case DFF_Prop_adjust8Value :
345 case DFF_Prop_adjust9Value :
346 case DFF_Prop_adjust10Value :
348 rParameter += "$";
349 rParameter += OUString::number( ( nPara - DFF_Prop_adjustValue ) );
350 rParameter += " ";
352 break;
353 case DFF_Prop_geoLeft :
355 rParameter += "left";
357 break;
358 case DFF_Prop_geoTop :
360 rParameter += "top";
362 break;
363 case DFF_Prop_geoRight :
365 rParameter += "right";
367 break;
368 case DFF_Prop_geoBottom :
370 rParameter += "bottom";
372 break;
376 else
378 rParameter += OUString::number( ( nPara ) );
382 void EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( EnhancedCustomShapeParameter& rParameter, const sal_Int32 nPara, const bool bIsSpecialValue, bool bHorz )
384 sal_Int32 nValue = 0;
385 if ( bIsSpecialValue )
387 if ( ( nPara >= 0x100 ) && ( nPara <= 0x107 ) )
389 nValue = nPara & 0xff;
390 rParameter.Type = EnhancedCustomShapeParameterType::ADJUSTMENT;
392 else if ( ( nPara >= 3 ) && ( nPara <= 0x82 ) )
394 nValue = nPara - 3;
395 rParameter.Type = EnhancedCustomShapeParameterType::EQUATION;
397 else if ( nPara == 0 )
399 nValue = 0;
400 if ( bHorz )
401 rParameter.Type = EnhancedCustomShapeParameterType::LEFT;
402 else
403 rParameter.Type = EnhancedCustomShapeParameterType::TOP;
405 else if ( nPara == 1 )
407 nValue = 0;
408 if ( bHorz )
409 rParameter.Type = EnhancedCustomShapeParameterType::RIGHT;
410 else
411 rParameter.Type = EnhancedCustomShapeParameterType::BOTTOM;
413 else if ( nPara == 2 ) // means to be centered, but should not be
414 { // used in our implementation
415 nValue = 5600;
416 rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
418 else
420 nValue = nPara;
421 rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
424 else
426 nValue = nPara;
427 rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
429 rParameter.Value <<= nValue;
432 bool EnhancedCustomShape2d::ConvertSequenceToEnhancedCustomShape2dHandle(
433 const css::beans::PropertyValues& rHandleProperties,
434 EnhancedCustomShape2d::Handle& rDestinationHandle )
436 bool bRetValue = false;
437 sal_uInt32 i, nProperties = rHandleProperties.getLength();
438 if ( nProperties )
440 rDestinationHandle.nFlags = HandleFlags::NONE;
441 for ( i = 0; i < nProperties; i++ )
443 const css::beans::PropertyValue& rPropVal = rHandleProperties[ i ];
445 if ( rPropVal.Name == "Position" )
447 if ( rPropVal.Value >>= rDestinationHandle.aPosition )
448 bRetValue = true;
450 else if ( rPropVal.Name == "MirroredX" )
452 bool bMirroredX;
453 if ( rPropVal.Value >>= bMirroredX )
455 if ( bMirroredX )
456 rDestinationHandle.nFlags |= HandleFlags::MIRRORED_X;
459 else if ( rPropVal.Name == "MirroredY" )
461 bool bMirroredY;
462 if ( rPropVal.Value >>= bMirroredY )
464 if ( bMirroredY )
465 rDestinationHandle.nFlags |= HandleFlags::MIRRORED_Y;
468 else if ( rPropVal.Name == "Switched" )
470 bool bSwitched;
471 if ( rPropVal.Value >>= bSwitched )
473 if ( bSwitched )
474 rDestinationHandle.nFlags |= HandleFlags::SWITCHED;
477 else if ( rPropVal.Name == "Polar" )
479 if ( rPropVal.Value >>= rDestinationHandle.aPolar )
480 rDestinationHandle.nFlags |= HandleFlags::POLAR;
482 else if ( rPropVal.Name == "RefX" )
484 if ( rPropVal.Value >>= rDestinationHandle.nRefX )
485 rDestinationHandle.nFlags |= HandleFlags::REFX;
487 else if ( rPropVal.Name == "RefY" )
489 if ( rPropVal.Value >>= rDestinationHandle.nRefY )
490 rDestinationHandle.nFlags |= HandleFlags::REFY;
492 else if ( rPropVal.Name == "RefAngle" )
494 if ( rPropVal.Value >>= rDestinationHandle.nRefAngle )
495 rDestinationHandle.nFlags |= HandleFlags::REFANGLE;
497 else if ( rPropVal.Name == "RefR" )
499 if ( rPropVal.Value >>= rDestinationHandle.nRefR )
500 rDestinationHandle.nFlags |= HandleFlags::REFR;
502 else if ( rPropVal.Name == "RadiusRangeMinimum" )
504 if ( rPropVal.Value >>= rDestinationHandle.aRadiusRangeMinimum )
505 rDestinationHandle.nFlags |= HandleFlags::RADIUS_RANGE_MINIMUM;
507 else if ( rPropVal.Name == "RadiusRangeMaximum" )
509 if ( rPropVal.Value >>= rDestinationHandle.aRadiusRangeMaximum )
510 rDestinationHandle.nFlags |= HandleFlags::RADIUS_RANGE_MAXIMUM;
512 else if ( rPropVal.Name == "RangeXMinimum" )
514 if ( rPropVal.Value >>= rDestinationHandle.aXRangeMinimum )
515 rDestinationHandle.nFlags |= HandleFlags::RANGE_X_MINIMUM;
517 else if ( rPropVal.Name == "RangeXMaximum" )
519 if ( rPropVal.Value >>= rDestinationHandle.aXRangeMaximum )
520 rDestinationHandle.nFlags |= HandleFlags::RANGE_X_MAXIMUM;
522 else if ( rPropVal.Name == "RangeYMinimum" )
524 if ( rPropVal.Value >>= rDestinationHandle.aYRangeMinimum )
525 rDestinationHandle.nFlags |= HandleFlags::RANGE_Y_MINIMUM;
527 else if ( rPropVal.Name == "RangeYMaximum" )
529 if ( rPropVal.Value >>= rDestinationHandle.aYRangeMaximum )
530 rDestinationHandle.nFlags |= HandleFlags::RANGE_Y_MAXIMUM;
534 return bRetValue;
537 void EnhancedCustomShape2d::ApplyShapeAttributes( const SdrCustomShapeGeometryItem& rGeometryItem )
539 // AdjustmentValues
540 const Any* pAny = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( "AdjustmentValues" );
541 if ( pAny )
542 *pAny >>= seqAdjustmentValues;
545 // Coordsize
546 const Any* pViewBox = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( "ViewBox" );
547 css::awt::Rectangle aViewBox;
548 if ( pViewBox && (*pViewBox >>= aViewBox ) )
550 nCoordLeft = aViewBox.X;
551 nCoordTop = aViewBox.Y;
552 nCoordWidthG = labs( aViewBox.Width );
553 nCoordHeightG = labs( aViewBox.Height);
555 const OUString sPath( "Path" );
558 // Path/Coordinates
559 pAny = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( sPath, "Coordinates" );
560 if ( pAny )
561 *pAny >>= seqCoordinates;
564 // Path/GluePoints
565 pAny = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( sPath, "GluePoints" );
566 if ( pAny )
567 *pAny >>= seqGluePoints;
570 // Path/Segments
571 pAny = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( sPath, "Segments" );
572 if ( pAny )
573 *pAny >>= seqSegments;
576 // Path/SubViewSize
577 pAny = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( sPath, "SubViewSize" );
578 if ( pAny )
579 *pAny >>= seqSubViewSize;
582 // Path/StretchX
583 pAny = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( sPath, "StretchX" );
584 if ( pAny )
586 sal_Int32 nStretchX = 0;
587 if ( *pAny >>= nStretchX )
588 nXRef = nStretchX;
592 // Path/StretchY
593 pAny = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( sPath, "StretchY" );
594 if ( pAny )
596 sal_Int32 nStretchY = 0;
597 if ( *pAny >>= nStretchY )
598 nYRef = nStretchY;
602 // Path/TextFrames
603 pAny = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( sPath, "TextFrames" );
604 if ( pAny )
605 *pAny >>= seqTextFrames;
608 // Equations
609 pAny = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( "Equations" );
610 if ( pAny )
611 *pAny >>= seqEquations;
614 // Handles
615 pAny = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( "Handles" );
616 if ( pAny )
617 *pAny >>= seqHandles;
620 EnhancedCustomShape2d::~EnhancedCustomShape2d()
624 void EnhancedCustomShape2d::SetPathSize( sal_Int32 nIndex )
626 sal_Int32 nWidth = 0;
627 sal_Int32 nHeight = 0;
629 if ( seqSubViewSize.getLength() && nIndex < seqSubViewSize.getLength() ) {
630 nWidth = seqSubViewSize[ nIndex ].Width;
631 nHeight = seqSubViewSize[ nIndex ].Height;
632 SAL_INFO(
633 "svx",
634 "set subpath " << nIndex << " size: " << nWidth << " x "
635 << nHeight);
638 if ( nWidth && nHeight ) {
639 nCoordWidth = nWidth;
640 nCoordHeight = nHeight;
641 } else {
642 nCoordWidth = nCoordWidthG;
643 nCoordHeight = nCoordHeightG;
646 fXScale = nCoordWidth == 0 ? 0.0 : (double)aLogicRect.GetWidth() / (double)nCoordWidth;
647 fYScale = nCoordHeight == 0 ? 0.0 : (double)aLogicRect.GetHeight() / (double)nCoordHeight;
648 if ( bOOXMLShape )
650 SAL_INFO(
651 "svx",
652 "ooxml shape, path width: " << nCoordWidth << " height: "
653 << nCoordHeight);
655 // Try to set up scale separately, if given only width or height
656 // This is possible case in OOXML when only width or height is non-zero
657 if ( nCoordWidth == 0 )
659 if ( nWidth )
660 fXScale = (double)aLogicRect.GetWidth() / (double)nWidth;
661 else
662 fXScale = 1.0;
664 if ( nCoordHeight == 0 )
666 if ( nHeight )
667 fYScale = (double)aLogicRect.GetHeight() / (double)nHeight;
668 else
669 fYScale = 1.0;
672 if ( (sal_uInt32)nXRef != 0x80000000 && aLogicRect.GetHeight() )
674 fXRatio = (double)aLogicRect.GetWidth() / (double)aLogicRect.GetHeight();
675 if ( fXRatio > 1 )
676 fXScale /= fXRatio;
677 else
678 fXRatio = 1.0;
680 else
681 fXRatio = 1.0;
682 if ( (sal_uInt32)nYRef != 0x80000000 && aLogicRect.GetWidth() )
684 fYRatio = (double)aLogicRect.GetHeight() / (double)aLogicRect.GetWidth();
685 if ( fYRatio > 1 )
686 fYScale /= fYRatio;
687 else
688 fYRatio = 1.0;
690 else
691 fYRatio = 1.0;
694 EnhancedCustomShape2d::EnhancedCustomShape2d( SdrObject* pAObj ) :
695 SfxItemSet ( pAObj->GetMergedItemSet() ),
696 pCustomShapeObj ( pAObj ),
697 eSpType ( mso_sptNil ),
698 nCoordLeft ( 0 ),
699 nCoordTop ( 0 ),
700 nCoordWidthG ( 21600 ),
701 nCoordHeightG ( 21600 ),
702 bOOXMLShape ( false ),
703 nXRef ( 0x80000000 ),
704 nYRef ( 0x80000000 ),
705 nColorData ( 0 ),
706 bTextFlow ( false ),
707 bFilled ( static_cast<const XFillStyleItem&>(pAObj->GetMergedItem( XATTR_FILLSTYLE )).GetValue() != drawing::FillStyle_NONE ),
708 bStroked ( static_cast<const XLineStyleItem&>(pAObj->GetMergedItem( XATTR_LINESTYLE )).GetValue() != drawing::LineStyle_NONE ),
709 bFlipH ( false ),
710 bFlipV ( false )
712 // bTextFlow needs to be set before clearing the TextDirection Item
714 ClearItem( SDRATTR_TEXTDIRECTION ); //SJ: vertical writing is not required, by removing this item no outliner is created
716 // #i105323# For 2D AutoShapes, the shadow attribute does not need to be applied to any
717 // of the constructed helper SdrObjects. This would lead to problems since the shadow
718 // of one helper object would fall on one helper object behind it (e.g. with the
719 // eyes of the smiley shape). This is not wanted; instead a single shadow 'behind'
720 // the AutoShape visualisation is wanted. This is done with primitive functionality
721 // now in SdrCustomShapePrimitive2D::create2DDecomposition, but only for 2D objects
722 // (see there and in EnhancedCustomShape3d::Create3DObject to read more).
723 // This exception may be removed later when AutoShapes will create primitives directly.
724 // So, currently remove the ShadowAttribute from the ItemSet to not apply it to any
725 // 2D helper shape.
726 ClearItem(SDRATTR_SHADOW);
728 Point aP( pCustomShapeObj->GetSnapRect().Center() );
729 Size aS( pCustomShapeObj->GetLogicRect().GetSize() );
730 aP.X() -= aS.Width() / 2;
731 aP.Y() -= aS.Height() / 2;
732 aLogicRect = tools::Rectangle( aP, aS );
734 OUString sShapeType;
735 const SdrCustomShapeGeometryItem& rGeometryItem = static_cast<const SdrCustomShapeGeometryItem&>(pCustomShapeObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
736 const Any* pAny = rGeometryItem.GetPropertyValueByName( "Type" );
737 if ( pAny ) {
738 *pAny >>= sShapeType;
739 bOOXMLShape = sShapeType.startsWith("ooxml-");
740 SAL_INFO("svx", "shape type: " << sShapeType << " " << bOOXMLShape);
742 eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
744 pAny = rGeometryItem.GetPropertyValueByName( "MirroredX" );
745 if ( pAny )
746 *pAny >>= bFlipH;
747 pAny = rGeometryItem.GetPropertyValueByName( "MirroredY" );
748 if ( pAny )
749 *pAny >>= bFlipV;
751 if ( dynamic_cast<const SdrObjCustomShape*>( pCustomShapeObj) != nullptr ) // should always be a SdrObjCustomShape, but you don't know
752 nRotateAngle = (sal_Int32)(static_cast<SdrObjCustomShape*>(pCustomShapeObj)->GetObjectRotation() * 100.0);
753 else
754 nRotateAngle = pCustomShapeObj->GetRotateAngle();
756 /*const sal_Int32* pDefData =*/ ApplyShapeAttributes( rGeometryItem );
757 SetPathSize();
759 switch( eSpType )
761 case mso_sptCan : nColorData = 0x20400000; break;
762 case mso_sptCube : nColorData = 0x302e0000; break;
763 case mso_sptActionButtonBlank : nColorData = 0x502ce400; break;
764 case mso_sptActionButtonHome : nColorData = 0x702ce4ce; break;
765 case mso_sptActionButtonHelp : nColorData = 0x602ce4c0; break;
766 case mso_sptActionButtonInformation : nColorData = 0x702ce4c5; break;
767 case mso_sptActionButtonBackPrevious : nColorData = 0x602ce4c0; break;
768 case mso_sptActionButtonForwardNext : nColorData = 0x602ce4c0; break;
769 case mso_sptActionButtonBeginning : nColorData = 0x602ce4c0; break;
770 case mso_sptActionButtonEnd : nColorData = 0x602ce4c0; break;
771 case mso_sptActionButtonReturn : nColorData = 0x602ce4c0; break;
772 case mso_sptActionButtonDocument : nColorData = 0x702ce4ec; break;
773 case mso_sptActionButtonSound : nColorData = 0x602ce4c0; break;
774 case mso_sptActionButtonMovie : nColorData = 0x602ce4c0; break;
775 case mso_sptBevel : nColorData = 0x502ce400; break;
776 case mso_sptFoldedCorner : nColorData = 0x20e00000; break;
777 case mso_sptSmileyFace : nColorData = 0x20e00000; break;
778 case mso_sptNil :
780 if( sShapeType.getLength() > 4 &&
781 sShapeType.match( "col-" ))
783 nColorData = sShapeType.copy( 4 ).toUInt32( 16 );
786 break;
787 case mso_sptCurvedLeftArrow :
788 case mso_sptCurvedRightArrow :
789 case mso_sptCurvedUpArrow :
790 case mso_sptCurvedDownArrow : nColorData = 0x20d00000; break;
791 case mso_sptRibbon2 : nColorData = 0x30ee0000; break;
792 case mso_sptRibbon : nColorData = 0x30ee0000; break;
794 case mso_sptEllipseRibbon2 : nColorData = 0x30ee0000; break;
795 case mso_sptEllipseRibbon : nColorData = 0x30ee0000; break;
797 case mso_sptVerticalScroll : nColorData = 0x30ee0000; break;
798 case mso_sptHorizontalScroll : nColorData = 0x30ee0000; break;
799 default:
800 break;
803 sal_Int32 i, nLength = seqEquations.getLength();
805 if ( nLength )
807 vNodesSharedPtr.resize( nLength );
808 vEquationResults.resize( nLength );
809 for ( i = 0; i < seqEquations.getLength(); i++ )
811 vEquationResults[ i ].bReady = false;
814 vNodesSharedPtr[ i ] = EnhancedCustomShape::FunctionParser::parseFunction( seqEquations[ i ], *this );
816 catch ( EnhancedCustomShape::ParseError& )
818 SAL_INFO(
819 "svx",
820 "error: equation number: " << i << ", parser failed ("
821 << seqEquations[i] << ")");
827 using EnhancedCustomShape::ExpressionFunct;
829 double EnhancedCustomShape2d::GetEnumFunc( const ExpressionFunct eFunc ) const
831 double fRet = 0.0;
832 switch( eFunc )
834 case ExpressionFunct::EnumPi : fRet = F_PI; break;
835 case ExpressionFunct::EnumLeft : fRet = 0.0; break;
836 case ExpressionFunct::EnumTop : fRet = 0.0; break;
837 case ExpressionFunct::EnumRight : fRet = (double)nCoordWidth * fXRatio; break;
838 case ExpressionFunct::EnumBottom : fRet = (double)nCoordHeight * fYRatio; break;
839 case ExpressionFunct::EnumXStretch : fRet = nXRef; break;
840 case ExpressionFunct::EnumYStretch : fRet = nYRef; break;
841 case ExpressionFunct::EnumHasStroke : fRet = bStroked ? 1.0 : 0.0; break;
842 case ExpressionFunct::EnumHasFill : fRet = bFilled ? 1.0 : 0.0; break;
843 case ExpressionFunct::EnumWidth : fRet = nCoordWidth; break;
844 case ExpressionFunct::EnumHeight : fRet = nCoordHeight; break;
845 case ExpressionFunct::EnumLogWidth : fRet = aLogicRect.GetWidth(); break;
846 case ExpressionFunct::EnumLogHeight : fRet = aLogicRect.GetHeight(); break;
847 default: break;
849 return fRet;
851 double EnhancedCustomShape2d::GetAdjustValueAsDouble( const sal_Int32 nIndex ) const
853 double fNumber = 0.0;
854 if ( nIndex < seqAdjustmentValues.getLength() )
856 if ( seqAdjustmentValues[ nIndex ].Value.getValueTypeClass() == TypeClass_DOUBLE )
857 seqAdjustmentValues[ nIndex ].Value >>= fNumber;
858 else
860 sal_Int32 nNumber = 0;
861 seqAdjustmentValues[ nIndex ].Value >>= nNumber;
862 fNumber = (double)nNumber;
865 return fNumber;
867 double EnhancedCustomShape2d::GetEquationValueAsDouble( const sal_Int32 nIndex ) const
869 double fNumber = 0.0;
870 static sal_uInt32 nLevel = 0;
871 if ( nIndex < (sal_Int32)vNodesSharedPtr.size() )
873 if ( vNodesSharedPtr[ nIndex ].get() ) {
874 nLevel ++;
877 if ( vEquationResults[ nIndex ].bReady )
878 fNumber = vEquationResults[ nIndex ].fValue;
879 else {
880 // cast to non const, so that we can optimize by caching
881 // equation results, without changing all the const in the stack
882 struct EquationResult &aResult = const_cast<EnhancedCustomShape2d*>(this)->vEquationResults[ nIndex ];
884 fNumber = aResult.fValue = (*vNodesSharedPtr[ nIndex ])();
885 aResult.bReady = true;
887 if ( !rtl::math::isFinite( fNumber ) )
888 fNumber = 0.0;
889 SAL_INFO("svx", "equation " << nLevel << " (level: " << seqEquations[nIndex] << "): "
890 << fNumber << " --> " << 180.0*fNumber/10800000.0);
893 catch ( ... )
895 SAL_WARN("svx", "EnhancedCustomShape2d::GetEquationValueAsDouble failed");
897 nLevel --;
899 SAL_INFO(
900 "svx",
901 "?" << nIndex << " --> " << fNumber << " (angle: "
902 << 180.0*fNumber/10800000.0 << ")");
905 return fNumber;
908 bool EnhancedCustomShape2d::SetAdjustValueAsDouble( const double& rValue, const sal_Int32 nIndex )
910 bool bRetValue = false;
911 if ( nIndex < seqAdjustmentValues.getLength() )
913 // updating our local adjustment sequence
914 seqAdjustmentValues[ nIndex ].Value <<= rValue;
915 seqAdjustmentValues[ nIndex ].State = css::beans::PropertyState_DIRECT_VALUE;
916 bRetValue = true;
918 return bRetValue;
921 Point EnhancedCustomShape2d::GetPoint( const css::drawing::EnhancedCustomShapeParameterPair& rPair,
922 const bool bScale, const bool bReplaceGeoSize ) const
924 Point aRetValue;
925 sal_uInt32 nPass = 0;
928 sal_uInt32 nIndex = nPass;
930 double fVal;
931 const EnhancedCustomShapeParameter& rParameter = nIndex ? rPair.Second : rPair.First;
932 if ( nPass ) // height
934 GetParameter( fVal, rParameter, false, bReplaceGeoSize );
935 fVal -= nCoordTop;
936 if ( bScale )
938 fVal *= fYScale;
940 aRetValue.Y() = (sal_Int32)fVal;
942 else // width
944 GetParameter( fVal, rParameter, bReplaceGeoSize, false );
945 fVal -= nCoordLeft;
946 if ( bScale )
948 fVal *= fXScale;
950 aRetValue.X() = static_cast<long>(fVal);
953 while ( ++nPass < 2 );
954 return aRetValue;
957 void EnhancedCustomShape2d::GetParameter( double& rRetValue, const EnhancedCustomShapeParameter& rParameter,
958 const bool bReplaceGeoWidth, const bool bReplaceGeoHeight ) const
960 rRetValue = 0.0;
961 switch ( rParameter.Type )
963 case EnhancedCustomShapeParameterType::ADJUSTMENT :
965 sal_Int32 nAdjustmentIndex = 0;
966 if ( rParameter.Value >>= nAdjustmentIndex )
968 rRetValue = GetAdjustValueAsDouble( nAdjustmentIndex );
971 break;
972 case EnhancedCustomShapeParameterType::EQUATION :
974 sal_Int32 nEquationIndex = 0;
975 if ( rParameter.Value >>= nEquationIndex )
977 rRetValue = GetEquationValueAsDouble( nEquationIndex );
980 break;
981 case EnhancedCustomShapeParameterType::NORMAL :
983 if ( rParameter.Value.getValueTypeClass() == TypeClass_DOUBLE )
985 double fValue(0.0);
986 if ( rParameter.Value >>= fValue )
988 rRetValue = fValue;
991 else
993 sal_Int32 nValue = 0;
994 if ( rParameter.Value >>= nValue )
996 rRetValue = nValue;
997 if ( bReplaceGeoWidth && ( nValue == nCoordWidth ) )
998 rRetValue *= fXRatio;
999 else if ( bReplaceGeoHeight && ( nValue == nCoordHeight ) )
1000 rRetValue *= fYRatio;
1004 break;
1005 case EnhancedCustomShapeParameterType::LEFT :
1007 rRetValue = 0.0;
1009 break;
1010 case EnhancedCustomShapeParameterType::TOP :
1012 rRetValue = 0.0;
1014 break;
1015 case EnhancedCustomShapeParameterType::RIGHT :
1017 rRetValue = nCoordWidth;
1019 break;
1020 case EnhancedCustomShapeParameterType::BOTTOM :
1022 rRetValue = nCoordHeight;
1024 break;
1028 // nLumDat 28-31 = number of luminance entries in nLumDat
1029 // nLumDat 27-24 = nLumDatEntry 0
1030 // nLumDat 23-20 = nLumDatEntry 1 ...
1031 // each 4bit entry is to be interpreted as a 10 percent signed luminance changing
1032 sal_Int32 EnhancedCustomShape2d::GetLuminanceChange( sal_uInt32 nIndex ) const
1034 const sal_uInt32 nCount = nColorData >> 28;
1035 if ( !nCount )
1036 return 0;
1038 if ( nIndex >= nCount )
1039 nIndex = nCount - 1;
1041 const sal_Int32 nLumDat = nColorData << ( ( 1 + nIndex ) << 2 );
1042 return ( nLumDat >> 28 ) * 10;
1045 Color EnhancedCustomShape2d::GetColorData( const Color& rFillColor, sal_uInt32 nIndex, double dBrightness ) const
1047 if ( bOOXMLShape || ( mso_sptMin == eSpType /* ODF "non-primitive" */ ) )
1048 { //do LibreOffice way, using dBrightness
1049 if ( dBrightness == 0.0)
1051 return rFillColor;
1053 else
1055 if (dBrightness >=0.0)
1056 { //lighten, blending with white
1057 return Color( (sal_uInt8)static_cast< sal_Int32 >( basegfx::clamp(rFillColor.GetRed() * (1.0-dBrightness) + dBrightness * 255.0, 0.0, 255.0) ),
1058 (sal_uInt8)static_cast< sal_Int32 >( basegfx::clamp(rFillColor.GetGreen() * (1.0-dBrightness) + dBrightness * 255.0, 0.0, 255.0) ),
1059 (sal_uInt8)static_cast< sal_Int32 >( basegfx::clamp(rFillColor.GetBlue() * (1.0-dBrightness) + dBrightness * 255.0, 0.0, 255.0) ) );
1061 else
1062 { //darken (indicated by negative sign), blending with black
1063 return Color( (sal_uInt8)static_cast< sal_Int32 >( basegfx::clamp(rFillColor.GetRed() * (1.0+dBrightness), 0.0, 255.0) ),
1064 (sal_uInt8)static_cast< sal_Int32 >( basegfx::clamp(rFillColor.GetGreen() * (1.0+dBrightness), 0.0, 255.0) ),
1065 (sal_uInt8)static_cast< sal_Int32 >( basegfx::clamp(rFillColor.GetBlue() * (1.0+dBrightness), 0.0, 255.0) ) );
1069 else
1070 { //do OpenOffice way, using nColorData
1071 const sal_Int32 nLuminance = GetLuminanceChange(nIndex);
1072 if( !nLuminance )
1073 return rFillColor;
1075 basegfx::BColor aHSVColor=
1076 basegfx::tools::rgb2hsv(
1077 basegfx::BColor(rFillColor.GetRed()/255.0,
1078 rFillColor.GetGreen()/255.0,
1079 rFillColor.GetBlue()/255.0));
1081 if( nLuminance > 0 )
1083 aHSVColor.setGreen(
1084 aHSVColor.getGreen() * (1.0-nLuminance/100.0));
1085 aHSVColor.setBlue(
1086 nLuminance/100.0 +
1087 (1.0-nLuminance/100.0)*aHSVColor.getBlue());
1089 else if( nLuminance < 0 )
1091 aHSVColor.setBlue(
1092 (1.0+nLuminance/100.0)*aHSVColor.getBlue());
1095 aHSVColor = basegfx::tools::hsv2rgb(aHSVColor);
1096 return Color( (sal_uInt8)static_cast< sal_Int32 >( basegfx::clamp(aHSVColor.getRed(),0.0,1.0) * 255.0 + 0.5 ),
1097 (sal_uInt8)static_cast< sal_Int32 >( basegfx::clamp(aHSVColor.getGreen(),0.0,1.0) * 255.0 + 0.5 ),
1098 (sal_uInt8)static_cast< sal_Int32 >( basegfx::clamp(aHSVColor.getBlue(),0.0,1.0) * 255.0 + 0.5 ) );
1102 tools::Rectangle EnhancedCustomShape2d::GetTextRect() const
1104 sal_Int32 nIndex, nSize = seqTextFrames.getLength();
1105 if ( !nSize )
1106 return aLogicRect;
1107 nIndex = 0;
1108 if ( bTextFlow && ( nSize > 1 ) )
1109 nIndex++;
1110 Point aTopLeft( GetPoint( seqTextFrames[ nIndex ].TopLeft, !bOOXMLShape, true ) );
1111 Point aBottomRight( GetPoint( seqTextFrames[ nIndex ].BottomRight, !bOOXMLShape, true ) );
1112 if ( bFlipH )
1114 aTopLeft.X() = aLogicRect.GetWidth() - aTopLeft.X();
1115 aBottomRight.X() = aLogicRect.GetWidth() - aBottomRight.X();
1117 if ( bFlipV )
1119 aTopLeft.Y() = aLogicRect.GetHeight() - aTopLeft.Y();
1120 aBottomRight.Y() = aLogicRect.GetHeight() - aBottomRight.Y();
1122 tools::Rectangle aRect( aTopLeft, aBottomRight );
1123 SAL_INFO("svx", aRect.GetWidth() << " x " << aRect.GetHeight());
1124 if( aRect.GetWidth() <= 1 || aRect.GetHeight() <= 1 )
1125 return aLogicRect;
1126 aRect.Move( aLogicRect.Left(), aLogicRect.Top() );
1127 aRect.Justify();
1128 return aRect;
1131 sal_uInt32 EnhancedCustomShape2d::GetHdlCount() const
1133 return seqHandles.getLength();
1136 bool EnhancedCustomShape2d::GetHandlePosition( const sal_uInt32 nIndex, Point& rReturnPosition ) const
1138 bool bRetValue = false;
1139 if ( nIndex < GetHdlCount() )
1141 Handle aHandle;
1142 if ( ConvertSequenceToEnhancedCustomShape2dHandle( seqHandles[ nIndex ], aHandle ) )
1144 if ( aHandle.nFlags & HandleFlags::POLAR )
1146 Point aReferencePoint( GetPoint( aHandle.aPolar ) );
1148 double fAngle;
1149 double fRadius;
1150 GetParameter( fRadius, aHandle.aPosition.First, false, false );
1151 GetParameter( fAngle, aHandle.aPosition.Second, false, false );
1153 double a = ( 360.0 - fAngle ) * F_PI180;
1154 double dx = fRadius * fXScale;
1155 double fX = dx * cos( a );
1156 double fY =-dx * sin( a );
1157 rReturnPosition =
1158 Point(
1159 svx::Round( fX + aReferencePoint.X() ),
1160 basegfx::fTools::equalZero(fXScale) ? aReferencePoint.Y() :
1161 svx::Round( ( fY * fYScale ) / fXScale + aReferencePoint.Y() ) );
1163 else
1165 if ( aHandle.nFlags & HandleFlags::SWITCHED )
1167 if ( aLogicRect.GetHeight() > aLogicRect.GetWidth() )
1169 css::drawing::EnhancedCustomShapeParameter aFirst = aHandle.aPosition.First;
1170 css::drawing::EnhancedCustomShapeParameter aSecond = aHandle.aPosition.Second;
1171 aHandle.aPosition.First = aSecond;
1172 aHandle.aPosition.Second = aFirst;
1175 rReturnPosition = GetPoint( aHandle.aPosition );
1177 const GeoStat aGeoStat( static_cast<SdrObjCustomShape*>(pCustomShapeObj)->GetGeoStat() );
1178 if ( aGeoStat.nShearAngle )
1180 double nTan = aGeoStat.nTan;
1181 if ((bFlipV&&!bFlipH )||(bFlipH&&!bFlipV))
1182 nTan = -nTan;
1183 ShearPoint( rReturnPosition, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), nTan );
1185 if ( nRotateAngle )
1187 double a = nRotateAngle * F_PI18000;
1188 RotatePoint( rReturnPosition, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), sin( a ), cos( a ) );
1190 if ( bFlipH )
1191 rReturnPosition.X() = aLogicRect.GetWidth() - rReturnPosition.X();
1192 if ( bFlipV )
1193 rReturnPosition.Y() = aLogicRect.GetHeight() - rReturnPosition.Y();
1194 rReturnPosition.Move( aLogicRect.Left(), aLogicRect.Top() );
1195 bRetValue = true;
1198 return bRetValue;
1201 bool EnhancedCustomShape2d::SetHandleControllerPosition( const sal_uInt32 nIndex, const css::awt::Point& rPosition )
1203 bool bRetValue = false;
1204 if ( nIndex < GetHdlCount() )
1206 Handle aHandle;
1207 if ( ConvertSequenceToEnhancedCustomShape2dHandle( seqHandles[ nIndex ], aHandle ) )
1209 Point aP( rPosition.X, rPosition.Y );
1210 // apply the negative object rotation to the controller position
1212 aP.Move( -aLogicRect.Left(), -aLogicRect.Top() );
1213 if ( bFlipH )
1214 aP.X() = aLogicRect.GetWidth() - aP.X();
1215 if ( bFlipV )
1216 aP.Y() = aLogicRect.GetHeight() - aP.Y();
1217 if ( nRotateAngle )
1219 double a = -nRotateAngle * F_PI18000;
1220 RotatePoint( aP, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), sin( a ), cos( a ) );
1222 const GeoStat aGeoStat( static_cast<SdrObjCustomShape*>(pCustomShapeObj)->GetGeoStat() );
1223 if ( aGeoStat.nShearAngle )
1225 double nTan = -aGeoStat.nTan;
1226 if ((bFlipV&&!bFlipH )||(bFlipH&&!bFlipV))
1227 nTan = -nTan;
1228 ShearPoint( aP, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), nTan );
1231 double fPos1 = aP.X(); //( bFlipH ) ? aLogicRect.GetWidth() - aP.X() : aP.X();
1232 double fPos2 = aP.Y(); //( bFlipV ) ? aLogicRect.GetHeight() -aP.Y() : aP.Y();
1233 fPos1 /= fXScale;
1234 fPos2 /= fYScale;
1236 // Used for scaling the adjustment values based on handle positions
1237 double fWidth;
1238 double fHeight;
1240 if ( nCoordWidth || nCoordHeight )
1242 fWidth = nCoordWidth;
1243 fHeight = nCoordHeight;
1245 else
1247 fWidth = aLogicRect.GetWidth();
1248 fHeight = aLogicRect.GetHeight();
1251 if ( aHandle.nFlags & HandleFlags::SWITCHED )
1253 if ( aLogicRect.GetHeight() > aLogicRect.GetWidth() )
1255 double fX = fPos1;
1256 double fY = fPos2;
1257 double fTmp = fWidth;
1258 fPos1 = fY;
1259 fPos2 = fX;
1260 fHeight = fWidth;
1261 fWidth = fTmp;
1265 sal_Int32 nFirstAdjustmentValue = -1, nSecondAdjustmentValue = -1;
1267 if ( aHandle.aPosition.First.Type == EnhancedCustomShapeParameterType::ADJUSTMENT )
1268 aHandle.aPosition.First.Value >>= nFirstAdjustmentValue;
1269 if ( aHandle.aPosition.Second.Type == EnhancedCustomShapeParameterType::ADJUSTMENT )
1270 aHandle.aPosition.Second.Value>>= nSecondAdjustmentValue;
1273 // DrawingML polar handles set REFR or REFANGLE instead of POLAR
1274 if ( aHandle.nFlags & ( HandleFlags::POLAR | HandleFlags::REFR | HandleFlags::REFANGLE ) )
1276 double fXRef, fYRef, fAngle;
1277 if ( aHandle.nFlags & HandleFlags::POLAR )
1279 GetParameter( fXRef, aHandle.aPolar.First, false, false );
1280 GetParameter( fYRef, aHandle.aPolar.Second, false, false );
1282 else
1284 // DrawingML polar handles don't have reference center.
1285 fXRef = fWidth / 2;
1286 fYRef = fHeight / 2;
1288 const double fDX = fPos1 - fXRef;
1289 fAngle = -( atan2( -fPos2 + fYRef, ( ( fDX == 0.0L ) ? 0.000000001 : fDX ) ) / F_PI180 );
1290 double fX = ( fPos1 - fXRef );
1291 double fY = ( fPos2 - fYRef );
1292 double fRadius = sqrt( fX * fX + fY * fY );
1293 if ( aHandle.nFlags & HandleFlags::RADIUS_RANGE_MINIMUM )
1295 double fMin;
1296 GetParameter( fMin, aHandle.aRadiusRangeMinimum, false, false );
1297 if ( fRadius < fMin )
1298 fRadius = fMin;
1300 if ( aHandle.nFlags & HandleFlags::RADIUS_RANGE_MAXIMUM )
1302 double fMax;
1303 GetParameter( fMax, aHandle.aRadiusRangeMaximum, false, false );
1304 if ( fRadius > fMax )
1305 fRadius = fMax;
1307 if (aHandle.nFlags & HandleFlags::REFR)
1309 fRadius *= 100000.0;
1310 fRadius /= sqrt( fWidth * fWidth + fHeight * fHeight );
1311 nFirstAdjustmentValue = aHandle.nRefR;
1313 if (aHandle.nFlags & HandleFlags::REFANGLE)
1315 if ( fAngle < 0 )
1316 fAngle += 360.0;
1317 // Adjustment value referred by nRefAngle needs to be in 60000th a degree
1318 // from 0 to 21600000.
1319 fAngle *= 60000.0;
1320 nSecondAdjustmentValue = aHandle.nRefAngle;
1322 if ( nFirstAdjustmentValue >= 0 )
1323 SetAdjustValueAsDouble( fRadius, nFirstAdjustmentValue );
1324 if ( nSecondAdjustmentValue >= 0 )
1325 SetAdjustValueAsDouble( fAngle, nSecondAdjustmentValue );
1327 else
1329 if ( aHandle.nFlags & HandleFlags::REFX )
1331 nFirstAdjustmentValue = aHandle.nRefX;
1332 fPos1 *= 100000.0;
1333 fPos1 /= fWidth;
1335 if ( aHandle.nFlags & HandleFlags::REFY )
1337 nSecondAdjustmentValue = aHandle.nRefY;
1338 fPos2 *= 100000.0;
1339 fPos2 /= fHeight;
1341 if ( nFirstAdjustmentValue >= 0 )
1343 if ( aHandle.nFlags & HandleFlags::RANGE_X_MINIMUM ) // check if horizontal handle needs to be within a range
1345 double fXMin;
1346 GetParameter( fXMin, aHandle.aXRangeMinimum, false, false );
1347 if ( fPos1 < fXMin )
1348 fPos1 = fXMin;
1350 if ( aHandle.nFlags & HandleFlags::RANGE_X_MAXIMUM ) // check if horizontal handle needs to be within a range
1352 double fXMax;
1353 GetParameter( fXMax, aHandle.aXRangeMaximum, false, false );
1354 if ( fPos1 > fXMax )
1355 fPos1 = fXMax;
1357 SetAdjustValueAsDouble( fPos1, nFirstAdjustmentValue );
1359 if ( nSecondAdjustmentValue >= 0 )
1361 if ( aHandle.nFlags & HandleFlags::RANGE_Y_MINIMUM ) // check if vertical handle needs to be within a range
1363 double fYMin;
1364 GetParameter( fYMin, aHandle.aYRangeMinimum, false, false );
1365 if ( fPos2 < fYMin )
1366 fPos2 = fYMin;
1368 if ( aHandle.nFlags & HandleFlags::RANGE_Y_MAXIMUM ) // check if vertical handle needs to be within a range
1370 double fYMax;
1371 GetParameter( fYMax, aHandle.aYRangeMaximum, false, false );
1372 if ( fPos2 > fYMax )
1373 fPos2 = fYMax;
1375 SetAdjustValueAsDouble( fPos2, nSecondAdjustmentValue );
1378 // and writing them back into the GeometryItem
1379 SdrCustomShapeGeometryItem aGeometryItem(
1380 static_cast<const SdrCustomShapeGeometryItem&>(pCustomShapeObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY )));
1381 css::beans::PropertyValue aPropVal;
1382 aPropVal.Name = "AdjustmentValues";
1383 aPropVal.Value <<= seqAdjustmentValues;
1384 aGeometryItem.SetPropertyValue( aPropVal );
1385 pCustomShapeObj->SetMergedItem( aGeometryItem );
1386 bRetValue = true;
1389 return bRetValue;
1392 void EnhancedCustomShape2d::SwapStartAndEndArrow( SdrObject* pObj ) //#108274
1394 XLineStartItem aLineStart;
1395 aLineStart.SetLineStartValue(static_cast<const XLineStartItem&>(pObj->GetMergedItem( XATTR_LINEEND )).GetLineStartValue());
1396 XLineStartWidthItem aLineStartWidth(static_cast<const XLineStartWidthItem&>(pObj->GetMergedItem( XATTR_LINEENDWIDTH )).GetValue());
1397 XLineStartCenterItem aLineStartCenter(static_cast<const XLineStartCenterItem&>(pObj->GetMergedItem( XATTR_LINEENDCENTER )).GetValue());
1399 XLineEndItem aLineEnd;
1400 aLineEnd.SetLineEndValue(static_cast<const XLineEndItem&>(pObj->GetMergedItem( XATTR_LINESTART )).GetLineEndValue());
1401 XLineEndWidthItem aLineEndWidth(static_cast<const XLineEndWidthItem&>(pObj->GetMergedItem( XATTR_LINESTARTWIDTH )).GetValue());
1402 XLineEndCenterItem aLineEndCenter(static_cast<const XLineEndCenterItem&>(pObj->GetMergedItem( XATTR_LINESTARTCENTER )).GetValue());
1404 pObj->SetMergedItem( aLineStart );
1405 pObj->SetMergedItem( aLineStartWidth );
1406 pObj->SetMergedItem( aLineStartCenter );
1407 pObj->SetMergedItem( aLineEnd );
1408 pObj->SetMergedItem( aLineEndWidth );
1409 pObj->SetMergedItem( aLineEndCenter );
1412 static basegfx::B2DPolygon CreateArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd, const bool bClockwise, bool bFullCircle = false )
1414 tools::Rectangle aRect( rRect );
1415 Point aStart( rStart );
1416 Point aEnd( rEnd );
1418 sal_Int32 bSwapStartEndAngle = 0;
1420 if ( aRect.Left() > aRect.Right() )
1421 bSwapStartEndAngle ^= 0x01;
1422 if ( aRect.Top() > aRect.Bottom() )
1423 bSwapStartEndAngle ^= 0x11;
1424 if ( bSwapStartEndAngle )
1426 aRect.Justify();
1427 if ( bSwapStartEndAngle & 1 )
1429 Point aTmp( aStart );
1430 aStart = aEnd;
1431 aEnd = aTmp;
1435 tools::Polygon aTempPoly( aRect, aStart, aEnd, PolyStyle::Arc, bFullCircle );
1436 basegfx::B2DPolygon aRetval;
1438 if ( bClockwise )
1440 for ( sal_uInt16 j = aTempPoly.GetSize(); j--; )
1442 aRetval.append(basegfx::B2DPoint(aTempPoly[ j ].X(), aTempPoly[ j ].Y()));
1445 else
1447 for ( sal_uInt16 j = 0; j < aTempPoly.GetSize(); j++ )
1449 aRetval.append(basegfx::B2DPoint(aTempPoly[ j ].X(), aTempPoly[ j ].Y()));
1453 return aRetval;
1456 void EnhancedCustomShape2d::CreateSubPath( sal_Int32& rSrcPt, sal_Int32& rSegmentInd, std::vector< SdrPathObj* >& rObjectList,
1457 const bool bLineGeometryNeededOnly,
1458 const bool bSortFilledObjectsToBack,
1459 sal_Int32 nIndex )
1461 bool bNoFill = false;
1462 bool bNoStroke = false;
1463 double dBrightness = 0.0; //no blending
1465 basegfx::B2DPolyPolygon aNewB2DPolyPolygon;
1466 basegfx::B2DPolygon aNewB2DPolygon;
1468 SetPathSize( nIndex );
1470 sal_Int32 nCoordSize = seqCoordinates.getLength();
1471 sal_Int32 nSegInfoSize = seqSegments.getLength();
1472 if ( !nSegInfoSize )
1474 const EnhancedCustomShapeParameterPair* pTmp = seqCoordinates.getArray();
1476 for ( sal_Int32 nPtNum(0L); nPtNum < nCoordSize; nPtNum++ )
1478 const Point aTempPoint(GetPoint( *pTmp++, true, true ));
1479 aNewB2DPolygon.append(basegfx::B2DPoint(aTempPoint.X(), aTempPoint.Y()));
1482 aNewB2DPolygon.setClosed(true);
1484 else
1486 for ( ;rSegmentInd < nSegInfoSize; )
1488 sal_Int16 nCommand = seqSegments[ rSegmentInd ].Command;
1489 sal_Int16 nPntCount= seqSegments[ rSegmentInd++ ].Count;
1491 switch ( nCommand )
1493 case NOFILL :
1494 bNoFill = true;
1495 break;
1496 case NOSTROKE :
1497 bNoStroke = true;
1498 break;
1499 case DARKEN :
1500 dBrightness = -0.4; //use sign to distinguish DARKEN from LIGHTEN
1501 break;
1502 case DARKENLESS :
1503 dBrightness = -0.2;
1504 break;
1505 case LIGHTEN :
1506 dBrightness = 0.4;
1507 break;
1508 case LIGHTENLESS :
1509 dBrightness = 0.2;
1510 break;
1511 case MOVETO :
1513 if(aNewB2DPolygon.count() > 1L)
1515 // #i76201# Add conversion to closed polygon when first and last points are equal
1516 basegfx::tools::checkClosed(aNewB2DPolygon);
1517 aNewB2DPolyPolygon.append(aNewB2DPolygon);
1520 aNewB2DPolygon.clear();
1522 if ( rSrcPt < nCoordSize )
1524 const Point aTempPoint(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
1525 SAL_INFO(
1526 "svx",
1527 "moveTo: " << aTempPoint.X() << ","
1528 << aTempPoint.Y());
1529 aNewB2DPolygon.append(basegfx::B2DPoint(aTempPoint.X(), aTempPoint.Y()));
1532 break;
1533 case ENDSUBPATH :
1534 break;
1535 case CLOSESUBPATH :
1537 if(aNewB2DPolygon.count())
1539 if(aNewB2DPolygon.count() > 1L)
1541 aNewB2DPolygon.setClosed(true);
1542 aNewB2DPolyPolygon.append(aNewB2DPolygon);
1545 aNewB2DPolygon.clear();
1548 break;
1549 case CURVETO :
1551 for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( ( rSrcPt + 2 ) < nCoordSize ); i++ )
1553 const Point aControlA(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
1554 const Point aControlB(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
1555 const Point aEnd(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
1557 DBG_ASSERT(aNewB2DPolygon.count(), "EnhancedCustomShape2d::CreateSubPath: Error in adding control point (!)");
1558 aNewB2DPolygon.appendBezierSegment(
1559 basegfx::B2DPoint(aControlA.X(), aControlA.Y()),
1560 basegfx::B2DPoint(aControlB.X(), aControlB.Y()),
1561 basegfx::B2DPoint(aEnd.X(), aEnd.Y()));
1564 break;
1566 case ANGLEELLIPSE :
1568 if ( nPntCount )
1570 if(aNewB2DPolygon.count() > 1L)
1572 // #i76201# Add conversion to closed polygon when first and last points are equal
1573 basegfx::tools::checkClosed(aNewB2DPolygon);
1574 aNewB2DPolyPolygon.append(aNewB2DPolygon);
1576 aNewB2DPolygon.clear();
1578 SAL_FALLTHROUGH;
1580 case ANGLEELLIPSETO :
1582 for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( ( rSrcPt + 2 ) < nCoordSize ); i++ )
1584 // create a circle
1585 Point _aCenter;
1586 double fWidth, fHeight;
1587 const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( mso_sptEllipse );
1588 bool bIsDefaultViewBox = false;
1589 bool bIsDefaultPath = false;
1590 bool bIsMSEllipse = false;
1592 if( ( nCoordWidth == pDefCustomShape->nCoordWidth )
1593 && ( nCoordHeight == pDefCustomShape->nCoordHeight ) )
1594 bIsDefaultViewBox = true;
1595 sal_Int32 j, nCount = pDefCustomShape->nVertices;//==3
1596 std::vector< css::drawing::EnhancedCustomShapeParameterPair> seqCoordinates1, seqCoordinates2;
1598 seqCoordinates1.resize( nCount );
1599 for ( j = 0; j < nCount; j++ )
1601 seqCoordinates1[j] = seqCoordinates[ rSrcPt + j];
1604 seqCoordinates2.resize( nCount );
1605 for ( j = 0; j < nCount; j++ )
1607 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqCoordinates2[ j ].First, pDefCustomShape->pVertices[ j ].nValA );
1608 EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( seqCoordinates2[ j ].Second, pDefCustomShape->pVertices[ j ].nValB );
1610 if(seqCoordinates1 == seqCoordinates2)
1611 bIsDefaultPath = true;
1613 OUString sShpType;
1614 SdrCustomShapeGeometryItem& rGeometryItem = const_cast<SdrCustomShapeGeometryItem&>(static_cast<const SdrCustomShapeGeometryItem&>(pCustomShapeObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY )));
1615 Any* pAny = rGeometryItem.GetPropertyValueByName( "Type" );
1616 if ( pAny )
1617 *pAny >>= sShpType;
1618 if( sShpType.getLength() > 3 &&
1619 sShpType.startsWith( "mso" )){
1620 bIsMSEllipse = true;
1622 if( (! bIsDefaultPath && ! bIsDefaultViewBox) || (bIsDefaultViewBox && bIsMSEllipse) /*&& (nGeneratorVersion == SfxObjectShell::Sym_L2)*/ )
1624 _aCenter = GetPoint( seqCoordinates[ rSrcPt ], true, true );
1625 GetParameter( fWidth, seqCoordinates[ rSrcPt + 1 ].First, true, false );
1626 GetParameter( fHeight, seqCoordinates[ rSrcPt + 1 ].Second, false, true );
1627 fWidth /= 2;
1628 fHeight /= 2;
1629 }else if( bIsDefaultPath && !bIsDefaultViewBox /*&& (nGeneratorVersion == SfxObjectShell::Sym_L2)*/ )
1631 _aCenter.X() = nCoordWidth/2 * fXScale;
1632 _aCenter.Y() = nCoordHeight/2 * fYScale;
1633 fWidth = nCoordWidth/2;
1634 fHeight = nCoordHeight/2;
1635 const Any* pViewBox = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( "ViewBox" );
1636 css::awt::Rectangle aViewBox;
1637 if ( pViewBox && (*pViewBox >>= aViewBox ) )
1639 aViewBox.Width = pDefCustomShape->nCoordWidth;
1640 aViewBox.Height = pDefCustomShape->nCoordHeight;
1642 css::beans::PropertyValue aPropVal;
1643 aPropVal.Name = "ViewBox";
1644 aPropVal.Value <<= aViewBox;
1645 rGeometryItem.SetPropertyValue( aPropVal );
1646 pCustomShapeObj->SetMergedItem( rGeometryItem );
1647 }else{
1648 _aCenter = GetPoint( seqCoordinates[ rSrcPt ], true, true );
1649 GetParameter( fWidth, seqCoordinates[ rSrcPt + 1 ].First, true, false);
1650 GetParameter( fHeight, seqCoordinates[ rSrcPt + 1 ].Second, false, true );
1653 fWidth *= fXScale;
1654 fHeight*= fYScale;
1655 Point aP( (sal_Int32)( _aCenter.X() - fWidth ), (sal_Int32)( _aCenter.Y() - fHeight ) );
1656 Size aS( (sal_Int32)( fWidth * 2.0 ), (sal_Int32)( fHeight * 2.0 ) );
1657 tools::Rectangle aRect( aP, aS );
1658 if ( aRect.GetWidth() && aRect.GetHeight() )
1660 double fStartAngle, fEndAngle;
1661 GetParameter( fStartAngle, seqCoordinates[ rSrcPt + 2 ].First, false, false );
1662 GetParameter( fEndAngle , seqCoordinates[ rSrcPt + 2 ].Second, false, false );
1664 if ( ((sal_Int32)fStartAngle % 360) != ((sal_Int32)fEndAngle % 360) )
1666 if ( (sal_Int32)fStartAngle & 0x7fff0000 ) // SJ: if the angle was imported from our escher import, then the
1667 fStartAngle /= 65536.0; // value is shifted by 16. TODO: already change the fixed float to a
1668 if ( (sal_Int32)fEndAngle & 0x7fff0000 ) // double in the import filter
1670 fEndAngle /= 65536.0;
1671 fEndAngle = fEndAngle + fStartAngle;
1672 if ( fEndAngle < 0 )
1673 { // in the binary filter the endangle is the amount
1674 double fTemp = fStartAngle;
1675 fStartAngle = fEndAngle;
1676 fEndAngle = fTemp;
1679 double fCenterX = aRect.Center().X();
1680 double fCenterY = aRect.Center().Y();
1681 double fx1 = ( cos( fStartAngle * F_PI180 ) * 65536.0 * fXScale ) + fCenterX;
1682 double fy1 = ( -sin( fStartAngle * F_PI180 ) * 65536.0 * fYScale ) + fCenterY;
1683 double fx2 = ( cos( fEndAngle * F_PI180 ) * 65536.0 * fXScale ) + fCenterX;
1684 double fy2 = ( -sin( fEndAngle * F_PI180 ) * 65536.0 * fYScale ) + fCenterY;
1685 aNewB2DPolygon.append(CreateArc( aRect, Point( (sal_Int32)fx1, (sal_Int32)fy1 ), Point( (sal_Int32)fx2, (sal_Int32)fy2 ), false));
1687 else
1688 { /* SJ: TODO: this block should be replaced sometimes, because the current point
1689 is not set correct, it also does not use the correct moveto
1690 point if ANGLEELLIPSETO was used, but the method CreateArc
1691 is at the moment not able to draw full circles (if startangle is 0
1692 and endangle 360 nothing is painted :-( */
1693 sal_Int32 nXControl = (sal_Int32)((double)aRect.GetWidth() * 0.2835 );
1694 sal_Int32 nYControl = (sal_Int32)((double)aRect.GetHeight() * 0.2835 );
1695 Point aCenter( aRect.Center() );
1697 // append start point
1698 aNewB2DPolygon.append(basegfx::B2DPoint(aCenter.X(), aRect.Top()));
1700 // append four bezier segments
1701 aNewB2DPolygon.appendBezierSegment(
1702 basegfx::B2DPoint(aCenter.X() + nXControl, aRect.Top()),
1703 basegfx::B2DPoint(aRect.Right(), aCenter.Y() - nYControl),
1704 basegfx::B2DPoint(aRect.Right(), aCenter.Y()));
1706 aNewB2DPolygon.appendBezierSegment(
1707 basegfx::B2DPoint(aRect.Right(), aCenter.Y() + nYControl),
1708 basegfx::B2DPoint(aCenter.X() + nXControl, aRect.Bottom()),
1709 basegfx::B2DPoint(aCenter.X(), aRect.Bottom()));
1711 aNewB2DPolygon.appendBezierSegment(
1712 basegfx::B2DPoint(aCenter.X() - nXControl, aRect.Bottom()),
1713 basegfx::B2DPoint(aRect.Left(), aCenter.Y() + nYControl),
1714 basegfx::B2DPoint(aRect.Left(), aCenter.Y()));
1716 aNewB2DPolygon.appendBezierSegment(
1717 basegfx::B2DPoint(aRect.Left(), aCenter.Y() - nYControl),
1718 basegfx::B2DPoint(aCenter.X() - nXControl, aRect.Top()),
1719 basegfx::B2DPoint(aCenter.X(), aRect.Top()));
1721 // close, rescue last controlpoint, remove double last point
1722 basegfx::tools::closeWithGeometryChange(aNewB2DPolygon);
1725 rSrcPt += 3;
1728 break;
1730 case QUADRATICCURVETO :
1732 for ( sal_Int32 i(0L); ( i < nPntCount ) && ( rSrcPt + 1 < nCoordSize ); i++ )
1734 if ( rSrcPt )
1736 const Point aPreviousEndPoint(GetPoint( seqCoordinates[ rSrcPt - 1 ], true, true));
1737 const Point aControlQ(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
1738 const Point aEnd(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
1739 const Point aControlA((aPreviousEndPoint + (aControlQ * 2)) / 3);
1740 const Point aControlB(((aControlQ * 2) + aEnd) / 3);
1742 DBG_ASSERT(aNewB2DPolygon.count(), "EnhancedCustomShape2d::CreateSubPath: Error in adding Q control point (!)");
1743 aNewB2DPolygon.appendBezierSegment(
1744 basegfx::B2DPoint(aControlA.X(), aControlA.Y()),
1745 basegfx::B2DPoint(aControlB.X(), aControlB.Y()),
1746 basegfx::B2DPoint(aEnd.X(), aEnd.Y()));
1748 else // no previous point , do a moveto
1750 rSrcPt++; // skip control point
1751 const Point aEnd(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
1753 DBG_ASSERT(aNewB2DPolygon.count(), "EnhancedCustomShape2d::CreateSubPath: Error in adding Q control point (!)");
1754 aNewB2DPolygon.append(basegfx::B2DPoint(aEnd.X(), aEnd.Y()));
1758 break;
1760 case LINETO :
1762 for ( sal_Int32 i(0L); ( i < nPntCount ) && ( rSrcPt < nCoordSize ); i++ )
1764 const Point aTempPoint(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
1765 SAL_INFO(
1766 "svx",
1767 "lineTo: " << aTempPoint.X() << ","
1768 << aTempPoint.Y());
1769 aNewB2DPolygon.append(basegfx::B2DPoint(aTempPoint.X(), aTempPoint.Y()));
1772 break;
1774 case ARC :
1775 case CLOCKWISEARC :
1777 if(aNewB2DPolygon.count() > 1L)
1779 // #i76201# Add conversion to closed polygon when first and last points are equal
1780 basegfx::tools::checkClosed(aNewB2DPolygon);
1781 aNewB2DPolyPolygon.append(aNewB2DPolygon);
1784 aNewB2DPolygon.clear();
1786 SAL_FALLTHROUGH;
1788 case ARCTO :
1789 case CLOCKWISEARCTO :
1791 bool bClockwise = ( nCommand == CLOCKWISEARC ) || ( nCommand == CLOCKWISEARCTO );
1792 sal_uInt32 nXor = bClockwise ? 3 : 2;
1793 for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( ( rSrcPt + 3 ) < nCoordSize ); i++ )
1795 tools::Rectangle aRect( GetPoint( seqCoordinates[ rSrcPt ], true, true ), GetPoint( seqCoordinates[ rSrcPt + 1 ], true, true ) );
1796 if ( aRect.GetWidth() && aRect.GetHeight() )
1798 Point aCenter( aRect.Center() );
1799 Point aStart( GetPoint( seqCoordinates[ (sal_uInt16)( rSrcPt + nXor ) ], true, true ) );
1800 Point aEnd( GetPoint( seqCoordinates[ (sal_uInt16)( rSrcPt + ( nXor ^ 1 ) ) ], true, true ) );
1801 aStart.X() = (sal_Int32)( ( (double)( aStart.X() - aCenter.X() ) ) ) + aCenter.X();
1802 aStart.Y() = (sal_Int32)( ( (double)( aStart.Y() - aCenter.Y() ) ) ) + aCenter.Y();
1803 aEnd.X() = (sal_Int32)( ( (double)( aEnd.X() - aCenter.X() ) ) ) + aCenter.X();
1804 aEnd.Y() = (sal_Int32)( ( (double)( aEnd.Y() - aCenter.Y() ) ) ) + aCenter.Y();
1805 aNewB2DPolygon.append(CreateArc( aRect, aStart, aEnd, bClockwise));
1807 rSrcPt += 4;
1810 break;
1812 case ARCANGLETO :
1814 double fWR, fHR, fStartAngle, fSwingAngle;
1816 for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( rSrcPt + 1 < nCoordSize ); i++ )
1818 GetParameter ( fWR, seqCoordinates[ (sal_uInt16)( rSrcPt ) ].First, true, false );
1819 GetParameter ( fHR, seqCoordinates[ (sal_uInt16)( rSrcPt ) ].Second, false, true );
1821 GetParameter ( fStartAngle, seqCoordinates[ (sal_uInt16)( rSrcPt + 1) ].First, false, false );
1822 GetParameter ( fSwingAngle, seqCoordinates[ (sal_uInt16)( rSrcPt + 1 ) ].Second, false, false );
1824 // Convert angles to radians, but don't do any scaling / translation yet.
1826 fStartAngle *= F_PI180;
1827 fSwingAngle *= F_PI180;
1829 SAL_INFO("svx", "ARCANGLETO scale: " << fWR << "x" << fHR << " angles: " << fStartAngle << "," << fSwingAngle);
1831 bool bClockwise = fSwingAngle >= 0.0;
1833 if (aNewB2DPolygon.count() > 0)
1835 basegfx::B2DPoint aStartPointB2D( aNewB2DPolygon.getB2DPoint(aNewB2DPolygon.count() - 1 ) );
1836 Point aStartPoint( 0, 0 );
1838 double fT = atan2((fWR*sin(fStartAngle)), (fHR*cos(fStartAngle)));
1839 double fTE = atan2((fWR*sin(fStartAngle + fSwingAngle)), fHR*cos(fStartAngle + fSwingAngle));
1841 SAL_INFO("svx", "ARCANGLETO angles: " << fStartAngle << ", " << fSwingAngle
1842 << " --> parameters: " << fT <<", " << fTE );
1844 fWR *= fXScale;
1845 fHR *= fYScale;
1847 tools::Rectangle aRect ( Point ( aStartPoint.getX() - fWR*cos(fT) - fWR, aStartPoint.getY() - fHR*sin(fT) - fHR ),
1848 Point ( aStartPoint.getX() - fWR*cos(fT) + fWR, aStartPoint.getY() - fHR*sin(fT) + fHR) );
1850 Point aEndPoint ( aStartPoint.getX() - fWR*(cos(fT) - cos(fTE)), aStartPoint.getY() - fHR*(sin(fT) - sin(fTE)) );
1852 SAL_INFO(
1853 "svx",
1854 "ARCANGLETO rect: " << aRect.Left() << ", "
1855 << aRect.Top() << " x " << aRect.Right()
1856 << ", " << aRect.Bottom() << " start: "
1857 << aStartPoint.X() << ", "
1858 << aStartPoint.Y() << " end: "
1859 << aEndPoint.X() << ", " << aEndPoint.Y()
1860 << " clockwise: " << int(bClockwise));
1861 basegfx::B2DPolygon aArc = CreateArc( aRect, bClockwise ? aEndPoint : aStartPoint, bClockwise ? aStartPoint : aEndPoint, bClockwise, aStartPoint == aEndPoint && ((bClockwise && fSwingAngle > F_PI) || (!bClockwise && fSwingAngle < -F_PI)));
1862 // Now that we have the arc, move it to aStartPointB2D.
1863 basegfx::B2DHomMatrix aMatrix = basegfx::tools::createTranslateB2DHomMatrix(aStartPointB2D.getX(), aStartPointB2D.getY());
1864 aArc.transform(aMatrix);
1865 aNewB2DPolygon.append(aArc);
1868 rSrcPt += 2;
1871 break;
1873 case ELLIPTICALQUADRANTX :
1874 case ELLIPTICALQUADRANTY :
1876 bool bFirstDirection(true);
1877 basegfx::B2DPoint aControlPointA;
1878 basegfx::B2DPoint aControlPointB;
1880 for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( rSrcPt < nCoordSize ); i++ )
1882 sal_uInt32 nModT = ( nCommand == ELLIPTICALQUADRANTX ) ? 1 : 0;
1883 Point aCurrent( GetPoint( seqCoordinates[ rSrcPt ], true, true ) );
1885 if ( rSrcPt ) // we need a previous point
1887 Point aPrev( GetPoint( seqCoordinates[ rSrcPt - 1 ], true, true ) );
1888 sal_Int32 nX, nY;
1889 nX = aCurrent.X() - aPrev.X();
1890 nY = aCurrent.Y() - aPrev.Y();
1891 if ( ( nY ^ nX ) & 0x80000000 )
1893 if ( !i )
1894 bFirstDirection = true;
1895 else if ( !bFirstDirection )
1896 nModT ^= 1;
1898 else
1900 if ( !i )
1901 bFirstDirection = false;
1902 else if ( bFirstDirection )
1903 nModT ^= 1;
1905 if ( nModT ) // get the right corner
1907 nX = aCurrent.X();
1908 nY = aPrev.Y();
1910 else
1912 nX = aPrev.X();
1913 nY = aCurrent.Y();
1915 sal_Int32 nXVec = ( nX - aPrev.X() ) >> 1;
1916 sal_Int32 nYVec = ( nY - aPrev.Y() ) >> 1;
1917 Point aControl1( aPrev.X() + nXVec, aPrev.Y() + nYVec );
1919 aControlPointA = basegfx::B2DPoint(aControl1.X(), aControl1.Y());
1921 nXVec = ( nX - aCurrent.X() ) >> 1;
1922 nYVec = ( nY - aCurrent.Y() ) >> 1;
1923 Point aControl2( aCurrent.X() + nXVec, aCurrent.Y() + nYVec );
1925 aControlPointB = basegfx::B2DPoint(aControl2.X(), aControl2.Y());
1927 aNewB2DPolygon.appendBezierSegment(
1928 aControlPointA,
1929 aControlPointB,
1930 basegfx::B2DPoint(aCurrent.X(), aCurrent.Y()));
1932 else
1934 aNewB2DPolygon.append(basegfx::B2DPoint(aCurrent.X(), aCurrent.Y()));
1937 rSrcPt++;
1940 break;
1942 #ifdef DBG_CUSTOMSHAPE
1943 case UNKNOWN :
1944 default :
1946 OStringBuffer aString("CustomShapes::unknown PolyFlagValue :");
1947 aString.append(static_cast<sal_Int32>(nCommand));
1948 OSL_FAIL(aString.getStr());
1950 break;
1951 #endif
1953 if ( nCommand == ENDSUBPATH )
1954 break;
1957 if ( rSegmentInd == nSegInfoSize )
1958 rSegmentInd++;
1960 if(aNewB2DPolygon.count() > 1L)
1962 // #i76201# Add conversion to closed polygon when first and last points are equal
1963 basegfx::tools::checkClosed(aNewB2DPolygon);
1964 aNewB2DPolyPolygon.append(aNewB2DPolygon);
1967 if(aNewB2DPolyPolygon.count())
1969 if( !bLineGeometryNeededOnly )
1971 // hack aNewB2DPolyPolygon to fill logic rect - this is
1972 // needed to produce gradient fills that look like mso
1973 aNewB2DPolygon.clear();
1974 aNewB2DPolygon.append(basegfx::B2DPoint(0,0));
1975 aNewB2DPolygon.setClosed(true);
1976 aNewB2DPolyPolygon.append(aNewB2DPolygon);
1978 aNewB2DPolygon.clear();
1979 aNewB2DPolygon.append(basegfx::B2DPoint(aLogicRect.GetWidth(),
1980 aLogicRect.GetHeight()));
1981 aNewB2DPolygon.setClosed(true);
1982 aNewB2DPolyPolygon.append(aNewB2DPolygon);
1985 // #i37011#
1986 bool bForceCreateTwoObjects(false);
1988 if(!bSortFilledObjectsToBack && !aNewB2DPolyPolygon.isClosed() && !bNoStroke)
1990 bForceCreateTwoObjects = true;
1993 if(bLineGeometryNeededOnly)
1995 bForceCreateTwoObjects = true;
1996 bNoFill = true;
1997 bNoStroke = false;
2000 if(bForceCreateTwoObjects || bSortFilledObjectsToBack)
2002 if(bFilled && !bNoFill)
2004 basegfx::B2DPolyPolygon aClosedPolyPolygon(aNewB2DPolyPolygon);
2005 aClosedPolyPolygon.setClosed(true);
2006 SdrPathObj* pFill = new SdrPathObj(OBJ_POLY, aClosedPolyPolygon, dBrightness);
2007 SfxItemSet aTempSet(*this);
2008 aTempSet.Put(makeSdrShadowItem(false));
2009 aTempSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
2010 pFill->SetMergedItemSet(aTempSet);
2011 rObjectList.push_back(pFill);
2014 if(!bNoStroke)
2016 // there is no reason to use OBJ_PLIN here when the polygon is actually closed,
2017 // the non-fill is defined by XFILL_NONE. Since SdrPathObj::ImpForceKind() needs
2018 // to correct the polygon (here: open it) using the type, the last edge may get lost.
2019 // Thus, use a type that fits the polygon
2020 SdrPathObj* pStroke = new SdrPathObj(
2021 aNewB2DPolyPolygon.isClosed() ? OBJ_POLY : OBJ_PLIN,
2022 aNewB2DPolyPolygon, dBrightness);
2023 SfxItemSet aTempSet(*this);
2024 aTempSet.Put(makeSdrShadowItem(false));
2025 aTempSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
2026 pStroke->SetMergedItemSet(aTempSet);
2027 rObjectList.push_back(pStroke);
2030 else
2032 SdrPathObj* pObj = nullptr;
2033 SfxItemSet aTempSet(*this);
2034 aTempSet.Put(makeSdrShadowItem(false));
2036 if(bNoFill)
2038 // see comment above about OBJ_PLIN
2039 pObj = new SdrPathObj(
2040 aNewB2DPolyPolygon.isClosed() ? OBJ_POLY : OBJ_PLIN,
2041 aNewB2DPolyPolygon, dBrightness);
2042 aTempSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
2044 else
2046 aNewB2DPolyPolygon.setClosed(true);
2047 pObj = new SdrPathObj(OBJ_POLY, aNewB2DPolyPolygon, dBrightness);
2050 if(bNoStroke)
2052 aTempSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
2055 if(pObj)
2057 pObj->SetMergedItemSet(aTempSet);
2058 rObjectList.push_back(pObj);
2064 void CorrectCalloutArrows( MSO_SPT eSpType, sal_uInt32 nLineObjectCount, std::vector< SdrPathObj* >& vObjectList )
2066 bool bAccent = false;
2067 switch( eSpType )
2069 case mso_sptCallout1 :
2070 case mso_sptBorderCallout1 :
2071 case mso_sptCallout90 :
2072 case mso_sptBorderCallout90 :
2073 default:
2074 break;
2076 case mso_sptAccentCallout1 :
2077 case mso_sptAccentBorderCallout1 :
2078 case mso_sptAccentCallout90 :
2079 case mso_sptAccentBorderCallout90 :
2081 sal_uInt32 nLine = 0;
2082 for ( SdrPathObj* pObj: vObjectList )
2084 if(pObj->IsLine())
2086 nLine++;
2087 if ( nLine == nLineObjectCount )
2089 pObj->ClearMergedItem( XATTR_LINESTART );
2090 pObj->ClearMergedItem( XATTR_LINEEND );
2095 break;
2097 // switch start & end
2098 case mso_sptAccentCallout2 :
2099 case mso_sptAccentBorderCallout2 :
2100 bAccent = true;
2101 SAL_FALLTHROUGH;
2102 case mso_sptCallout2 :
2103 case mso_sptBorderCallout2 :
2105 sal_uInt32 nLine = 0;
2106 for ( SdrPathObj* pObj: vObjectList )
2108 if(pObj->IsLine())
2110 nLine++;
2111 if ( nLine == 1 )
2112 pObj->ClearMergedItem( XATTR_LINEEND );
2113 else if ( ( bAccent && ( nLine == nLineObjectCount - 1 ) ) || ( !bAccent && ( nLine == nLineObjectCount ) ) )
2114 pObj->ClearMergedItem( XATTR_LINESTART );
2115 else
2117 pObj->ClearMergedItem( XATTR_LINESTART );
2118 pObj->ClearMergedItem( XATTR_LINEEND );
2123 break;
2125 case mso_sptAccentCallout3 :
2126 case mso_sptAccentBorderCallout3 :
2127 case mso_sptCallout3 :
2128 case mso_sptBorderCallout3 :
2130 sal_uInt32 nLine = 0;
2131 for ( SdrPathObj* pObj: vObjectList )
2133 if(pObj->IsLine())
2135 if ( nLine )
2137 pObj->ClearMergedItem( XATTR_LINESTART );
2138 pObj->ClearMergedItem( XATTR_LINEEND );
2140 else
2141 EnhancedCustomShape2d::SwapStartAndEndArrow( pObj );
2143 nLine++;
2147 break;
2151 void EnhancedCustomShape2d::AdaptObjColor(SdrPathObj& rObj, const SfxItemSet& rCustomShapeSet,
2152 sal_uInt32& nColorIndex, sal_uInt32 nColorCount)
2154 if ( !rObj.IsLine() )
2156 const drawing::FillStyle eFillStyle = static_cast<const XFillStyleItem&>(rObj.GetMergedItem(XATTR_FILLSTYLE)).GetValue();
2157 switch( eFillStyle )
2159 default:
2160 case drawing::FillStyle_SOLID:
2162 Color aFillColor;
2163 if ( nColorCount || rObj.GetBrightness() != 0.0 )
2165 aFillColor = GetColorData(
2166 static_cast<const XFillColorItem&>(rCustomShapeSet.Get( XATTR_FILLCOLOR )).GetColorValue(),
2167 std::min(nColorIndex, nColorCount-1), rObj.GetBrightness() );
2168 rObj.SetMergedItem( XFillColorItem( "", aFillColor ) );
2170 break;
2172 case drawing::FillStyle_GRADIENT:
2174 XGradient aXGradient(static_cast<const XFillGradientItem&>(rObj.GetMergedItem(XATTR_FILLGRADIENT)).GetGradientValue());
2175 if ( nColorCount || rObj.GetBrightness() != 0.0 )
2177 aXGradient.SetStartColor(
2178 GetColorData(
2179 aXGradient.GetStartColor(),
2180 std::min(nColorIndex, nColorCount-1), rObj.GetBrightness() ));
2181 aXGradient.SetEndColor(
2182 GetColorData(
2183 aXGradient.GetEndColor(),
2184 std::min(nColorIndex, nColorCount-1), rObj.GetBrightness() ));
2187 rObj.SetMergedItem( XFillGradientItem( "", aXGradient ) );
2188 break;
2190 case drawing::FillStyle_HATCH:
2192 XHatch aXHatch(static_cast<const XFillHatchItem&>(rObj.GetMergedItem(XATTR_FILLHATCH)).GetHatchValue());
2193 if ( nColorCount || rObj.GetBrightness() != 0.0 )
2195 aXHatch.SetColor(
2196 GetColorData(
2197 aXHatch.GetColor(),
2198 std::min(nColorIndex, nColorCount-1), rObj.GetBrightness() ));
2201 rObj.SetMergedItem( XFillHatchItem( "", aXHatch ) );
2202 break;
2204 case drawing::FillStyle_BITMAP:
2206 if ( nColorCount || rObj.GetBrightness() != 0.0 )
2208 Bitmap aBitmap(static_cast<const XFillBitmapItem&>(rObj.GetMergedItem(XATTR_FILLBITMAP)).GetGraphicObject().GetGraphic().GetBitmapEx().GetBitmap());
2210 aBitmap.Adjust(
2211 static_cast< short > ( GetLuminanceChange(
2212 std::min(nColorIndex, nColorCount-1))));
2214 rObj.SetMergedItem(XFillBitmapItem(OUString(), Graphic(aBitmap)));
2217 break;
2221 if ( nColorIndex < nColorCount )
2222 nColorIndex++;
2226 SdrObject* EnhancedCustomShape2d::CreatePathObj( bool bLineGeometryNeededOnly )
2228 sal_Int32 nCoordSize = seqCoordinates.getLength();
2229 if ( !nCoordSize )
2230 return nullptr;
2232 std::vector< SdrPathObj* > vObjectList;
2233 bool bSortFilledObjectsToBack = SortFilledObjectsToBackByDefault( eSpType );
2235 sal_Int32 nSubPathIndex = 0;
2237 sal_Int32 nSrcPt = 0;
2238 sal_Int32 nSegmentInd = 0;
2239 while( nSegmentInd <= seqSegments.getLength() )
2241 CreateSubPath( nSrcPt, nSegmentInd, vObjectList, bLineGeometryNeededOnly, bSortFilledObjectsToBack, nSubPathIndex );
2242 nSubPathIndex ++;
2245 SdrObject* pRet = nullptr;
2247 if ( !vObjectList.empty() )
2249 const SfxItemSet& rCustomShapeSet = pCustomShapeObj->GetMergedItemSet();
2250 sal_uInt32 nColorCount = nColorData >> 28;
2251 sal_uInt32 nColorIndex = 0;
2253 // #i37011# remove invisible objects
2254 if(!vObjectList.empty())
2256 std::vector< SdrPathObj* > vTempList;
2258 for(SdrPathObj* pObj : vObjectList)
2260 const drawing::LineStyle eLineStyle =static_cast<const XLineStyleItem&>(pObj->GetMergedItem(XATTR_LINESTYLE)).GetValue();
2261 const drawing::FillStyle eFillStyle = static_cast<const XFillStyleItem&>(pObj->GetMergedItem(XATTR_FILLSTYLE)).GetValue();
2263 // #i40600# if bLineGeometryNeededOnly is set, linestyle does not matter
2264 if( !bLineGeometryNeededOnly && ( drawing::LineStyle_NONE == eLineStyle ) && ( drawing::FillStyle_NONE == eFillStyle ) )
2265 delete pObj;
2266 else
2267 vTempList.push_back(pObj);
2270 vObjectList = vTempList;
2273 if(1L == vObjectList.size())
2275 // a single object, correct some values
2276 AdaptObjColor(*vObjectList[0L],rCustomShapeSet,nColorIndex,nColorCount);
2278 else
2280 sal_Int32 nLineObjectCount = 0;
2281 sal_Int32 nAreaObjectCount = 0;
2283 // correct some values and collect content data
2284 for (SdrPathObj* pObj : vObjectList)
2286 if(pObj->IsLine())
2288 nLineObjectCount++;
2290 else
2292 nAreaObjectCount++;
2293 AdaptObjColor(*pObj,rCustomShapeSet,nColorIndex,nColorCount);
2297 // #i88870# correct line arrows for callouts
2298 if ( nLineObjectCount )
2299 CorrectCalloutArrows( eSpType, nLineObjectCount, vObjectList );
2301 // sort objects so that filled ones are in front. Necessary
2302 // for some strange objects
2303 if ( bSortFilledObjectsToBack )
2305 std::vector< SdrPathObj* > vTempList;
2306 vTempList.reserve(vObjectList.size());
2308 for (SdrPathObj* pObj : vObjectList)
2310 if ( !pObj->IsLine() )
2312 vTempList.push_back(pObj);
2316 for (SdrPathObj* pObj : vObjectList)
2318 if ( pObj->IsLine() )
2320 vTempList.push_back(pObj);
2324 vObjectList = vTempList;
2329 // #i37011#
2330 if(!vObjectList.empty())
2332 // copy remaining objects to pRet
2333 if(vObjectList.size() > 1)
2335 pRet = new SdrObjGroup;
2337 for (SdrPathObj* pObj : vObjectList)
2339 pRet->GetSubList()->NbcInsertObject(pObj);
2342 else if(1 == vObjectList.size())
2344 pRet = vObjectList[0L];
2347 if(pRet)
2349 // move to target position
2350 tools::Rectangle aCurRect(pRet->GetSnapRect());
2351 aCurRect.Move(aLogicRect.Left(), aLogicRect.Top());
2352 pRet->NbcSetSnapRect(aCurRect);
2356 return pRet;
2359 SdrObject* EnhancedCustomShape2d::CreateObject( bool bLineGeometryNeededOnly )
2361 SdrObject* pRet = nullptr;
2363 if ( eSpType == mso_sptRectangle )
2365 pRet = new SdrRectObj( aLogicRect );
2366 pRet->SetMergedItemSet( *this );
2368 if ( !pRet )
2369 pRet = CreatePathObj( bLineGeometryNeededOnly );
2371 return pRet;
2374 void EnhancedCustomShape2d::ApplyGluePoints( SdrObject* pObj )
2376 if ( pObj && seqGluePoints.getLength() )
2378 sal_uInt32 i, nCount = seqGluePoints.getLength();
2379 for ( i = 0; i < nCount; i++ )
2381 SdrGluePoint aGluePoint;
2383 aGluePoint.SetPos( GetPoint( seqGluePoints[ i ], true, true ) );
2384 aGluePoint.SetPercent( false );
2385 aGluePoint.SetAlign( SdrAlign::VERT_TOP | SdrAlign::HORZ_LEFT );
2386 aGluePoint.SetEscDir( SdrEscapeDirection::SMART );
2387 SdrGluePointList* pList = pObj->ForceGluePointList();
2388 if( pList )
2389 /* sal_uInt16 nId = */ pList->Insert( aGluePoint );
2394 bool EnhancedCustomShape2d::IsPostRotate() const
2396 return dynamic_cast<const SdrObjCustomShape*>( pCustomShapeObj) != nullptr && static_cast<SdrObjCustomShape*>(pCustomShapeObj)->IsPostRotate();
2399 SdrObject* EnhancedCustomShape2d::CreateLineGeometry()
2401 return CreateObject( true );
2405 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */