LibreOffice Module svx (master) 1
EnhancedCustomShape2d.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
22#include <o3tl/string_view.hxx>
26#include <svx/svdoashp.hxx>
27#include <svx/svdtrans.hxx>
28#include <svx/svdogrp.hxx>
29#include <svx/svdopath.hxx>
30#include <svx/svdorect.hxx>
31#include <svx/svdpage.hxx>
32#include <svx/xflclit.hxx>
33#include <svx/xfillit0.hxx>
34#include <svx/xlineit0.hxx>
35#include <svx/xlnstit.hxx>
36#include <svx/xlnedit.hxx>
37#include <svx/xlnstwit.hxx>
38#include <svx/xlnedwit.hxx>
39#include <svx/xlnstcit.hxx>
40#include <svx/xlnedcit.hxx>
41#include <svx/xflgrit.hxx>
42#include <svx/xflhtit.hxx>
43#include <svx/xbtmpit.hxx>
44#include <svx/xgrad.hxx>
45#include <svx/xhatch.hxx>
46#include <svx/sdshitm.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 <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
51#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
52#include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
58#include <sal/log.hxx>
59
60#include <algorithm>
61#include <cstdlib>
62#include <string_view>
63#include <unordered_set>
64
65using namespace ::com::sun::star;
66using namespace ::com::sun::star::uno;
67using namespace ::com::sun::star::drawing;
68using namespace ::com::sun::star::drawing::EnhancedCustomShapeSegmentCommand;
69
70void EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( EnhancedCustomShapeParameter& rParameter, const sal_Int32 nValue )
71{
72 sal_uInt32 nDat = static_cast<sal_uInt32>(nValue);
73 sal_Int32 nNewValue = nValue;
74
75 // check if this is a special point
76 if ( ( nDat >> 16 ) == 0x8000 )
77 {
78 nNewValue = static_cast<sal_uInt16>(nDat);
79 rParameter.Type = EnhancedCustomShapeParameterType::EQUATION;
80 }
81 else
82 rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
83 rParameter.Value <<= nNewValue;
84}
85
86OUString EnhancedCustomShape2d::GetEquation( const sal_uInt16 nFlags, sal_Int32 nP1, sal_Int32 nP2, sal_Int32 nP3 )
87{
88 OUString aEquation;
89 bool b1Special = ( nFlags & 0x2000 ) != 0;
90 bool b2Special = ( nFlags & 0x4000 ) != 0;
91 bool b3Special = ( nFlags & 0x8000 ) != 0;
92 switch( nFlags & 0xff )
93 {
94 case 0 :
95 case 14 :
96 {
97 sal_Int32 nOptimize = 0;
98 if ( nP1 )
99 nOptimize |= 1;
100 if ( nP2 )
101 nOptimize |= 2;
102 if ( b1Special )
103 nOptimize |= 4;
104 if ( b2Special )
105 nOptimize |= 8;
106 switch( nOptimize )
107 {
108 case 0 :
109 break;
110 case 1 :
111 case 4 :
112 case 5 :
114 break;
115 case 2 :
116 case 8 :
117 case 10:
119 break;
120 default :
121 {
123 aEquation += "+";
125 }
126 break;
127 }
128 if ( b3Special || nP3 )
129 {
130 aEquation += "-";
132 }
133 }
134 break;
135 case 1 :
136 {
138 if ( b2Special || ( nP2 != 1 ) )
139 {
140 aEquation += "*";
142 }
143 if ( b3Special || ( ( nP3 != 1 ) && ( nP3 != 0 ) ) )
144 {
145 aEquation += "/";
147 }
148 }
149 break;
150 case 2 :
151 {
152 aEquation += "(";
154 aEquation += "+";
156 aEquation += ")/2";
157 }
158 break;
159 case 3 :
160 {
161 aEquation += "abs(";
163 aEquation += ")";
164 }
165 break;
166 case 4 :
167 {
168 aEquation += "min(";
170 aEquation += ",";
172 aEquation += ")";
173 }
174 break;
175 case 5 :
176 {
177 aEquation += "max(";
179 aEquation += ",";
181 aEquation += ")";
182 }
183 break;
184 case 6 :
185 {
186 aEquation += "if(";
188 aEquation += ",";
190 aEquation += ",";
192 aEquation += ")";
193 }
194 break;
195 case 7 :
196 {
197 aEquation += "sqrt(";
199 aEquation += "*";
201 aEquation += "+";
203 aEquation += "*";
205 aEquation += "+";
207 aEquation += "*";
209 aEquation += ")";
210 }
211 break;
212 case 8 :
213 {
214 aEquation += "atan2(";
216 aEquation += ",";
218 aEquation += ")/(pi/180)";
219 }
220 break;
221 case 9 :
222 {
224 aEquation += "*sin(";
226 aEquation += "*(pi/180))";
227 }
228 break;
229 case 10 :
230 {
232 aEquation += "*cos(";
234 aEquation += "*(pi/180))";
235 }
236 break;
237 case 11 :
238 {
240 aEquation += "*cos(atan2(";
242 aEquation += ",";
244 aEquation += "))";
245 }
246 break;
247 case 12 :
248 {
250 aEquation += "*sin(atan2(";
252 aEquation += ",";
254 aEquation += "))";
255 }
256 break;
257 case 13 :
258 {
259 aEquation += "sqrt(";
261 aEquation += ")";
262 }
263 break;
264 case 15 :
265 {
267 aEquation += "*sqrt(1-(";
269 aEquation += "/";
271 aEquation += ")"
272 "*(";
274 aEquation += "/";
276 aEquation += "))";
277 }
278 break;
279 case 16 :
280 {
282 aEquation += "*tan(";
284 aEquation += ")";
285 }
286 break;
287 case 0x80 :
288 {
289 aEquation += "sqrt(";
291 aEquation += "*";
293 aEquation += "-";
295 aEquation += "*";
297 aEquation += ")";
298 }
299 break;
300 case 0x81 :
301 {
302 aEquation += "(cos(";
304 aEquation += "*(pi/180))*(";
306 aEquation += "-10800)+sin(";
308 aEquation += "*(pi/180))*(";
310 aEquation += "-10800))+10800";
311 }
312 break;
313 case 0x82 :
314 {
315 aEquation += "-(sin(";
317 aEquation += "*(pi/180))*(";
319 aEquation += "-10800)-cos(";
321 aEquation += "*(pi/180))*(";
323 aEquation += "-10800))+10800";
324 }
325 break;
326 }
327 return aEquation;
328}
329
330void EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( OUString& rParameter, const sal_Int32 nPara, const bool bIsSpecialValue )
331{
332 if ( bIsSpecialValue )
333 {
334 if ( nPara & 0x400 )
335 {
336 rParameter += "?";
337 rParameter += OUString::number( nPara & 0xff );
338 rParameter += " ";
339 }
340 else
341 {
342 switch( nPara )
343 {
354 {
355 rParameter += "$";
356 rParameter += OUString::number( nPara - DFF_Prop_adjustValue );
357 rParameter += " ";
358 }
359 break;
360 case DFF_Prop_geoLeft :
361 {
362 rParameter += "left";
363 }
364 break;
365 case DFF_Prop_geoTop :
366 {
367 rParameter += "top";
368 }
369 break;
370 case DFF_Prop_geoRight :
371 {
372 rParameter += "right";
373 }
374 break;
375 case DFF_Prop_geoBottom :
376 {
377 rParameter += "bottom";
378 }
379 break;
380 }
381 }
382 }
383 else
384 {
385 rParameter += OUString::number( nPara );
386 }
387}
388
389void EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( EnhancedCustomShapeParameter& rParameter, const sal_Int32 nPara, const bool bIsSpecialValue, bool bHorz )
390{
391 sal_Int32 nValue = 0;
392 if ( bIsSpecialValue )
393 {
394 if ( ( nPara >= 0x100 ) && ( nPara <= 0x107 ) )
395 {
396 nValue = nPara & 0xff;
397 rParameter.Type = EnhancedCustomShapeParameterType::ADJUSTMENT;
398 }
399 else if ( ( nPara >= 3 ) && ( nPara <= 0x82 ) )
400 {
401 nValue = nPara - 3;
402 rParameter.Type = EnhancedCustomShapeParameterType::EQUATION;
403 }
404 else if ( nPara == 0 )
405 {
406 nValue = 0;
407 if ( bHorz )
408 rParameter.Type = EnhancedCustomShapeParameterType::LEFT;
409 else
410 rParameter.Type = EnhancedCustomShapeParameterType::TOP;
411 }
412 else if ( nPara == 1 )
413 {
414 nValue = 0;
415 if ( bHorz )
416 rParameter.Type = EnhancedCustomShapeParameterType::RIGHT;
417 else
418 rParameter.Type = EnhancedCustomShapeParameterType::BOTTOM;
419 }
420 else if ( nPara == 2 ) // means to be centered, but should not be
421 { // used in our implementation
422 nValue = 5600;
423 rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
424 }
425 else
426 {
427 nValue = nPara;
428 rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
429 }
430 }
431 else
432 {
433 nValue = nPara;
434 rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
435 }
436 rParameter.Value <<= nValue;
437}
438
440 const css::beans::PropertyValues& rHandleProperties,
441 EnhancedCustomShape2d::Handle& rDestinationHandle )
442{
443 bool bRetValue = false;
444 if ( rHandleProperties.hasElements() )
445 {
446 rDestinationHandle.nFlags = HandleFlags::NONE;
447 for ( const css::beans::PropertyValue& rPropVal : rHandleProperties )
448 {
449 if ( rPropVal.Name == "Position" )
450 {
451 if ( rPropVal.Value >>= rDestinationHandle.aPosition )
452 bRetValue = true;
453 }
454 else if ( rPropVal.Name == "MirroredX" )
455 {
456 bool bMirroredX;
457 if ( rPropVal.Value >>= bMirroredX )
458 {
459 if ( bMirroredX )
460 rDestinationHandle.nFlags |= HandleFlags::MIRRORED_X;
461 }
462 }
463 else if ( rPropVal.Name == "MirroredY" )
464 {
465 bool bMirroredY;
466 if ( rPropVal.Value >>= bMirroredY )
467 {
468 if ( bMirroredY )
469 rDestinationHandle.nFlags |= HandleFlags::MIRRORED_Y;
470 }
471 }
472 else if ( rPropVal.Name == "Switched" )
473 {
474 bool bSwitched;
475 if ( rPropVal.Value >>= bSwitched )
476 {
477 if ( bSwitched )
478 rDestinationHandle.nFlags |= HandleFlags::SWITCHED;
479 }
480 }
481 else if ( rPropVal.Name == "Polar" )
482 {
483 if ( rPropVal.Value >>= rDestinationHandle.aPolar )
484 rDestinationHandle.nFlags |= HandleFlags::POLAR;
485 }
486 else if ( rPropVal.Name == "RefX" )
487 {
488 if ( rPropVal.Value >>= rDestinationHandle.nRefX )
489 rDestinationHandle.nFlags |= HandleFlags::REFX;
490 }
491 else if ( rPropVal.Name == "RefY" )
492 {
493 if ( rPropVal.Value >>= rDestinationHandle.nRefY )
494 rDestinationHandle.nFlags |= HandleFlags::REFY;
495 }
496 else if ( rPropVal.Name == "RefAngle" )
497 {
498 if ( rPropVal.Value >>= rDestinationHandle.nRefAngle )
499 rDestinationHandle.nFlags |= HandleFlags::REFANGLE;
500 }
501 else if ( rPropVal.Name == "RefR" )
502 {
503 if ( rPropVal.Value >>= rDestinationHandle.nRefR )
504 rDestinationHandle.nFlags |= HandleFlags::REFR;
505 }
506 else if ( rPropVal.Name == "RadiusRangeMinimum" )
507 {
508 if ( rPropVal.Value >>= rDestinationHandle.aRadiusRangeMinimum )
509 rDestinationHandle.nFlags |= HandleFlags::RADIUS_RANGE_MINIMUM;
510 }
511 else if ( rPropVal.Name == "RadiusRangeMaximum" )
512 {
513 if ( rPropVal.Value >>= rDestinationHandle.aRadiusRangeMaximum )
514 rDestinationHandle.nFlags |= HandleFlags::RADIUS_RANGE_MAXIMUM;
515 }
516 else if ( rPropVal.Name == "RangeXMinimum" )
517 {
518 if ( rPropVal.Value >>= rDestinationHandle.aXRangeMinimum )
519 rDestinationHandle.nFlags |= HandleFlags::RANGE_X_MINIMUM;
520 }
521 else if ( rPropVal.Name == "RangeXMaximum" )
522 {
523 if ( rPropVal.Value >>= rDestinationHandle.aXRangeMaximum )
524 rDestinationHandle.nFlags |= HandleFlags::RANGE_X_MAXIMUM;
525 }
526 else if ( rPropVal.Name == "RangeYMinimum" )
527 {
528 if ( rPropVal.Value >>= rDestinationHandle.aYRangeMinimum )
529 rDestinationHandle.nFlags |= HandleFlags::RANGE_Y_MINIMUM;
530 }
531 else if ( rPropVal.Name == "RangeYMaximum" )
532 {
533 if ( rPropVal.Value >>= rDestinationHandle.aYRangeMaximum )
534 rDestinationHandle.nFlags |= HandleFlags::RANGE_Y_MAXIMUM;
535 }
536 }
537 }
538 return bRetValue;
539}
540
542{
543 // AdjustmentValues
544 static constexpr OUStringLiteral sAdjustmentValues( u"AdjustmentValues" );
545 const Any* pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sAdjustmentValues );
546 if ( pAny )
547 *pAny >>= seqAdjustmentValues;
548
549
550 // Coordsize
551 static constexpr OUStringLiteral sViewBox( u"ViewBox" );
552 const Any* pViewBox = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sViewBox );
553 css::awt::Rectangle aViewBox;
554 if ( pViewBox && (*pViewBox >>= aViewBox ) )
555 {
556 nCoordLeft = aViewBox.X;
557 nCoordTop = aViewBox.Y;
558 nCoordWidthG = std::abs( aViewBox.Width );
559 nCoordHeightG = std::abs( aViewBox.Height);
560 }
561 static constexpr OUStringLiteral sPath( u"Path" );
562 static constexpr OUStringLiteral sCoordinates( u"Coordinates" );
563 static constexpr OUStringLiteral sGluePoints( u"GluePoints" );
564 static constexpr OUStringLiteral sSegments( u"Segments" );
565 static constexpr OUStringLiteral sSubViewSize( u"SubViewSize" );
566 static constexpr OUStringLiteral sStretchX( u"StretchX" );
567 static constexpr OUStringLiteral sStretchY( u"StretchY" );
568 static constexpr OUStringLiteral sTextFrames( u"TextFrames" );
569 static constexpr OUStringLiteral sEquations( u"Equations" );
570 static constexpr OUStringLiteral sHandles( u"Handles" );
571
572
573 // Path/Coordinates
574 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sCoordinates );
575 if ( pAny )
576 *pAny >>= seqCoordinates;
577
578
579 // Path/GluePoints
580 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sGluePoints );
581 if ( pAny )
582 *pAny >>= seqGluePoints;
583
584
585 // Path/Segments
586 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sSegments );
587 if ( pAny )
588 *pAny >>= seqSegments;
589
590
591 // Path/SubViewSize
592 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sSubViewSize );
593 if ( pAny )
594 *pAny >>= seqSubViewSize;
595
596
597 // Path/StretchX
598 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sStretchX );
599 if ( pAny )
600 {
601 sal_Int32 nStretchX = 0;
602 if ( *pAny >>= nStretchX )
603 nXRef = nStretchX;
604 }
605
606
607 // Path/StretchY
608 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sStretchY );
609 if ( pAny )
610 {
611 sal_Int32 nStretchY = 0;
612 if ( *pAny >>= nStretchY )
613 nYRef = nStretchY;
614 }
615
616
617 // Path/TextFrames
618 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sTextFrames );
619 if ( pAny )
620 *pAny >>= seqTextFrames;
621
622
623 // Equations
624 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sEquations );
625 if ( pAny )
626 *pAny >>= seqEquations;
627
628
629 // Handles
630 pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sHandles );
631 if ( pAny )
632 *pAny >>= seqHandles;
633}
634
636{
637}
638
640{
641 sal_Int32 nWidth = 0;
642 sal_Int32 nHeight = 0;
643
644 if ( seqSubViewSize.hasElements() && nIndex < seqSubViewSize.getLength() ) {
645 nWidth = seqSubViewSize[ nIndex ].Width;
646 nHeight = seqSubViewSize[ nIndex ].Height;
647 SAL_INFO(
648 "svx",
649 "set subpath " << nIndex << " size: " << nWidth << " x "
650 << nHeight);
651 }
652
653 if ( nWidth && nHeight ) {
654 nCoordWidth = nWidth;
655 nCoordHeight = nHeight;
656 } else {
659 }
660
661 fXScale = nCoordWidth == 0 ? 0.0 : static_cast<double>(aLogicRect.GetWidth()) / static_cast<double>(nCoordWidth);
662 fYScale = nCoordHeight == 0 ? 0.0 : static_cast<double>(aLogicRect.GetHeight()) / static_cast<double>(nCoordHeight);
663 if ( bOOXMLShape )
664 {
665 SAL_INFO(
666 "svx",
667 "ooxml shape, path width: " << nCoordWidth << " height: "
668 << nCoordHeight);
669
670 // Try to set up scale separately, if given only width or height
671 // This is possible case in OOXML when only width or height is non-zero
672 if ( nCoordWidth == 0 )
673 {
674 if ( nWidth )
675 fXScale = static_cast<double>(aLogicRect.GetWidth()) / static_cast<double>(nWidth);
676 else
677 fXScale = 1.0;
678 }
679 if ( nCoordHeight == 0 )
680 {
681 if ( nHeight )
682 fYScale = static_cast<double>(aLogicRect.GetHeight()) / static_cast<double>(nHeight);
683 else
684 fYScale = 1.0;
685 }
686 }
687 if ( static_cast<sal_uInt32>(nXRef) != 0x80000000 && aLogicRect.GetHeight() )
688 {
689 fXRatio = static_cast<double>(aLogicRect.GetWidth()) / static_cast<double>(aLogicRect.GetHeight());
690 if ( fXRatio > 1 )
691 fXScale /= fXRatio;
692 else
693 fXRatio = 1.0;
694 }
695 else
696 fXRatio = 1.0;
697 if ( static_cast<sal_uInt32>(nYRef) != 0x80000000 && aLogicRect.GetWidth() )
698 {
699 fYRatio = static_cast<double>(aLogicRect.GetHeight()) / static_cast<double>(aLogicRect.GetWidth());
700 if ( fYRatio > 1 )
701 fYScale /= fYRatio;
702 else
703 fYRatio = 1.0;
704 }
705 else
706 fYRatio = 1.0;
707}
708
710: SfxItemSet ( rSdrObjCustomShape.GetMergedItemSet() ),
711 mrSdrObjCustomShape ( rSdrObjCustomShape ),
712 eSpType ( mso_sptNil ),
713 nCoordLeft ( 0 ),
714 nCoordTop ( 0 ),
715 nCoordWidthG ( 21600 ),
716 nCoordHeightG ( 21600 ),
717 bOOXMLShape ( false ),
718 nXRef ( 0x80000000 ),
719 nYRef ( 0x80000000 ),
720 nColorData ( 0 ),
721 bFilled ( rSdrObjCustomShape.GetMergedItem( XATTR_FILLSTYLE ).GetValue() != drawing::FillStyle_NONE ),
722 bStroked ( rSdrObjCustomShape.GetMergedItem( XATTR_LINESTYLE ).GetValue() != drawing::LineStyle_NONE ),
723 bFlipH ( false ),
724 bFlipV ( false )
725{
726 // bTextFlow needs to be set before clearing the TextDirection Item
727
728 ClearItem( SDRATTR_TEXTDIRECTION ); //SJ: vertical writing is not required, by removing this item no outliner is created
729
730 // #i105323# For 2D AutoShapes, the shadow attribute does not need to be applied to any
731 // of the constructed helper SdrObjects. This would lead to problems since the shadow
732 // of one helper object would fall on one helper object behind it (e.g. with the
733 // eyes of the smiley shape). This is not wanted; instead a single shadow 'behind'
734 // the AutoShape visualisation is wanted. This is done with primitive functionality
735 // now in SdrCustomShapePrimitive2D::create2DDecomposition, but only for 2D objects
736 // (see there and in EnhancedCustomShape3d::Create3DObject to read more).
737 // This exception may be removed later when AutoShapes will create primitives directly.
738 // So, currently remove the ShadowAttribute from the ItemSet to not apply it to any
739 // 2D helper shape.
741
744 aP.AdjustX( -(aS.Width() / 2) );
745 aP.AdjustY( -(aS.Height() / 2) );
746 aLogicRect = tools::Rectangle( aP, aS );
747
748 OUString sShapeType;
750 static constexpr OUStringLiteral sType = u"Type";
751 const Any* pAny = rGeometryItem.GetPropertyValueByName( sType );
752 if ( pAny ) {
753 *pAny >>= sShapeType;
754 bOOXMLShape = sShapeType.startsWith("ooxml-");
755 SAL_INFO("svx", "shape type: " << sShapeType << " " << bOOXMLShape);
756 }
758
759 static constexpr OUStringLiteral sMirroredX = u"MirroredX";
760 static constexpr OUStringLiteral sMirroredY = u"MirroredY";
761 pAny = rGeometryItem.GetPropertyValueByName( sMirroredX );
762 if ( pAny )
763 *pAny >>= bFlipH;
764 pAny = rGeometryItem.GetPropertyValueByName( sMirroredY );
765 if ( pAny )
766 *pAny >>= bFlipV;
767
768 nRotateAngle = Degree100(static_cast<sal_Int32>(mrSdrObjCustomShape.GetObjectRotation() * 100.0));
769
770 /*const sal_Int32* pDefData =*/ ApplyShapeAttributes( rGeometryItem );
771 SetPathSize();
772
773 switch( eSpType )
774 {
775 case mso_sptCan : nColorData = 0x20400000; break;
776 case mso_sptCube : nColorData = 0x302e0000; break;
777 case mso_sptActionButtonBlank : nColorData = 0x502ce400; break;
778 case mso_sptActionButtonHome : nColorData = 0x702ce4ce; break;
779 case mso_sptActionButtonHelp : nColorData = 0x602ce4c0; break;
780 case mso_sptActionButtonInformation : nColorData = 0x702ce4c5; break;
781 case mso_sptActionButtonBackPrevious : nColorData = 0x602ce4c0; break;
782 case mso_sptActionButtonForwardNext : nColorData = 0x602ce4c0; break;
783 case mso_sptActionButtonBeginning : nColorData = 0x602ce4c0; break;
784 case mso_sptActionButtonEnd : nColorData = 0x602ce4c0; break;
785 case mso_sptActionButtonReturn : nColorData = 0x602ce4c0; break;
786 case mso_sptActionButtonDocument : nColorData = 0x702ce4ec; break;
787 case mso_sptActionButtonSound : nColorData = 0x602ce4c0; break;
788 case mso_sptActionButtonMovie : nColorData = 0x602ce4c0; break;
789 case mso_sptBevel : nColorData = 0x502ce400; break;
790 case mso_sptFoldedCorner : nColorData = 0x20e00000; break;
791 case mso_sptSmileyFace : nColorData = 0x20e00000; break;
792 case mso_sptNil :
793 {
794 // Because calculation method has changed in #i102797 original color encoding for
795 // Octagon Bevel and Diamond Bevel can no longer be used. We keep the color coding
796 // only for self-created shapes, as authors may have already considered the change.
797 // We use ColorData compatible to OOXML.
798 if (sShapeType == "col-60da8460") // Octagon Bevel
799 {
800 nColorData = 0x60ecc240;
801 }
802 else if (sShapeType == "col-502ad400") // Diamond Bevel
803 {
804 nColorData = 0x502ce400;
805 }
806 else if (sShapeType.getLength() > 4 && sShapeType.match( "col-" ))
807 {
808 nColorData = o3tl::toUInt32(sShapeType.subView( 4 ), 16);
809 }
810 }
811 break;
815 case mso_sptCurvedDownArrow : nColorData = 0x20d00000; break;
816 case mso_sptRibbon2 : nColorData = 0x30ee0000; break;
817 case mso_sptRibbon : nColorData = 0x30ee0000; break;
818
819 case mso_sptEllipseRibbon2 : nColorData = 0x30ee0000; break;
820 case mso_sptEllipseRibbon : nColorData = 0x30ee0000; break;
821
822 case mso_sptVerticalScroll : nColorData = 0x30ee0000; break;
823 case mso_sptHorizontalScroll : nColorData = 0x30ee0000; break;
824 default:
825 break;
826 }
827
828 sal_Int32 nLength = seqEquations.getLength();
829
830 if ( !nLength )
831 return;
832
833 vNodesSharedPtr.resize( nLength );
834 vEquationResults.resize( nLength );
835 for ( sal_Int32 i = 0; i < nLength; i++ )
836 {
837 vEquationResults[ i ].bReady = false;
838 try
839 {
840 vNodesSharedPtr[ i ] = EnhancedCustomShape::FunctionParser::parseFunction( seqEquations[ i ], *this );
841 }
843 {
844 SAL_INFO(
845 "svx",
846 "error: equation number: " << i << ", parser failed ("
847 << seqEquations[i] << ")");
848 }
849 }
850}
851
853
855{
856 double fRet = 0.0;
857 switch( eFunc )
858 {
859 case ExpressionFunct::EnumPi : fRet = M_PI; break;
860 case ExpressionFunct::EnumLeft : fRet = static_cast<double>(nCoordLeft); break;
861 case ExpressionFunct::EnumTop : fRet = static_cast<double>(nCoordTop); break;
862 case ExpressionFunct::EnumRight : fRet = (static_cast<double>(nCoordLeft) + static_cast<double>(nCoordWidth)) * fXRatio; break;
863 case ExpressionFunct::EnumBottom : fRet = (static_cast<double>(nCoordTop) + static_cast<double>(nCoordHeight)) * fYRatio; break;
864 case ExpressionFunct::EnumXStretch : fRet = nXRef; break;
865 case ExpressionFunct::EnumYStretch : fRet = nYRef; break;
866 case ExpressionFunct::EnumHasStroke : fRet = bStroked ? 1.0 : 0.0; break;
867 case ExpressionFunct::EnumHasFill : fRet = bFilled ? 1.0 : 0.0; break;
868 case ExpressionFunct::EnumWidth : fRet = nCoordWidth; break;
869 case ExpressionFunct::EnumHeight : fRet = nCoordHeight; break;
870 case ExpressionFunct::EnumLogWidth : fRet = aLogicRect.GetWidth(); break;
871 case ExpressionFunct::EnumLogHeight : fRet = aLogicRect.GetHeight(); break;
872 default: break;
873 }
874 return fRet;
875}
876double EnhancedCustomShape2d::GetAdjustValueAsDouble( const sal_Int32 nIndex ) const
877{
878 double fNumber = 0.0;
879 if ( nIndex < seqAdjustmentValues.getLength() )
880 {
881 if ( seqAdjustmentValues[ nIndex ].Value.getValueTypeClass() == TypeClass_DOUBLE )
882 seqAdjustmentValues[ nIndex ].Value >>= fNumber;
883 else
884 {
885 sal_Int32 nNumber = 0;
886 seqAdjustmentValues[ nIndex ].Value >>= nNumber;
887 fNumber = static_cast<double>(nNumber);
888 }
889 }
890 return fNumber;
891}
892double EnhancedCustomShape2d::GetEquationValueAsDouble( const sal_Int32 nIndex ) const
893{
894 double fNumber = 0.0;
895 static sal_uInt32 nLevel = 0;
896 if ( nIndex < static_cast<sal_Int32>(vNodesSharedPtr.size()) )
897 {
898 if ( vNodesSharedPtr[ nIndex ] ) {
899 nLevel ++;
900 try
901 {
902 if ( vEquationResults[ nIndex ].bReady )
903 fNumber = vEquationResults[ nIndex ].fValue;
904 else {
905 // cast to non const, so that we can optimize by caching
906 // equation results, without changing all the const in the stack
907 struct EquationResult &aResult = const_cast<EnhancedCustomShape2d*>(this)->vEquationResults[ nIndex ];
908
909 fNumber = aResult.fValue = (*vNodesSharedPtr[ nIndex ])();
910 aResult.bReady = true;
911
912 SAL_INFO("svx", "equation " << nLevel << " (level: " << seqEquations[nIndex] << "): "
913 << fNumber << " --> " << 180.0*fNumber/10800000.0);
914 }
915 if ( !std::isfinite( fNumber ) )
916 fNumber = 0.0;
917 }
918 catch ( ... )
919 {
920 SAL_WARN("svx", "EnhancedCustomShape2d::GetEquationValueAsDouble failed");
921 }
922 nLevel --;
923 }
924 SAL_INFO(
925 "svx",
926 "?" << nIndex << " --> " << fNumber << " (angle: "
927 << 180.0*fNumber/10800000.0 << ")");
928 }
929
930 return fNumber;
931}
932
933bool EnhancedCustomShape2d::SetAdjustValueAsDouble( const double& rValue, const sal_Int32 nIndex )
934{
935 bool bRetValue = false;
936 if ( nIndex < seqAdjustmentValues.getLength() )
937 {
938 // updating our local adjustment sequence
939 auto pseqAdjustmentValues = seqAdjustmentValues.getArray();
940 pseqAdjustmentValues[ nIndex ].Value <<= rValue;
941 pseqAdjustmentValues[ nIndex ].State = css::beans::PropertyState_DIRECT_VALUE;
942 bRetValue = true;
943 }
944 return bRetValue;
945}
946
947basegfx::B2DPoint EnhancedCustomShape2d::GetPointAsB2DPoint( const css::drawing::EnhancedCustomShapeParameterPair& rPair,
948 const bool bScale, const bool bReplaceGeoSize ) const
949{
950 double fValX, fValY;
951 // width
952 GetParameter(fValX, rPair.First, bReplaceGeoSize, false);
953 fValX -= nCoordLeft;
954 if (bScale)
955 {
956 fValX *= fXScale;
957 }
958 // height
959 GetParameter(fValY, rPair.Second, false, bReplaceGeoSize);
960 fValY -= nCoordTop;
961 if (bScale)
962 {
963 fValY *= fYScale;
964 }
965 return basegfx::B2DPoint(fValX,fValY);
966}
967
968Point EnhancedCustomShape2d::GetPoint( const css::drawing::EnhancedCustomShapeParameterPair& rPair,
969 const bool bScale, const bool bReplaceGeoSize ) const
970{
971 basegfx::B2DPoint aPoint(GetPointAsB2DPoint(rPair, bScale, bReplaceGeoSize));
972 return Point(static_cast<tools::Long>(aPoint.getX()), static_cast<tools::Long>(aPoint.getY()));
973}
974
975void EnhancedCustomShape2d::GetParameter( double& rRetValue, const EnhancedCustomShapeParameter& rParameter,
976 const bool bReplaceGeoWidth, const bool bReplaceGeoHeight ) const
977{
978 rRetValue = 0.0;
979 switch ( rParameter.Type )
980 {
981 case EnhancedCustomShapeParameterType::ADJUSTMENT :
982 {
983 sal_Int32 nAdjustmentIndex = 0;
984 if ( rParameter.Value >>= nAdjustmentIndex )
985 {
986 rRetValue = GetAdjustValueAsDouble( nAdjustmentIndex );
987 }
988 }
989 break;
990 case EnhancedCustomShapeParameterType::EQUATION :
991 {
992 sal_Int32 nEquationIndex = 0;
993 if ( rParameter.Value >>= nEquationIndex )
994 {
995 rRetValue = GetEquationValueAsDouble( nEquationIndex );
996 }
997 }
998 break;
999 case EnhancedCustomShapeParameterType::NORMAL :
1000 {
1001 if ( rParameter.Value.getValueTypeClass() == TypeClass_DOUBLE )
1002 {
1003 double fValue(0.0);
1004 if ( rParameter.Value >>= fValue )
1005 {
1006 rRetValue = fValue;
1007 }
1008 }
1009 else
1010 {
1011 sal_Int32 nValue = 0;
1012 if ( rParameter.Value >>= nValue )
1013 {
1014 rRetValue = nValue;
1015 if ( bReplaceGeoWidth && ( nValue == nCoordWidth ) )
1016 rRetValue *= fXRatio;
1017 else if ( bReplaceGeoHeight && ( nValue == nCoordHeight ) )
1018 rRetValue *= fYRatio;
1019 }
1020 }
1021 }
1022 break;
1023 case EnhancedCustomShapeParameterType::LEFT :
1024 {
1025 rRetValue = 0.0;
1026 }
1027 break;
1028 case EnhancedCustomShapeParameterType::TOP :
1029 {
1030 rRetValue = 0.0;
1031 }
1032 break;
1033 case EnhancedCustomShapeParameterType::RIGHT :
1034 {
1035 rRetValue = nCoordWidth;
1036 }
1037 break;
1038 case EnhancedCustomShapeParameterType::BOTTOM :
1039 {
1040 rRetValue = nCoordHeight;
1041 }
1042 break;
1043 }
1044}
1045
1046// nLumDat 28-31 = number of luminance entries in nLumDat
1047// nLumDat 27-24 = nLumDatEntry 0
1048// nLumDat 23-20 = nLumDatEntry 1 ...
1049// each 4bit entry is to be interpreted as a 10 percent signed luminance changing
1050sal_Int32 EnhancedCustomShape2d::GetLuminanceChange( sal_uInt32 nIndex ) const
1051{
1052 const sal_uInt32 nCount = nColorData >> 28;
1053 if ( !nCount )
1054 return 0;
1055
1056 if ( nIndex >= nCount )
1057 nIndex = nCount - 1;
1058
1059 const sal_Int32 nLumDat = nColorData << ( ( 1 + nIndex ) << 2 );
1060 return ( nLumDat >> 28 ) * 10;
1061}
1062
1063Color EnhancedCustomShape2d::GetColorData( const Color& rFillColor, sal_uInt32 nIndex, double dBrightness ) const
1064{
1065 if ( bOOXMLShape || ( mso_sptMin == eSpType /* ODF "non-primitive" */ ) )
1066 { //do LibreOffice way, using dBrightness
1067 if ( dBrightness == 0.0)
1068 {
1069 return rFillColor;
1070 }
1071 else
1072 {
1073 if (dBrightness >=0.0)
1074 { //lighten, blending with white
1075 return Color( static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetRed() * (1.0-dBrightness) + dBrightness * 255.0, 0.0, 255.0) )),
1076 static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetGreen() * (1.0-dBrightness) + dBrightness * 255.0, 0.0, 255.0) )),
1077 static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetBlue() * (1.0-dBrightness) + dBrightness * 255.0, 0.0, 255.0) )) );
1078 }
1079 else
1080 { //darken (indicated by negative sign), blending with black
1081 return Color( static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetRed() * (1.0+dBrightness), 0.0, 255.0) )),
1082 static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetGreen() * (1.0+dBrightness), 0.0, 255.0) )),
1083 static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetBlue() * (1.0+dBrightness), 0.0, 255.0) )) );
1084 }
1085 }
1086 }
1087 else
1088 { //do OpenOffice way, using nColorData
1089 const sal_Int32 nLuminance = GetLuminanceChange(nIndex);
1090 if( !nLuminance )
1091 return rFillColor;
1092
1093 basegfx::BColor aHSVColor=
1095 basegfx::BColor(rFillColor.GetRed()/255.0,
1096 rFillColor.GetGreen()/255.0,
1097 rFillColor.GetBlue()/255.0));
1098
1099 if( nLuminance > 0 )
1100 {
1101 aHSVColor.setGreen(
1102 aHSVColor.getGreen() * (1.0-nLuminance/100.0));
1103 aHSVColor.setBlue(
1104 nLuminance/100.0 +
1105 (1.0-nLuminance/100.0)*aHSVColor.getBlue());
1106 }
1107 else if( nLuminance < 0 )
1108 {
1109 aHSVColor.setBlue(
1110 (1.0+nLuminance/100.0)*aHSVColor.getBlue());
1111 }
1112
1113 aHSVColor = basegfx::utils::hsv2rgb(aHSVColor);
1114 return Color( static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(aHSVColor.getRed(),0.0,1.0) * 255.0 + 0.5 )),
1115 static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(aHSVColor.getGreen(),0.0,1.0) * 255.0 + 0.5 )),
1116 static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(aHSVColor.getBlue(),0.0,1.0) * 255.0 + 0.5 )) );
1117 }
1118}
1119
1121{
1122 if ( !seqTextFrames.hasElements() )
1123 return aLogicRect;
1124 sal_Int32 nIndex = 0;
1125 Point aTopLeft( GetPoint( seqTextFrames[ nIndex ].TopLeft, !bOOXMLShape, true ) );
1126 Point aBottomRight( GetPoint( seqTextFrames[ nIndex ].BottomRight, !bOOXMLShape, true ) );
1127 tools::Rectangle aRect( aTopLeft, aBottomRight );
1128 if ( bFlipH )
1129 {
1130 aRect.SetLeft(aLogicRect.GetWidth() - 1 - aBottomRight.X());
1131 aRect.SetRight( aLogicRect.GetWidth() - 1 - aTopLeft.X());
1132 }
1133 if ( bFlipV )
1134 {
1135 aRect.SetTop(aLogicRect.GetHeight() - 1 - aBottomRight.Y());
1136 aRect.SetBottom(aLogicRect.GetHeight() - 1 - aTopLeft.Y());
1137 }
1138 SAL_INFO("svx", aRect.GetWidth() << " x " << aRect.GetHeight());
1139 if( aRect.GetWidth() <= 1 || aRect.GetHeight() <= 1 )
1140 return aLogicRect;
1141 aRect.Move( aLogicRect.Left(), aLogicRect.Top() );
1142 aRect.Normalize();
1143 return aRect;
1144}
1145
1147{
1148 return seqHandles.getLength();
1149}
1150
1151bool EnhancedCustomShape2d::GetHandlePosition( const sal_uInt32 nIndex, Point& rReturnPosition ) const
1152{
1153 bool bRetValue = false;
1154 if ( nIndex < GetHdlCount() )
1155 {
1156 Handle aHandle;
1158 {
1159 if ( aHandle.nFlags & HandleFlags::POLAR )
1160 {
1161 Point aReferencePoint( GetPoint( aHandle.aPolar ) );
1162
1163 double fAngle;
1164 double fRadius;
1165 GetParameter( fRadius, aHandle.aPosition.First, false, false );
1166 GetParameter( fAngle, aHandle.aPosition.Second, false, false );
1167
1168 double a = basegfx::deg2rad(360.0 - fAngle);
1169 double dx = fRadius * fXScale;
1170 double fX = dx * cos( a );
1171 double fY =-dx * sin( a );
1172 rReturnPosition =
1173 Point(
1174 FRound( fX + aReferencePoint.X() ),
1175 basegfx::fTools::equalZero(fXScale) ? aReferencePoint.Y() :
1176 FRound( ( fY * fYScale ) / fXScale + aReferencePoint.Y() ) );
1177 }
1178 else
1179 {
1180 if ( aHandle.nFlags & HandleFlags::SWITCHED )
1181 {
1183 {
1184 css::drawing::EnhancedCustomShapeParameter aFirst = aHandle.aPosition.First;
1185 css::drawing::EnhancedCustomShapeParameter aSecond = aHandle.aPosition.Second;
1186 aHandle.aPosition.First = aSecond;
1187 aHandle.aPosition.Second = aFirst;
1188 }
1189 }
1190 if (bOOXMLShape)
1191 rReturnPosition = GetPoint(aHandle.aPosition, false /*bScale*/);
1192 else
1193 rReturnPosition = GetPoint(aHandle.aPosition, true /*bScale*/);
1194 }
1195 const GeoStat aGeoStat(mrSdrObjCustomShape.GetGeoStat());
1196 if ( aGeoStat.nShearAngle )
1197 {
1198 double nTan = aGeoStat.mfTanShearAngle;
1199 if (bFlipV != bFlipH)
1200 nTan = -nTan;
1201 ShearPoint( rReturnPosition, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), nTan );
1202 }
1203 if ( nRotateAngle )
1204 {
1205 double a = toRadians(nRotateAngle);
1206 RotatePoint( rReturnPosition, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), sin( a ), cos( a ) );
1207 }
1208 if ( bFlipH )
1209 rReturnPosition.setX( aLogicRect.GetWidth() - rReturnPosition.X() );
1210 if ( bFlipV )
1211 rReturnPosition.setY( aLogicRect.GetHeight() - rReturnPosition.Y() );
1212 rReturnPosition.Move( aLogicRect.Left(), aLogicRect.Top() );
1213 bRetValue = true;
1214 }
1215 }
1216 return bRetValue;
1217}
1218
1219static double lcl_getXAdjustmentValue(std::u16string_view rShapeType, const sal_uInt32 nHandleIndex,
1220 const double fX, const double fW, const double fH)
1221{
1222 // degenerated shapes are not worth to calculate special case for each shape type
1223 if (fW <= 0.0 || fH <= 0.0)
1224 return 50000;
1225
1226 // pattern (w - x) / ss * 100000 or (r - x) / ss * 100000
1227 if ((rShapeType == u"ooxml-bentArrow" && nHandleIndex == 2) || (rShapeType == u"ooxml-chevron")
1228 || (rShapeType == u"ooxml-curvedRightArrow") || (rShapeType == u"ooxml-foldedCorner")
1229 || (rShapeType == u"ooxml-homePlate") || (rShapeType == u"ooxml-notchedRightArrow")
1230 || (rShapeType == u"ooxml-nonIsoscelesTrapezoid" && nHandleIndex == 1)
1231 || (rShapeType == u"ooxml-rightArrow")
1232 || (rShapeType == u"ooxml-rightArrowCallout" && nHandleIndex == 2)
1233 || (rShapeType == u"ooxml-round1Rect")
1234 || (rShapeType == u"ooxml-round2DiagRect" && nHandleIndex == 1)
1235 || (rShapeType == u"ooxml-round2SameRect" && nHandleIndex == 0)
1236 || (rShapeType == u"ooxml-snip1Rect")
1237 || (rShapeType == u"ooxml-snip2DiagRect" && nHandleIndex == 1)
1238 || (rShapeType == u"ooxml-snip2SameRect" && nHandleIndex == 0)
1239 || (rShapeType == u"ooxml-snipRoundRect" && nHandleIndex == 1)
1240 || (rShapeType == u"ooxml-swooshArrow") || (rShapeType == u"ooxml-stripedRightArrow"))
1241 return (fW - fX) / std::min(fW, fH) * 100000.0;
1242
1243 // pattern x / ss * 100000 or (x - l) / ss * 100000
1244 if ((rShapeType == u"ooxml-bentArrow" && nHandleIndex == 0)
1245 || (rShapeType == u"ooxml-bentArrow" && nHandleIndex == 3)
1246 || (rShapeType == u"ooxml-corner")
1247 || (rShapeType == u"ooxml-curvedDownArrow") || (rShapeType == u"ooxml-curvedLeftArrow")
1248 || (rShapeType == u"ooxml-curvedUpArrow") || (rShapeType == u"ooxml-leftArrow")
1249 || (rShapeType == u"ooxml-leftArrowCallout" && nHandleIndex == 2)
1250 || (rShapeType == u"ooxml-leftRightArrow")
1251 || (rShapeType == u"ooxml-leftRightArrowCallout" && nHandleIndex == 2)
1252 || (rShapeType == u"ooxml-leftRightRibbon")
1253 || (rShapeType == u"ooxml-nonIsoscelesTrapezoid" && nHandleIndex == 0)
1254 || (rShapeType == u"ooxml-parallelogram")
1255 || (rShapeType == u"ooxml-round2DiagRect" && nHandleIndex == 0)
1256 || (rShapeType == u"ooxml-round2SameRect" && nHandleIndex == 1)
1257 || (rShapeType == u"ooxml-roundRect")
1258 || (rShapeType == u"ooxml-snip2DiagRect" && nHandleIndex == 0)
1259 || (rShapeType == u"ooxml-snip2SameRect" && nHandleIndex == 1)
1260 || (rShapeType == u"ooxml-snipRoundRect" && nHandleIndex == 0)
1261 || (rShapeType == u"ooxml-uturnArrow" && nHandleIndex == 0)
1262 || (rShapeType == u"ooxml-uturnArrow" && nHandleIndex == 3))
1263 return fX / std::min(fW, fH) * 100000.0;
1264
1265 // pattern (hc - x) / ss * 200000
1266 if ((rShapeType == u"ooxml-downArrowCallout" && nHandleIndex == 0)
1267 || (rShapeType == u"ooxml-leftRightUpArrow" && nHandleIndex == 0)
1268 || (rShapeType == u"ooxml-quadArrow" && nHandleIndex == 0)
1269 || (rShapeType == u"ooxml-quadArrowCallout" && nHandleIndex == 0)
1270 || (rShapeType == u"ooxml-upArrowCallout" && nHandleIndex == 0)
1271 || (rShapeType == u"ooxml-upDownArrowCallout" && nHandleIndex == 0))
1272 return (fW / 2.0 - fX) / std::min(fW, fH) * 200000.0;
1273
1274 // pattern (hc - x) / ss * 100000
1275 if ((rShapeType == u"ooxml-downArrowCallout" && nHandleIndex == 1)
1276 || (rShapeType == u"ooxml-leftRightUpArrow" && nHandleIndex == 1)
1277 || (rShapeType == u"ooxml-quadArrow" && nHandleIndex == 1)
1278 || (rShapeType == u"ooxml-quadArrowCallout" && nHandleIndex == 1)
1279 || (rShapeType == u"ooxml-upArrowCallout" && nHandleIndex == 1)
1280 || (rShapeType == u"ooxml-upDownArrowCallout" && nHandleIndex == 1))
1281 return (fW / 2.0 - fX) / std::min(fW, fH) * 100000.0;
1282
1283 // pattern (w - x) / ss * 50000 or (r - x) / ss * 50000
1284 if ((rShapeType == u"ooxml-bentUpArrow") || (rShapeType == u"ooxml-leftUpArrow")
1285 || (rShapeType == u"ooxml-uturnArrow" && nHandleIndex == 1))
1286 return (fW - fX) / std::min(fW, fH) * 50000.0;
1287
1288 // pattern x / ss * 200000
1289 if (rShapeType == u"ooxml-nonIsoscelesTrapezoid" && nHandleIndex == 0)
1290 return fX / std::min(fW, fH) * 200000.0;
1291
1292 // pattern (hc - x) / w * 200000
1293 if ((rShapeType == u"ooxml-downArrow" && nHandleIndex == 0)
1294 || (rShapeType == u"ooxml-ellipseRibbon") || (rShapeType == u"ooxml-ellipseRibbon2")
1295 || (rShapeType == u"ooxml-leftRightArrowCallout" && nHandleIndex == 3)
1296 || (rShapeType == u"ooxml-ribbon") || (rShapeType == u"ooxml-ribbon2")
1297 || (rShapeType == u"ooxml-upArrow" && nHandleIndex == 0)
1298 || (rShapeType == u"ooxml-upDownArrow" && nHandleIndex == 0))
1299 return (fW / 2.0 - fX) / fW * 200000.0;
1300
1301 // pattern (x - hc) / w * 100000
1302 if ((rShapeType == u"ooxml-cloudCallout") || (rShapeType == u"ooxml-doubleWave")
1303 || (rShapeType == u"ooxml-wave") || (rShapeType == u"ooxml-wedgeEllipseCallout")
1304 || (rShapeType == u"ooxml-wedgeRectCallout")
1305 || (rShapeType == u"ooxml-wedgeRoundRectCallout"))
1306 return (fX - fW / 2.0) / fW * 100000.0;
1307
1308 // pattern (x - hc) / w * 200000
1309 if (rShapeType == u"ooxml-teardrop")
1310 return (fX - fW / 2.0) / fW * 200000.0;
1311
1312 // pattern (w - x) / w * 100000 or (r - x) / w * 100000
1313 if (rShapeType == u"ooxml-leftArrowCallout" && nHandleIndex == 3)
1314 return (fW - fX) / fW * 100000.0;
1315
1316 // pattern (hc - x) / h * 100000
1317 if (rShapeType == u"ooxml-mathDivide")
1318 return (fW / 2.0 - fX) / fH * 100000.0;
1319
1320 // pattern x / w * 100000, simple scaling
1321 if (o3tl::starts_with(rShapeType, u"ooxml-"))
1322 return fX / fW * 100000.0;
1323
1324 return fX; // method is unknown
1325}
1326
1327static double lcl_getYAdjustmentValue(std::u16string_view rShapeType, const sal_uInt32 nHandleIndex,
1328 const double fY, const double fW, const double fH)
1329{
1330 // degenerated shapes are not worth to calculate a special case for each shape type
1331 if (fW <= 0.0 || fH <= 0.0)
1332 return 50000;
1333
1334 // pattern (vc - y) / ss * 100000
1335 if ((rShapeType == u"ooxml-leftArrowCallout" && nHandleIndex == 1)
1336 || (rShapeType == u"ooxml-leftRightArrowCallout" && nHandleIndex == 1)
1337 || (rShapeType == u"ooxml-rightArrowCallout" && nHandleIndex == 1))
1338 return (fH / 2.0 - fY) / std::min(fW, fH) * 100000.0;
1339
1340 // pattern (vc - y) / ss * 200000
1341 if ((rShapeType == u"ooxml-curvedLeftArrow") || (rShapeType == u"ooxml-curvedRightArrow")
1342 || (rShapeType == u"ooxml-leftArrowCallout" && nHandleIndex == 0)
1343 || (rShapeType == u"ooxml-leftRightArrowCallout" && nHandleIndex == 0)
1344 || (rShapeType == u"ooxml-mathPlus")
1345 || (rShapeType == u"ooxml-rightArrowCallout" && nHandleIndex == 0))
1346 return (fH / 2.0 - fY) / std::min(fW, fH) * 200000.0;
1347
1348 // pattern (h - y) / ss * 100000 or (b - y) / ss * 100000
1349 if ((rShapeType == u"ooxml-bentUpArrow" && nHandleIndex == 0) || (rShapeType == u"ooxml-corner")
1350 || (rShapeType == u"ooxml-curvedDownArrow") || (rShapeType == u"ooxml-downArrow")
1351 || (rShapeType == u"ooxml-downArrowCallout" && nHandleIndex == 2)
1352 || (rShapeType == u"ooxml-uturnArrow" && nHandleIndex == 2))
1353 return (fH - fY) / std::min(fW, fH) * 100000.0;
1354
1355 // pattern (h - y) / ss * 200000 or (b - y) / ss * 200000
1356 if (rShapeType == u"ooxml-leftUpArrow" && nHandleIndex == 0) // - adj2 * 2 outside
1357 return (fH - fY) / std::min(fW, fH) * 200000.0;
1358
1359 // pattern y / ss * 100000 or (y - t) / ss * 100000
1360 if ((rShapeType == u"ooxml-bentUpArrow" && nHandleIndex == 2)
1361 || (rShapeType == u"ooxml-bracePair") || (rShapeType == u"ooxml-bracketPair")
1362 || (rShapeType == u"ooxml-can") || (rShapeType == u"ooxml-cube")
1363 || (rShapeType == u"ooxml-curvedUpArrow") || (rShapeType == u"ooxml-halfFrame")
1364 || (rShapeType == u"ooxml-leftBrace" && nHandleIndex == 0)
1365 || (rShapeType == u"ooxml-leftBracket") || (rShapeType == u"ooxml-leftRightUpArrow")
1366 || (rShapeType == u"ooxml-leftUpArrow" && nHandleIndex == 2)
1367 || (rShapeType == u"ooxml-mathMultiply") || (rShapeType == u"ooxml-quadArrow")
1368 || (rShapeType == u"ooxml-quadArrowCallout" && nHandleIndex == 2)
1369 || (rShapeType == u"ooxml-rightBrace" && nHandleIndex == 0)
1370 || (rShapeType == u"ooxml-rightBracket") || (rShapeType == u"ooxml-upArrow")
1371 || (rShapeType == u"ooxml-upArrowCallout" && nHandleIndex == 2)
1372 || (rShapeType == u"ooxml-upDownArrow")
1373 || (rShapeType == u"ooxml-upDownArrowCallout" && nHandleIndex == 2)
1374 || (rShapeType == u"ooxml-verticalScroll"))
1375 return fY / std::min(fW, fH) * 100000.0;
1376
1377 // pattern y / ss * 50000
1378 if (rShapeType == u"ooxml-bentArrow")
1379 return fY / std::min(fW, fH) * 50000.0;
1380
1381 // pattern (vc - y) / h * 100000
1382 if ((rShapeType == u"ooxml-mathDivide" && nHandleIndex == 1) // -adj1 / 2 - adj3 outside
1383 || (rShapeType == u"ooxml-mathEqual" && nHandleIndex == 0) // -adj2 / 2 outside
1384 || (rShapeType == u"ooxml-mathNotEqual" && nHandleIndex == 0) // -adj3 / 2 outside
1385 || (rShapeType == u"ooxml-star4") || (rShapeType == u"ooxml-star6")
1386 || (rShapeType == u"ooxml-star8") || (rShapeType == u"ooxml-star10")
1387 || (rShapeType == u"ooxml-star12") || (rShapeType == u"ooxml-star16")
1388 || (rShapeType == u"ooxml-star24") || (rShapeType == u"ooxml-star32"))
1389 return (fH / 2.0 - fY) / fH * 100000.0;
1390
1391 // pattern (vc - y) / h * 200000
1392 if ((rShapeType == u"ooxml-leftArrow") || (rShapeType == u"ooxml-leftRightArrow")
1393 || (rShapeType == u"ooxml-mathDivide" && nHandleIndex == 0)
1394 || (rShapeType == u"ooxml-mathEqual" && nHandleIndex == 1)
1395 || (rShapeType == u"ooxml-mathMinus") || (rShapeType == u"ooxml-notchedRightArrow")
1396 || (rShapeType == u"ooxml-mathNotEqual" && nHandleIndex == 2)
1397 || (rShapeType == u"ooxml-quadArrowCallout" && nHandleIndex == 3)
1398 || (rShapeType == u"ooxml-rightArrow") || (rShapeType == u"ooxml-stripedRightArrow")
1399 || (rShapeType == u"ooxml-upDownArrowCallout" && nHandleIndex == 3))
1400 return (fH / 2.0 - fY) / fH * 200000.0;
1401
1402 // pattern (y - vc) / h * 100000
1403 if ((rShapeType == u"ooxml-cloudCallout") || (rShapeType == u"ooxml-wedgeEllipseCallout")
1404 || (rShapeType == u"ooxml-wedgeRectCallout")
1405 || (rShapeType == u"ooxml-wedgeRoundRectCallout"))
1406 return (fY - fH / 2.0) / fH * 100000.0;
1407
1408 // pattern (h - y) / h * 100000 or (b - y) / h * 100000
1409 if ((rShapeType == u"ooxml-ellipseRibbon" && nHandleIndex == 2)
1410 || (rShapeType == u"ooxml-ellipseRibbon2" && nHandleIndex == 0)
1411 || (rShapeType == u"ooxml-ribbon2")
1412 || (rShapeType == u"ooxml-upArrowCallout" && nHandleIndex == 3))
1413 return (fH - fY) / fH * 100000.0;
1414
1415 // special pattern smiley
1416 if (rShapeType == u"ooxml-smileyFace")
1417 return (fY - fH * 16515.0 / 21600.0) / fH * 100000.0;
1418
1419 // special pattern for star with odd number of tips, because center of star not center of shape
1420 if (rShapeType == u"ooxml-star5")
1421 return (fH / 2.0 - fY * 100000.0 / 110557.0) / fH * 100000.0;
1422 if (rShapeType == u"ooxml-star7")
1423 return (fH / 2.0 - fY * 100000.0 / 105210.0) / fH * 100000.0;
1424
1425 // special pattern swooshArrow
1426 if (rShapeType == u"ooxml-swooshArrow")
1427 return (fY - std::min(fW, fH) / 8.0) / fH * 100000.0;
1428
1429 // special pattern leftRightRibbon
1430 if (rShapeType == u"ooxml-leftRightRibbon")
1431 return fY / fH * 200000 - 100000;
1432
1433 // pattern y / h * 100000, simple scaling
1434 if (o3tl::starts_with(rShapeType, u"ooxml-"))
1435 return fY / fH * 100000.0;
1436
1437 return fY; // method is unknown
1438}
1439
1440static double lcl_getAngleInOOXMLUnit(double fDY, double fDX)
1441{
1442 if (fDX != 0.0 || fDY != 0.0)
1443 {
1444 double fAngleRad(atan2(fDY, fDX));
1445 double fAngle = basegfx::rad2deg(fAngleRad);
1446 // atan2 returns angle in ]-pi; pi], OOXML preset shapes use [0;360[.
1447 if (fAngle < 0.0)
1448 fAngle += 360.0;
1449 // OOXML uses angle unit 1/60000 degree.
1450 fAngle *= 60000.0;
1451 return fAngle;
1452 }
1453 return 0.0; // no angle defined for origin in polar coordinate system
1454}
1455
1456static double lcl_getRadiusDistance(double fWR, double fHR, double fX, double fY)
1457{
1458 // Get D so, that point (fX|fY) is on the ellipse, that has width fWR-D and
1459 // height fHR-D and center in origin.
1460 // Get solution of ellipse equation (fX/(fWR-D))^2 + (fY/(fHR-D)^2 = 1 by solving
1461 // fX^2*(fHR-D)^2 + fY^2*(fWR-D)^2 - (fWR-D)^2 * (fHR-D)^2 = 0 with Newton-method.
1462 if (fX == 0.0)
1463 return std::min(fHR - fY, fWR);
1464 else if (fY == 0.0)
1465 return std::min(fWR - fX, fHR);
1466
1467 double fD = std::min(fWR, fHR) - std::hypot(fX, fY); // iteration start value
1468 sal_uInt8 nIter(0);
1469 bool bFound(false);
1470 do
1471 {
1472 ++nIter;
1473 const double fOldD(fD);
1474 const double fWRmD(fWR - fD);
1475 const double fHRmD(fHR - fD);
1476 double fNumerator
1477 = fX * fX * fHRmD * fHRmD + fY * fY * fWRmD * fWRmD - fWRmD * fWRmD * fHRmD * fHRmD;
1478 double fDenominator
1479 = 2.0 * (fHRmD * (fWRmD * fWRmD - fX * fX) + fWRmD * (fHRmD * fHRmD - fY * fY));
1480 if (fDenominator != 0.0)
1481 {
1482 fD = fD - fNumerator / fDenominator;
1483 bFound = fabs(fOldD - fD) < 1.0E-12;
1484 }
1485 else
1486 fD = fD * 0.9; // new start value
1487 } while (nIter < 50 && !bFound);
1488 return fD;
1489}
1490
1491bool EnhancedCustomShape2d::SetHandleControllerPosition( const sal_uInt32 nIndex, const css::awt::Point& rPosition )
1492{
1493 // The method name is misleading. Essentially it calculates the adjustment values from a given
1494 // handle position.
1495
1496 // For ooxml-foo shapes, the way to calculate the adjustment value from the handle position depends on
1497 // the type of the shape, therefore need 'Type'.
1498 OUString sShapeType("non-primitive"); // default for ODF
1500 const Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
1501 if (pAny)
1502 *pAny >>= sShapeType;
1503
1504 bool bRetValue = false;
1505 if ( nIndex < GetHdlCount() )
1506 {
1507 Handle aHandle;
1509 {
1510 Point aP( rPosition.X, rPosition.Y );
1511 // apply the negative object rotation to the controller position
1512
1513 aP.Move( -aLogicRect.Left(), -aLogicRect.Top() );
1514 if ( bFlipH )
1515 aP.setX( aLogicRect.GetWidth() - aP.X() );
1516 if ( bFlipV )
1517 aP.setY( aLogicRect.GetHeight() - aP.Y() );
1518 if ( nRotateAngle )
1519 {
1520 double a = -toRadians(nRotateAngle);
1521 RotatePoint( aP, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), sin( a ), cos( a ) );
1522 }
1523 const GeoStat aGeoStat(mrSdrObjCustomShape.GetGeoStat());
1524 if ( aGeoStat.nShearAngle )
1525 {
1526 double nTan = -aGeoStat.mfTanShearAngle;
1527 if (bFlipV != bFlipH)
1528 nTan = -nTan;
1529 ShearPoint( aP, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), nTan );
1530 }
1531
1532 double fPos1 = aP.X(); //( bFlipH ) ? aLogicRect.GetWidth() - aP.X() : aP.X();
1533 double fPos2 = aP.Y(); //( bFlipV ) ? aLogicRect.GetHeight() -aP.Y() : aP.Y();
1536 // revert -nCoordLeft and -nCoordTop aus GetPoint()
1537 fPos1 += nCoordLeft;
1538 fPos2 += nCoordTop;
1539
1540 // Used for scaling the adjustment values based on handle positions
1541 double fWidth;
1542 double fHeight;
1543
1544 if ( nCoordWidth || nCoordHeight )
1545 {
1546 fWidth = nCoordWidth;
1547 fHeight = nCoordHeight;
1548 }
1549 else
1550 {
1551 fWidth = aLogicRect.GetWidth();
1552 fHeight = aLogicRect.GetHeight();
1553 }
1554
1555 if ( aHandle.nFlags & HandleFlags::SWITCHED )
1556 {
1558 {
1559 double fX = fPos1;
1560 double fY = fPos2;
1561 double fTmp = fWidth;
1562 fPos1 = fY;
1563 fPos2 = fX;
1564 fHeight = fWidth;
1565 fWidth = fTmp;
1566 }
1567 }
1568
1569 sal_Int32 nFirstAdjustmentValue = -1, nSecondAdjustmentValue = -1;
1570
1571 // ODF shapes are expected to use a direct binding between position and adjustment
1572 // values. OOXML preset shapes use known formulas. These are calculated backward to
1573 // get the adjustment values. So far we do not have a general method to calculate
1574 // the adjustment values for any shape from the handle position.
1575 if ( aHandle.aPosition.First.Type == EnhancedCustomShapeParameterType::ADJUSTMENT )
1576 aHandle.aPosition.First.Value >>= nFirstAdjustmentValue;
1577 if ( aHandle.aPosition.Second.Type == EnhancedCustomShapeParameterType::ADJUSTMENT )
1578 aHandle.aPosition.Second.Value>>= nSecondAdjustmentValue;
1579
1581 { // Polar-Handle
1582
1583 if (aHandle.nFlags & HandleFlags::REFR)
1584 nFirstAdjustmentValue = aHandle.nRefR;
1585 if (aHandle.nFlags & HandleFlags::REFANGLE)
1586 nSecondAdjustmentValue = aHandle.nRefAngle;
1587
1588 double fAngle(0.0);
1589 double fRadius(0.0);
1590 // 'then' treats only shapes of type "ooxml-foo", fontwork shapes have been mapped
1591 // to MS binary import and will be treated in 'else'.
1592 if (bOOXMLShape)
1593 {
1594 // DrawingML polar handles set REFR or REFANGLE instead of POLAR
1595 // use the shape center instead.
1596 double fDX = fPos1 - fWidth / 2.0;
1597 double fDY = fPos2 - fHeight / 2.0;
1598
1599 // There exists no common pattern. 'radius' or 'angle' might have special meaning.
1600 if (sShapeType == "ooxml-blockArc" && nIndex == 1)
1601 {
1602 // usual angle, special radius
1603 fAngle = lcl_getAngleInOOXMLUnit(fDY, fDX);
1604 // The value connected to REFR is the _difference_ between the outer
1605 // ellipse given by shape width and height and the inner ellipse through
1606 // the handle position.
1607 double fRadiusDifference
1608 = lcl_getRadiusDistance(fWidth / 2.0, fHeight / 2.0, fDX, fDY);
1609 double fss(std::min(fWidth, fHeight));
1610 if (fss != 0)
1611 fRadius = fRadiusDifference * 100000.0 / fss;
1612 }
1613 else if (sShapeType == "ooxml-donut" || sShapeType == "ooxml-noSmoking")
1614 {
1615 // no angle adjustment, radius bound to x-coordinate of handle
1616 double fss(std::min(fWidth, fHeight));
1617 if (fss != 0.0)
1618 fRadius = fPos1 * 100000.0 / fss;
1619 }
1620 else if ((sShapeType == "ooxml-circularArrow"
1621 || sShapeType == "ooxml-leftRightCircularArrow"
1622 || sShapeType == "ooxml-leftCircularArrow")
1623 && nIndex == 0)
1624 {
1625 // The value adj2 is the increase compared to the angle in adj3
1626 double fHandleAngle = lcl_getAngleInOOXMLUnit(fDY, fDX);
1627 if (sShapeType == "ooxml-leftCircularArrow")
1628 fAngle = GetAdjustValueAsDouble(2) - fHandleAngle;
1629 else
1630 fAngle = fHandleAngle - GetAdjustValueAsDouble(2);
1631 if (fAngle < 0.0) // 0deg to 360deg cut
1632 fAngle += 21600000.0;
1633 // no REFR
1634 }
1635 else if ((sShapeType == "ooxml-circularArrow"
1636 || sShapeType == "ooxml-leftCircularArrow"
1637 || sShapeType == "ooxml-leftRightCircularArrow")
1638 && nIndex == 2)
1639 {
1640 // The value adj1 connected to REFR is the thickness of the arc. The adjustvalue adj5
1641 // has the _difference_ between the outer ellipse given by shape width and height
1642 // and the middle ellipse of the arc. The handle is on the outer side of the
1643 // arc. So we calculate the difference between the ellipse through the handle
1644 // and the outer ellipse and subtract then.
1645 double fRadiusDifferenceHandle
1646 = lcl_getRadiusDistance(fWidth / 2.0, fHeight / 2.0, fDX, fDY);
1647 double fadj5(GetAdjustValueAsDouble(4));
1648 double fss(std::min(fWidth, fHeight));
1649 if (fss != 0.0)
1650 {
1651 fadj5 = fadj5 * fss / 100000.0;
1652 fRadius = 2.0 * (fadj5 - fRadiusDifferenceHandle);
1653 fRadius = fRadius * 100000.0 / fss;
1654 }
1655 // ToDo: Get angle adj3 exact. Use approximation for now
1656 fAngle = lcl_getAngleInOOXMLUnit(fDY, fDX);
1657 }
1658 else if ((sShapeType == "ooxml-circularArrow"
1659 || sShapeType == "ooxml-leftCircularArrow"
1660 || sShapeType == "ooxml-leftRightCircularArrow")
1661 && nIndex == 3)
1662 {
1663 // ToDo: Getting handle position from adjustment value adj5 is complex.
1664 // Analytical or numerical solution for backward calculation is missing.
1665 // Approximation for now, using a line from center through handle position.
1666 double fAngleRad(0.0);
1667 if (fDX != 0.0 || fDY != 0.0)
1668 fAngleRad = atan2(fDY, fDX);
1669 double fHelpX = cos(fAngleRad) * fHeight / 2.0;
1670 double fHelpY = sin(fAngleRad) * fWidth / 2.0;
1671 if (fHelpX != 0.0 || fHelpY != 0.0)
1672 {
1673 double fHelpAngle = atan2(fHelpY, fHelpX);
1674 double fOuterX = fWidth / 2.0 * cos(fHelpAngle);
1675 double fOuterY = fHeight / 2.0 * sin(fHelpAngle);
1676 double fOuterRadius = std::hypot(fOuterX, fOuterY);
1677 double fHandleRadius = std::hypot(fDX, fDY);
1678 fRadius = (fOuterRadius - fHandleRadius) / 2.0;
1679 double fss(std::min(fWidth, fHeight));
1680 if (fss != 0.0)
1681 fRadius = fRadius * 100000.0 / fss;
1682 }
1683 // no REFANGLE
1684 }
1685 else if (sShapeType == "ooxml-mathNotEqual" && nIndex == 1)
1686 {
1687 double fadj1(GetAdjustValueAsDouble(0));
1688 double fadj3(GetAdjustValueAsDouble(2));
1689 fadj1 = fadj1 * fHeight / 100000.0;
1690 fadj3 = fadj3 * fHeight / 100000.0;
1691 double fDYRefHorizBar = fDY + fadj1 + fadj3;
1692 if (fDX != 0.0 || fDYRefHorizBar != 0.0)
1693 {
1694 double fRawAngleDeg = basegfx::rad2deg(atan2(fDYRefHorizBar, fDX));
1695 fAngle = (fRawAngleDeg + 180.0) * 60000.0;
1696 }
1697 // no REFR
1698 }
1699 else
1700 {
1701 // no special meaning of radius or angle, suitable for "ooxml-arc",
1702 // "ooxml-chord", "ooxml-pie" and circular arrows value adj4.
1703 fAngle = lcl_getAngleInOOXMLUnit(fDY, fDX);
1704 fRadius = std::hypot(fDX, fDY);
1705 double fss(std::min(fWidth, fHeight));
1706 if (fss != 0.0)
1707 fRadius = fRadius * 100000.0 / fss;
1708 }
1709 }
1710 else // e.g. shapes from ODF, MS binary import or shape type "fontwork-foo"
1711 {
1712 double fXRef, fYRef;
1713 if (aHandle.nFlags & HandleFlags::POLAR)
1714 {
1715 GetParameter(fXRef, aHandle.aPolar.First, false, false);
1716 GetParameter(fYRef, aHandle.aPolar.Second, false, false);
1717 }
1718 else
1719 {
1720 fXRef = fWidth / 2.0;
1721 fYRef = fHeight / 2.0;
1722 }
1723 const double fDX = fPos1 - fXRef;
1724 const double fDY = fPos2 - fYRef;
1725 // ToDo: MS binary uses fixed-point number for the angle. Make sure conversion
1726 // to double is done in import and export.
1727 // ToDo: Angle unit is degree, but range ]-180;180] or [0;360[? Assume ]-180;180].
1728 if (fDX != 0.0 || fDY != 0.0)
1729 {
1730 fRadius = std::hypot(fDX, fDY);
1731 fAngle = basegfx::rad2deg(atan2(fDY, fDX));
1732 }
1733 }
1734
1735 // All formats can restrict the radius to a range
1737 {
1738 double fMin;
1739 GetParameter( fMin, aHandle.aRadiusRangeMinimum, false, false );
1740 if ( fRadius < fMin )
1741 fRadius = fMin;
1742 }
1744 {
1745 double fMax;
1746 GetParameter( fMax, aHandle.aRadiusRangeMaximum, false, false );
1747 if ( fRadius > fMax )
1748 fRadius = fMax;
1749 }
1750
1751 if ( nFirstAdjustmentValue >= 0 )
1752 SetAdjustValueAsDouble( fRadius, nFirstAdjustmentValue );
1753 if ( nSecondAdjustmentValue >= 0 )
1754 SetAdjustValueAsDouble( fAngle, nSecondAdjustmentValue );
1755 }
1756 else // XY-Handle
1757 {
1758 // Calculating the adjustment values follows in most cases some patterns, which only
1759 // need width and height of the shape and handle position. These patterns are calculated
1760 // in the static, local methods. More complex calculations or additional steps are
1761 // done here.
1762 // Values for corner cases like 'root(negative)' or 'div zero' are meaningless dummies.
1763 // Identifiers often refer to guide names in OOXML shape definitions.
1764 double fAdjustX = fPos1;
1765 double fAdjustY = fPos2;
1766 if (aHandle.nFlags & HandleFlags::REFX)
1767 {
1768 nFirstAdjustmentValue = aHandle.nRefX;
1769 if ((sShapeType == "ooxml-gear6") || (sShapeType == "ooxml-gear9"))
1770 {
1771 // special, needs angle calculations
1772 double fss(std::min(fWidth, fHeight));
1773 double fadj1(GetAdjustValueAsDouble(0)); // from point D6 or D9
1774 double fth(fadj1 * fss / 100000.0); // radius difference
1775 double frw(fWidth / 2.0 - fth); // inner ellipse
1776 double frh(fHeight / 2.0 - fth);
1777 double fDX(fPos1 - fWidth / 2.0);
1778 double fDY(fPos2 - fHeight / 2.0);
1779 double fbA(-1.7); // effective angle for point A6 or A9, dummy value
1780 if (fDX != 0.0 || fDY != 0.0)
1781 fbA = atan2(fDY, fDX);
1782 double faA(fbA); // corresponding circle angle, dummy value
1783 double ftmpX(frh * cos(fbA));
1784 double ftmpY(frw * sin(fbA));
1785 if (ftmpX != 0.0 || ftmpY != 0.0)
1786 faA = atan2(ftmpY, ftmpX); // range ]-pi..pi], here -pi < faA < -pi/2
1787 // screen 270 deg = mathematic coordinate system -pi/2
1788 double fha(-M_PI_2 - faA); // positive circle angle difference to 270 deg
1789 if (abs(fha) == M_PI_2) // should not happen, but ensure no tan(90deg)
1790 fha = 0.12; // dummy value
1791 double flFD(2 * std::min(frw, frh) * tan(fha) - fth);
1792 if (fss != 0.0)
1793 fAdjustX = flFD / fss * 100000.0;
1794 }
1795 else
1796 {
1797 fAdjustX
1798 = lcl_getXAdjustmentValue(sShapeType, nIndex, fPos1, fWidth, fHeight);
1799 if ((sShapeType == "ooxml-curvedDownArrow")
1800 || (sShapeType == "ooxml-curvedUpArrow"))
1801 {
1802 double fss(std::min(fWidth, fHeight));
1803 if (fss != 0.0)
1804 {
1805 double fadj3(GetAdjustValueAsDouble(2));
1806 double fHScaled(100000.0 * fHeight / fss);
1807 double fRadicand(fHScaled * fHScaled - fadj3 * fadj3);
1808 double fSqrt = fRadicand >= 0.0 ? sqrt(fRadicand) : 0.0;
1809 double fPart(200000.0 * fWidth / fss * (fSqrt + fHScaled));
1810 fAdjustX = fPart - 4.0 * fHScaled * fAdjustX;
1811 if (nIndex == 0)
1812 {
1813 // calculate adj1
1814 double fadj2(GetAdjustValueAsDouble(1));
1815 fAdjustX = fAdjustX - fadj2 * (fSqrt + fHScaled);
1816 double fDenominator(fSqrt - 3.0 * fHScaled);
1817 fAdjustX /= fDenominator != 0.0 ? fDenominator : 1.0;
1818 }
1819 else
1820 {
1821 // nIndex == 1, calculate adj2
1822 double fadj1(GetAdjustValueAsDouble(0));
1823 fAdjustX = fAdjustX - fadj1 * (fSqrt - fHScaled);
1824 double fDenominator(fSqrt + 3.0 * fHScaled);
1825 fAdjustX /= fDenominator != 0.0 ? fDenominator : 1.0;
1826 }
1827 }
1828 }
1829 }
1830 }
1831
1832 if (aHandle.nFlags & HandleFlags::REFY)
1833 {
1834 nSecondAdjustmentValue = aHandle.nRefY;
1835 if ((sShapeType == "ooxml-gear6") || (sShapeType == "ooxml-gear9"))
1836 {
1837 // special, acts more like a polar handle radius
1838 double fDX = fPos1 - fWidth / 2.0;
1839 double fDY = fPos2 - fHeight / 2.0;
1840 double fRadiusDifference
1841 = lcl_getRadiusDistance(fWidth / 2.0, fHeight / 2.0, fDX, fDY);
1842 double fss(std::min(fWidth, fHeight));
1843 if (fss != 0)
1844 fAdjustY = fRadiusDifference / fss * 100000.0;
1845 }
1846 else
1847 {
1848 fAdjustY
1849 = lcl_getYAdjustmentValue(sShapeType, nIndex, fPos2, fWidth, fHeight);
1850 if (sShapeType == "ooxml-mathDivide" && nIndex == 1)
1851 fAdjustY = fAdjustY - GetAdjustValueAsDouble(0) / 2.0
1853 else if (sShapeType == "ooxml-mathEqual" && nIndex == 0)
1854 fAdjustY -= GetAdjustValueAsDouble(1) / 2.0;
1855 else if (sShapeType == "ooxml-mathNotEqual" && nIndex == 0)
1856 fAdjustY -= GetAdjustValueAsDouble(2) / 2.0;
1857 else if (sShapeType == "ooxml-leftUpArrow" && nIndex == 0)
1858 fAdjustY -= GetAdjustValueAsDouble(1) * 2.0;
1859 else if ((sShapeType == "ooxml-curvedRightArrow")
1860 || (sShapeType == "ooxml-curvedLeftArrow"))
1861 {
1862 double fss(std::min(fWidth, fHeight));
1863 if (fss != 0.0)
1864 {
1865 double fadj3(GetAdjustValueAsDouble(2));
1866 double fWScaled(100000.0 * fWidth / fss);
1867 double fRadicand(fWScaled * fWScaled - fadj3 * fadj3);
1868 double fSqrt = fRadicand >= 0.0 ? sqrt(fRadicand) : 0.0;
1869 if (nIndex == 0)
1870 {
1871 // calculate adj1
1872 double fadj2(GetAdjustValueAsDouble(1));
1873 fAdjustY = fWScaled * (2.0 * fAdjustY - fadj2);
1874 fAdjustY += (200000.0 / fss * fHeight - fadj2) * fSqrt;
1875 double fDenominator(fSqrt + fWScaled);
1876 fAdjustY /= fDenominator != 0.0 ? fDenominator : 1.0;
1877 }
1878 else
1879 {
1880 // nIndex == 1, calculate adj2
1881 double fadj1(GetAdjustValueAsDouble(0));
1882 fAdjustY = fWScaled * (2.0 * fAdjustY + fadj1);
1883 fAdjustY += (200000.0 / fss * fHeight - fadj1) * fSqrt;
1884 double fDenominator(fSqrt + 3.0 * fWScaled);
1885 fAdjustY /= fDenominator != 0.0 ? fDenominator : 1.0;
1886 }
1887 }
1888 }
1889 else if (sShapeType == "ooxml-uturnArrow" && nIndex == 2)
1890 {
1891 double fss(std::min(fWidth, fHeight));
1892 if (fss != 0.0)
1893 {
1894 double fadj5(GetAdjustValueAsDouble(4));
1895 fAdjustY += fHeight / fss * (fadj5 - 100000.0);
1896 }
1897 }
1898 else if (sShapeType == "ooxml-leftRightRibbon")
1899 {
1900 if (nIndex == 0)
1901 fAdjustY = GetAdjustValueAsDouble(2) - fAdjustY;
1902 else // nIndex == 2
1903 fAdjustY = GetAdjustValueAsDouble(0) + fAdjustY;
1904 }
1905 }
1906 }
1907
1908 if ( nFirstAdjustmentValue >= 0 )
1909 {
1910 if ( aHandle.nFlags & HandleFlags::RANGE_X_MINIMUM ) // check if horizontal handle needs to be within a range
1911 {
1912 double fXMin;
1913 GetParameter( fXMin, aHandle.aXRangeMinimum, false, false );
1914 if (fAdjustX < fXMin)
1915 fAdjustX = fXMin;
1916 }
1917 if ( aHandle.nFlags & HandleFlags::RANGE_X_MAXIMUM ) // check if horizontal handle needs to be within a range
1918 {
1919 double fXMax;
1920 GetParameter( fXMax, aHandle.aXRangeMaximum, false, false );
1921 if (fAdjustX > fXMax)
1922 fAdjustX = fXMax;
1923 }
1924 SetAdjustValueAsDouble(fAdjustX, nFirstAdjustmentValue);
1925 }
1926 if ( nSecondAdjustmentValue >= 0 )
1927 {
1928 if ( aHandle.nFlags & HandleFlags::RANGE_Y_MINIMUM ) // check if vertical handle needs to be within a range
1929 {
1930 double fYMin;
1931 GetParameter( fYMin, aHandle.aYRangeMinimum, false, false );
1932 if (fAdjustY < fYMin)
1933 fAdjustY = fYMin;
1934 }
1935 if ( aHandle.nFlags & HandleFlags::RANGE_Y_MAXIMUM ) // check if vertical handle needs to be within a range
1936 {
1937 double fYMax;
1938 GetParameter( fYMax, aHandle.aYRangeMaximum, false, false );
1939 if (fAdjustY > fYMax)
1940 fAdjustY = fYMax;
1941 }
1942 SetAdjustValueAsDouble(fAdjustY, nSecondAdjustmentValue);
1943 }
1944 }
1945 // and writing them back into the GeometryItem
1947 css::beans::PropertyValue aPropVal;
1948 aPropVal.Name = "AdjustmentValues";
1949 aPropVal.Value <<= seqAdjustmentValues;
1950 aGeometryItem.SetPropertyValue( aPropVal );
1951 mrSdrObjCustomShape.SetMergedItem( aGeometryItem );
1952 bRetValue = true;
1953 }
1954 }
1955 return bRetValue;
1956}
1957
1959{
1960 XLineStartItem aLineStart;
1961 aLineStart.SetLineStartValue(pObj->GetMergedItem( XATTR_LINEEND ).GetLineEndValue());
1962 XLineStartWidthItem aLineStartWidth(pObj->GetMergedItem( XATTR_LINEENDWIDTH ).GetValue());
1963 XLineStartCenterItem aLineStartCenter(pObj->GetMergedItem( XATTR_LINEENDCENTER ).GetValue());
1964
1965 XLineEndItem aLineEnd;
1966 aLineEnd.SetLineEndValue(pObj->GetMergedItem( XATTR_LINESTART ).GetLineStartValue());
1967 XLineEndWidthItem aLineEndWidth(pObj->GetMergedItem( XATTR_LINESTARTWIDTH ).GetValue());
1968 XLineEndCenterItem aLineEndCenter(pObj->GetMergedItem( XATTR_LINESTARTCENTER ).GetValue());
1969
1970 pObj->SetMergedItem( aLineStart );
1971 pObj->SetMergedItem( aLineStartWidth );
1972 pObj->SetMergedItem( aLineStartCenter );
1973 pObj->SetMergedItem( aLineEnd );
1974 pObj->SetMergedItem( aLineEndWidth );
1975 pObj->SetMergedItem( aLineEndCenter );
1976}
1977
1978static basegfx::B2DPolygon CreateArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd, const bool bClockwise )
1979{
1980 tools::Rectangle aRect( rRect );
1981 Point aStart( rStart );
1982 Point aEnd( rEnd );
1983
1984 sal_Int32 bSwapStartEndAngle = 0;
1985
1986 if ( aRect.Left() > aRect.Right() )
1987 bSwapStartEndAngle ^= 0x01;
1988 if ( aRect.Top() > aRect.Bottom() )
1989 bSwapStartEndAngle ^= 0x11;
1990 if ( bSwapStartEndAngle )
1991 {
1992 aRect.Normalize();
1993 if ( bSwapStartEndAngle & 1 )
1994 {
1995 Point aTmp( aStart );
1996 aStart = aEnd;
1997 aEnd = aTmp;
1998 }
1999 }
2000
2001 tools::Polygon aTempPoly( aRect, aStart, aEnd, PolyStyle::Arc );
2002 basegfx::B2DPolygon aRetval;
2003
2004 if ( bClockwise )
2005 {
2006 for ( sal_uInt16 j = aTempPoly.GetSize(); j--; )
2007 {
2008 aRetval.append(basegfx::B2DPoint(aTempPoly[ j ].X(), aTempPoly[ j ].Y()));
2009 }
2010 }
2011 else
2012 {
2013 for ( sal_uInt16 j = 0; j < aTempPoly.GetSize(); j++ )
2014 {
2015 aRetval.append(basegfx::B2DPoint(aTempPoly[ j ].X(), aTempPoly[ j ].Y()));
2016 }
2017 }
2018
2019 return aRetval;
2020}
2021
2022static double lcl_getNormalizedCircleAngleRad(const double fWR, const double fHR, const double fEllipseAngleDeg)
2023{
2024 double fRet(0.0);
2025 double fEAngleDeg(fmod(fEllipseAngleDeg, 360.0));
2026 if (fEAngleDeg < 0.0)
2027 fEAngleDeg += 360.0;
2028 if (fEAngleDeg == 0.0 || fEAngleDeg == 90.0 || fEAngleDeg == 180.0 || fEAngleDeg == 270.0)
2029 return basegfx::deg2rad(fEAngleDeg);
2030 const double fX(fHR * cos(basegfx::deg2rad(fEAngleDeg)));
2031 const double fY(fWR * sin(basegfx::deg2rad(fEAngleDeg)));
2032 if (fX != 0.0 || fY != 0.0)
2033 {
2034 fRet = atan2(fY, fX);
2035 if (fRet < 0.0)
2036 fRet += 2 * M_PI;
2037 }
2038 return fRet;
2039}
2040
2041static double lcl_getNormalizedAngleRad(const double fCircleAngleDeg)
2042{
2043 double fRet(fmod(fCircleAngleDeg, 360.0));
2044 if (fRet < 0.0)
2045 fRet += 360.0;
2046 return basegfx::deg2rad(fRet);
2047}
2048
2050 sal_Int32& rSrcPt,
2051 sal_Int32& rSegmentInd,
2052 std::vector< std::pair< rtl::Reference<SdrPathObj>, double> >& rObjectList,
2053 const bool bLineGeometryNeededOnly,
2054 const bool bSortFilledObjectsToBack,
2055 sal_Int32 nIndex)
2056{
2057 bool bNoFill = false;
2058 bool bNoStroke = false;
2059 double dBrightness = 0.0; //no blending
2060
2061 basegfx::B2DPolyPolygon aNewB2DPolyPolygon;
2062 basegfx::B2DPolygon aNewB2DPolygon;
2063
2065
2066 sal_Int32 nSegInfoSize = seqSegments.getLength();
2067 if ( !nSegInfoSize )
2068 {
2069 for ( const EnhancedCustomShapeParameterPair& rCoordinate : std::as_const(seqCoordinates) )
2070 {
2071 const Point aTempPoint(GetPoint( rCoordinate, true, true ));
2072 aNewB2DPolygon.append(basegfx::B2DPoint(aTempPoint.X(), aTempPoint.Y()));
2073 }
2074
2075 aNewB2DPolygon.setClosed(true);
2076 }
2077 else
2078 {
2079 sal_Int32 nCoordSize = seqCoordinates.getLength();
2080 for ( ;rSegmentInd < nSegInfoSize; )
2081 {
2082 sal_Int16 nCommand = seqSegments[ rSegmentInd ].Command;
2083 sal_Int16 nPntCount= seqSegments[ rSegmentInd++ ].Count;
2084
2085 switch ( nCommand )
2086 {
2087 case NOFILL :
2088 bNoFill = true;
2089 break;
2090 case NOSTROKE :
2091 bNoStroke = true;
2092 break;
2093 case DARKEN :
2094 dBrightness = -0.4; //use sign to distinguish DARKEN from LIGHTEN
2095 break;
2096 case DARKENLESS :
2097 dBrightness = -0.2;
2098 break;
2099 case LIGHTEN :
2100 dBrightness = 0.4;
2101 break;
2102 case LIGHTENLESS :
2103 dBrightness = 0.2;
2104 break;
2105 case MOVETO :
2106 {
2107 if(aNewB2DPolygon.count() > 1)
2108 {
2109 // #i76201# Add conversion to closed polygon when first and last points are equal
2110 basegfx::utils::checkClosed(aNewB2DPolygon);
2111 aNewB2DPolyPolygon.append(aNewB2DPolygon);
2112 }
2113
2114 aNewB2DPolygon.clear();
2115
2116 if ( rSrcPt < nCoordSize )
2117 {
2118 const Point aTempPoint(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2119 SAL_INFO(
2120 "svx",
2121 "moveTo: " << aTempPoint.X() << ","
2122 << aTempPoint.Y());
2123 aNewB2DPolygon.append(basegfx::B2DPoint(aTempPoint.X(), aTempPoint.Y()));
2124 }
2125 }
2126 break;
2127 case ENDSUBPATH :
2128 break;
2129 case CLOSESUBPATH :
2130 {
2131 if(aNewB2DPolygon.count())
2132 {
2133 if(aNewB2DPolygon.count() > 1)
2134 {
2135 aNewB2DPolygon.setClosed(true);
2136 aNewB2DPolyPolygon.append(aNewB2DPolygon);
2137 }
2138
2139 aNewB2DPolygon.clear();
2140 }
2141 }
2142 break;
2143 case CURVETO :
2144 {
2145 for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( ( rSrcPt + 2 ) < nCoordSize ); i++ )
2146 {
2147 const Point aControlA(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2148 const Point aControlB(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2149 const Point aEnd(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2150
2151 DBG_ASSERT(aNewB2DPolygon.count(), "EnhancedCustomShape2d::CreateSubPath: Error in adding control point (!)");
2152 aNewB2DPolygon.appendBezierSegment(
2153 basegfx::B2DPoint(aControlA.X(), aControlA.Y()),
2154 basegfx::B2DPoint(aControlB.X(), aControlB.Y()),
2155 basegfx::B2DPoint(aEnd.X(), aEnd.Y()));
2156 }
2157 }
2158 break;
2159
2160 case ANGLEELLIPSE: // command U
2161 case ANGLEELLIPSETO: // command T
2162 {
2163 // Some shapes will need special handling, decide on property 'Type'.
2164 OUString sShpType;
2166 Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
2167 if (pAny)
2168 *pAny >>= sShpType;
2169 // User defined shapes in MS binary format, which contain command U or T after import
2170 // in LibreOffice, starts with "mso".
2171 const bool bIsFromBinaryImport(sShpType.startsWith("mso"));
2172 // The only own or imported preset shapes with U command are those listed below.
2173 // Command T is not used in preset shapes.
2174 const std::unordered_set<OUString> aPresetShapesWithU =
2175 { "ellipse", "ring", "smiley", "sun", "forbidden", "flowchart-connector",
2176 "flowchart-summing-junction", "flowchart-or", "cloud-callout"};
2177 std::unordered_set<OUString>::const_iterator aIter = aPresetShapesWithU.find(sShpType);
2178 const bool bIsPresetShapeWithU(aIter != aPresetShapesWithU.end());
2179
2180 for (sal_uInt16 i = 0; (i < nPntCount) && ((rSrcPt + 2) < nCoordSize); i++)
2181 {
2182 // ANGLEELLIPSE is the same as ANGLEELLIPSETO, only that it
2183 // makes an implicit MOVETO. That ends the previous subpath.
2184 if (ANGLEELLIPSE == nCommand)
2185 {
2186 if (aNewB2DPolygon.count() > 1)
2187 {
2188 // #i76201# Add conversion to closed polygon when first and last points are equal
2189 basegfx::utils::checkClosed(aNewB2DPolygon);
2190 aNewB2DPolyPolygon.append(aNewB2DPolygon);
2191 }
2192 aNewB2DPolygon.clear();
2193 }
2194
2195 // Read all parameters, but do not finally handle them.
2196 basegfx::B2DPoint aCenter(GetPointAsB2DPoint(seqCoordinates[ rSrcPt ], true, true));
2197 double fWR; // horizontal ellipse radius
2198 double fHR; // vertical ellipse radius
2199 GetParameter(fWR, seqCoordinates[rSrcPt + 1].First, true, false);
2200 GetParameter(fHR, seqCoordinates[rSrcPt + 1].Second, false, true);
2201 double fStartAngle;
2202 GetParameter(fStartAngle, seqCoordinates[rSrcPt + 2].First, false, false);
2203 double fEndAngle;
2204 GetParameter(fEndAngle, seqCoordinates[rSrcPt + 2].Second, false, false);
2205 // Increasing here allows flat case differentiation tree by using 'continue'.
2206 rSrcPt += 3;
2207
2208 double fScaledWR(fWR * fXScale);
2209 double fScaledHR(fHR * fYScale);
2210 if (fScaledWR == 0.0 && fScaledHR == 0.0)
2211 {
2212 // degenerated ellipse, add center point
2213 aNewB2DPolygon.append(aCenter);
2214 continue;
2215 }
2216
2217 if (bIsFromBinaryImport)
2218 {
2219 // If a shape comes from MS binary ('escher') import, the angles are in degrees*2^16
2220 // and the second angle is not an end angle, but a swing angle.
2221 // MS Word shows this behavior: 0deg right, 90deg top, 180deg left and 270deg
2222 // bottom. Third and forth parameter are horizontal and vertical radius, not width
2223 // and height as noted in VML spec. A positive swing angle goes counter-clock
2224 // wise (in user view). The swing angle might go several times around in case
2225 // abs(swing angle) >= 360deg. Stroke accumulates, so that e.g. dash-dot might fill the
2226 // gaps of previous turn. Fill does not accumulate but uses even-odd rule, semi-transparent
2227 // fill does not become darker. The start and end points of the arc are calculated by
2228 // using the angles on a circle and then scaling the circle to the ellipse. Caution, that
2229 // is different from angle handling in ARCANGLETO and ODF.
2230 // The following implementation generates such rendering. It is only for rendering legacy
2231 // MS shapes and independent of the meaning of commands U and T in ODF specification.
2232
2233 // The WordArt shape 'RingOutside' has already angles in degree, all other need
2234 // conversion from fixed-point number.
2235 double fSwingAngle = fEndAngle;
2236 if (sShpType != "mso-spt143")
2237 {
2238 fStartAngle /= 65536.0;
2239 fSwingAngle = fEndAngle / 65536.0;
2240 }
2241 // Convert orientation
2242 fStartAngle = -fStartAngle;
2243 fSwingAngle = -fSwingAngle;
2244
2245 fEndAngle = fStartAngle + fSwingAngle;
2246 if (fSwingAngle < 0.0)
2247 std::swap(fStartAngle, fEndAngle);
2248 double fFrom(fStartAngle);
2249 double fTo(fFrom + 180.0);
2250 basegfx::B2DPolygon aTempB2DPolygon;
2251 double fS; // fFrom in radians in [0..2Pi[
2252 double fE; // fTo or fEndAngle in radians in [0..2PI[
2253 while (fTo < fEndAngle)
2254 {
2255 fS = lcl_getNormalizedAngleRad(fFrom);
2256 fE = lcl_getNormalizedAngleRad(fTo);
2257 aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR, fS,fE));
2258 fFrom = fTo;
2259 fTo += 180.0;
2260 }
2261 fS = lcl_getNormalizedAngleRad(fFrom);
2262 fE = lcl_getNormalizedAngleRad(fEndAngle);
2263 aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR,fS, fE));
2264 if (fSwingAngle < 0)
2265 aTempB2DPolygon.flip();
2266 aNewB2DPolygon.append(aTempB2DPolygon);
2267 continue;
2268 }
2269
2270 // The not yet handled shapes are own preset shapes, or preset shapes from MS binary import, or user
2271 // defined shapes, or foreign shapes. Shapes from OOXML import do not use ANGLEELLIPSE or
2272 // ANGLEELLIPSETO, but use ARCANGLETO.
2273 if (bIsPresetShapeWithU)
2274 {
2275 // Besides "cloud-callout" all preset shapes have angle values '0 360'.
2276 // The imported "cloud-callout" has angle values '0 360' too, only our own "cloud-callout"
2277 // has values '0 23592960'. But that is fixedfloat and means 360*2^16. Thus all these shapes
2278 // have a full ellipse with start at 0deg.
2279 aNewB2DPolygon.append(basegfx::utils::createPolygonFromEllipse(aCenter, fScaledWR, fScaledHR));
2280 continue;
2281 }
2282
2283 // In all other cases, full ODF conform handling is necessary. ODF rules:
2284 // Third and forth parameter are horizontal and vertical radius.
2285 // An angle determines the start or end point of the segment by intersection of the second angle
2286 // leg with the ellipse. The first angle leg is always the positive x-axis. For the position
2287 // of the intersection points the angle is used modulo 360deg in range [0deg..360deg[.
2288 // The position of range [0deg..360deg[ is the same as in command ARCANGLETO, with 0deg right,
2289 // 90deg bottom, 180deg left and 270deg top. Only if abs(end angle - start angle) == 360 deg,
2290 // a full ellipse is drawn. The segment is always drawn clock wise (in user view) from start
2291 // point to end point. The end point of the segment becomes the new "current" point.
2292
2293 if (fabs(fabs(fEndAngle - fStartAngle) - 360.0) < 1.0E-15)
2294 {
2295 // draw full ellipse
2296 // Because createPolygonFromEllipseSegment cannot create full ellipse and
2297 // createPolygonFromEllipse has no varying starts, we use two half ellipses.
2298 const double fS(lcl_getNormalizedCircleAngleRad(fWR, fHR, fStartAngle));
2299 const double fH(lcl_getNormalizedCircleAngleRad(fWR, fHR, fStartAngle + 180.0));
2300 const double fE(lcl_getNormalizedCircleAngleRad(fWR, fHR, fEndAngle));
2301 aNewB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR, fS, fH));
2302 aNewB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR, fH, fE));
2303 continue;
2304 }
2305
2306 // remaining cases with central segment angle < 360
2307 double fS(lcl_getNormalizedCircleAngleRad(fWR, fHR, fStartAngle));
2308 double fE(lcl_getNormalizedCircleAngleRad(fWR, fHR, fEndAngle));
2309 aNewB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR, fS, fE));
2310 } // end for
2311 } // end case
2312 break;
2313
2314 case QUADRATICCURVETO :
2315 {
2316 for ( sal_Int32 i(0); ( i < nPntCount ) && ( rSrcPt + 1 < nCoordSize ); i++ )
2317 {
2318 DBG_ASSERT(aNewB2DPolygon.count(), "EnhancedCustomShape2d::CreateSubPath: Error no previous point for Q (!)");
2319 if (aNewB2DPolygon.count() > 0)
2320 {
2321 const basegfx::B2DPoint aPreviousEndPoint(aNewB2DPolygon.getB2DPoint(aNewB2DPolygon.count()-1));
2322 const basegfx::B2DPoint aControlQ(GetPointAsB2DPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2323 const basegfx::B2DPoint aEnd(GetPointAsB2DPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2324 const basegfx::B2DPoint aControlA((aPreviousEndPoint + (aControlQ * 2)) / 3);
2325 const basegfx::B2DPoint aControlB(((aControlQ * 2) + aEnd) / 3);
2326 aNewB2DPolygon.appendBezierSegment(aControlA, aControlB, aEnd);
2327 }
2328 else // no previous point; ill structured path, but try to draw as much as possible
2329 {
2330 rSrcPt++; // skip control point
2331 const basegfx::B2DPoint aEnd(GetPointAsB2DPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2332 aNewB2DPolygon.append(aEnd);
2333 }
2334 }
2335 }
2336 break;
2337
2338 case LINETO :
2339 {
2340 for ( sal_Int32 i(0); ( i < nPntCount ) && ( rSrcPt < nCoordSize ); i++ )
2341 {
2342 const Point aTempPoint(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
2343 SAL_INFO(
2344 "svx",
2345 "lineTo: " << aTempPoint.X() << ","
2346 << aTempPoint.Y());
2347 aNewB2DPolygon.append(basegfx::B2DPoint(aTempPoint.X(), aTempPoint.Y()));
2348 }
2349 }
2350 break;
2351
2352 case ARC :
2353 case CLOCKWISEARC :
2354 case ARCTO :
2355 case CLOCKWISEARCTO :
2356 {
2357 bool bClockwise = ( nCommand == CLOCKWISEARC ) || ( nCommand == CLOCKWISEARCTO );
2358 bool bImplicitMoveTo = (nCommand == ARC) || (nCommand == CLOCKWISEARC);
2359 sal_uInt32 nXor = bClockwise ? 3 : 2;
2360 for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( ( rSrcPt + 3 ) < nCoordSize ); i++ )
2361 {
2362 if (bImplicitMoveTo)
2363 {
2364 if (aNewB2DPolygon.count() > 1)
2365 {
2366 // #i76201# Add conversion to closed polygon when first and last
2367 // points are equal
2368 basegfx::utils::checkClosed(aNewB2DPolygon);
2369 aNewB2DPolyPolygon.append(aNewB2DPolygon);
2370 }
2371 aNewB2DPolygon.clear();
2372 }
2373 tools::Rectangle aRect = tools::Rectangle::Normalize( GetPoint( seqCoordinates[ rSrcPt ], true, true ), GetPoint( seqCoordinates[ rSrcPt + 1 ], true, true ) );
2374 if ( aRect.GetWidth() && aRect.GetHeight() )
2375 {
2376 Point aStart( GetPoint( seqCoordinates[ static_cast<sal_uInt16>( rSrcPt + nXor ) ], true, true ) );
2377 Point aEnd( GetPoint( seqCoordinates[ static_cast<sal_uInt16>( rSrcPt + ( nXor ^ 1 ) ) ], true, true ) );
2378 aNewB2DPolygon.append(CreateArc( aRect, aStart, aEnd, bClockwise));
2379 }
2380 rSrcPt += 4;
2381 }
2382 }
2383 break;
2384
2385 case ARCANGLETO :
2386 {
2387 double fWR, fHR; // in Shape coordinate system
2388 double fStartAngle, fSwingAngle; // in deg
2389
2390 for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( rSrcPt + 1 < nCoordSize ); i++ )
2391 {
2392 basegfx::B2DPoint aTempPair;
2393 aTempPair = GetPointAsB2DPoint(seqCoordinates[static_cast<sal_uInt16>(rSrcPt)], false /*bScale*/, false /*bReplaceGeoSize*/);
2394 fWR = aTempPair.getX();
2395 fHR = aTempPair.getY();
2396 aTempPair = GetPointAsB2DPoint(seqCoordinates[static_cast<sal_uInt16>(rSrcPt + 1)], false /*bScale*/, false /*bReplaceGeoSize*/);
2397 fStartAngle = aTempPair.getX();
2398 fSwingAngle = aTempPair.getY();
2399
2400 // tdf#122323 MS Office clamps the swing angle to [-360,360]. Such restriction
2401 // is neither in OOXML nor in ODF. Nevertheless, to be compatible we do it for
2402 // "ooxml-foo" shapes. Those shapes have their origin in MS Office.
2403 if (bOOXMLShape)
2404 {
2405 fSwingAngle = std::clamp(fSwingAngle, -360.0, 360.0);
2406 }
2407
2408 SAL_INFO("svx", "ARCANGLETO scale: " << fWR << "x" << fHR << " angles: " << fStartAngle << "," << fSwingAngle);
2409
2410 if (aNewB2DPolygon.count() > 0) // otherwise no "current point"
2411 {
2412 // use similar methods as in command U
2413 basegfx::B2DPolygon aTempB2DPolygon;
2414
2415 if (fWR == 0.0 && fHR == 0.0)
2416 {
2417 // degenerated ellipse, add this one point
2418 aTempB2DPolygon.append(basegfx::B2DPoint(0.0, 0.0));
2419 }
2420 else
2421 {
2422 double fEndAngle = fStartAngle + fSwingAngle;
2423 // Generate arc with ellipse left|top = 0|0.
2424 basegfx::B2DPoint aCenter(fWR, fHR);
2425 if (fSwingAngle < 0.0)
2426 std::swap(fStartAngle, fEndAngle);
2427 double fS; // fFrom in radians in [0..2Pi[
2428 double fE; // fTo or fEndAngle in radians in [0..2PI[
2429 double fFrom(fStartAngle);
2430 // createPolygonFromEllipseSegment expects angles in [0..2PI[.
2431 if (fSwingAngle >= 360.0 || fSwingAngle <= -360.0)
2432 {
2433 double fTo(fFrom + 180.0);
2434 while (fTo < fEndAngle)
2435 {
2436 fS = lcl_getNormalizedCircleAngleRad(fWR, fHR, fFrom);
2437 fE = lcl_getNormalizedCircleAngleRad(fWR, fHR, fTo);
2438 aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fWR, fHR, fS,fE));
2439 fFrom = fTo;
2440 fTo += 180.0;
2441 }
2442 }
2443 fS = lcl_getNormalizedCircleAngleRad(fWR, fHR, fFrom);
2444 fE = lcl_getNormalizedCircleAngleRad(fWR, fHR, fEndAngle);
2445 aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fWR, fHR,fS, fE));
2446 if (fSwingAngle < 0)
2447 aTempB2DPolygon.flip();
2448 aTempB2DPolygon.removeDoublePoints();
2449 }
2450 // Scale arc to 1/100mm
2452 aTempB2DPolygon.transform(aMatrix);
2453
2454 // Now that we have the arc, move it to the "current point".
2455 basegfx::B2DPoint aCurrentPointB2D( aNewB2DPolygon.getB2DPoint(aNewB2DPolygon.count() - 1 ) );
2456 const double fDx(aCurrentPointB2D.getX() - aTempB2DPolygon.getB2DPoint(0).getX());
2457 const double fDy(aCurrentPointB2D.getY() - aTempB2DPolygon.getB2DPoint(0).getY());
2459 aTempB2DPolygon.transform(aMatrix);
2460 aNewB2DPolygon.append(aTempB2DPolygon);
2461 }
2462
2463 rSrcPt += 2;
2464 }
2465 }
2466 break;
2467
2468 case ELLIPTICALQUADRANTX :
2469 case ELLIPTICALQUADRANTY :
2470 {
2471 if (nPntCount && (rSrcPt < nCoordSize))
2472 {
2473 // The arc starts at the previous point and ends at the point given in the parameter.
2474 basegfx::B2DPoint aStart;
2475 basegfx::B2DPoint aEnd;
2476 sal_uInt16 i = 0;
2477 if (rSrcPt)
2478 {
2479 aStart = GetPointAsB2DPoint(seqCoordinates[rSrcPt - 1], true, true);
2480 }
2481 else
2482 { // no previous point, path is ill-structured. But we want to show as much as possible.
2483 // Thus make a moveTo to the point given as parameter and continue from there.
2484 aStart = GetPointAsB2DPoint(seqCoordinates[static_cast<sal_uInt16>(rSrcPt)], true, true);
2485 aNewB2DPolygon.append(aStart);
2486 rSrcPt++;
2487 i++;
2488 }
2489 // If there are several points, then the direction changes with every point.
2490 bool bIsXDirection(nCommand == ELLIPTICALQUADRANTX);
2492 for ( ; ( i < nPntCount ) && ( rSrcPt < nCoordSize ); i++ )
2493 {
2494 aEnd = GetPointAsB2DPoint(seqCoordinates[rSrcPt], true, true);
2495 basegfx::B2DPoint aCenter;
2496 double fRadiusX = fabs(aEnd.getX() - aStart.getX());
2497 double fRadiusY = fabs(aEnd.getY() - aStart.getY());
2498 if (bIsXDirection)
2499 {
2500 aCenter = basegfx::B2DPoint(aStart.getX(),aEnd.getY());
2501 if (aEnd.getX()<aStart.getX())
2502 {
2503 if (aEnd.getY()<aStart.getY()) // left, up
2504 {
2505 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, M_PI_2, M_PI);
2506 }
2507 else // left, down
2508 {
2509 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, M_PI, 1.5*M_PI);
2510 aArc.flip();
2511 }
2512 }
2513 else // aEnd.getX()>=aStart.getX()
2514 {
2515 if (aEnd.getY()<aStart.getY()) // right, up
2516 {
2517 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, 0.0, M_PI_2);
2518 aArc.flip();
2519 }
2520 else // right, down
2521 {
2522 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, 1.5*M_PI, 2*M_PI);
2523 }
2524 }
2525 }
2526 else // y-direction
2527 {
2528 aCenter = basegfx::B2DPoint(aEnd.getX(),aStart.getY());
2529 if (aEnd.getX()<aStart.getX())
2530 {
2531 if (aEnd.getY()<aStart.getY()) // up, left
2532 {
2533 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, 1.5*M_PI, 2*M_PI);
2534 aArc.flip();
2535 }
2536 else // down, left
2537 {
2538 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, 0.0, M_PI_2);
2539 }
2540 }
2541 else // aEnd.getX()>=aStart.getX()
2542 {
2543 if (aEnd.getY()<aStart.getY()) // up, right
2544 {
2545 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, M_PI, 1.5*M_PI);
2546 }
2547 else // down, right
2548 {
2549 aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, M_PI_2, M_PI);
2550 aArc.flip();
2551 }
2552 }
2553 }
2554 aNewB2DPolygon.append(aArc);
2555 rSrcPt++;
2556 bIsXDirection = !bIsXDirection;
2557 aStart = aEnd;
2558 }
2559 }
2560 // else error in path syntax, do nothing
2561 }
2562 break;
2563
2564#ifdef DBG_CUSTOMSHAPE
2565 case UNKNOWN :
2566 default :
2567 {
2568 SAL_WARN( "svx", "CustomShapes::unknown PolyFlagValue :" << nCommand );
2569 }
2570 break;
2571#endif
2572 }
2573 if ( nCommand == ENDSUBPATH )
2574 break;
2575 }
2576 }
2577 if ( rSegmentInd == nSegInfoSize )
2578 rSegmentInd++;
2579
2580 if(aNewB2DPolygon.count() > 1)
2581 {
2582 // #i76201# Add conversion to closed polygon when first and last points are equal
2583 basegfx::utils::checkClosed(aNewB2DPolygon);
2584 aNewB2DPolyPolygon.append(aNewB2DPolygon);
2585 }
2586
2587 if(!aNewB2DPolyPolygon.count())
2588 return;
2589
2590 // #i37011#
2591 bool bForceCreateTwoObjects(false);
2592
2593 if(!bSortFilledObjectsToBack && !aNewB2DPolyPolygon.isClosed() && !bNoStroke)
2594 {
2595 bForceCreateTwoObjects = true;
2596 }
2597
2598 if(bLineGeometryNeededOnly)
2599 {
2600 bForceCreateTwoObjects = true;
2601 bNoFill = true;
2602 bNoStroke = false;
2603 }
2604
2605 if(bForceCreateTwoObjects || bSortFilledObjectsToBack)
2606 {
2607 if(bFilled && !bNoFill)
2608 {
2609 basegfx::B2DPolyPolygon aClosedPolyPolygon(aNewB2DPolyPolygon);
2610 aClosedPolyPolygon.setClosed(true);
2614 std::move(aClosedPolyPolygon)));
2615 SfxItemSet aTempSet(*this);
2616 aTempSet.Put(makeSdrShadowItem(false));
2617 aTempSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
2618 pFill->SetMergedItemSet(aTempSet);
2619 rObjectList.push_back(std::pair< rtl::Reference<SdrPathObj>, double >(std::move(pFill), dBrightness));
2620 }
2621
2622 if(!bNoStroke)
2623 {
2624 // there is no reason to use OBJ_PLIN here when the polygon is actually closed,
2625 // the non-fill is defined by XFILL_NONE. Since SdrPathObj::ImpForceKind() needs
2626 // to correct the polygon (here: open it) using the type, the last edge may get lost.
2627 // Thus, use a type that fits the polygon
2630 aNewB2DPolyPolygon.isClosed() ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
2631 aNewB2DPolyPolygon));
2632 SfxItemSet aTempSet(*this);
2633 aTempSet.Put(makeSdrShadowItem(false));
2634 aTempSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
2635 pStroke->SetMergedItemSet(aTempSet);
2636 rObjectList.push_back(std::pair< rtl::Reference<SdrPathObj>, double >(std::move(pStroke), dBrightness));
2637 }
2638 }
2639 else
2640 {
2642 SfxItemSet aTempSet(*this);
2643 aTempSet.Put(makeSdrShadowItem(false));
2644
2645 if(bNoFill)
2646 {
2647 // see comment above about OBJ_PLIN
2648 pObj = new SdrPathObj(
2650 aNewB2DPolyPolygon.isClosed() ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
2651 aNewB2DPolyPolygon);
2652 aTempSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
2653 }
2654 else
2655 {
2656 aNewB2DPolyPolygon.setClosed(true);
2657 pObj = new SdrPathObj(
2660 aNewB2DPolyPolygon);
2661 }
2662
2663 if(bNoStroke)
2664 {
2665 aTempSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
2666 }
2667
2668 pObj->SetMergedItemSet(aTempSet);
2669 rObjectList.push_back(std::pair< rtl::Reference<SdrPathObj>, double >(std::move(pObj), dBrightness));
2670 }
2671}
2672
2674 MSO_SPT eSpType,
2675 sal_uInt32 nLineObjectCount,
2676 std::vector< std::pair< rtl::Reference<SdrPathObj>, double> >& vObjectList )
2677{
2678 bool bAccent = false;
2679 switch( eSpType )
2680 {
2681 case mso_sptCallout1 :
2683 case mso_sptCallout90 :
2685 default:
2686 break;
2687
2692 {
2693 sal_uInt32 nLine = 0;
2694
2695 for ( const std::pair< rtl::Reference<SdrPathObj>, double >& rCandidate : vObjectList )
2696 {
2697 SdrPathObj* pObj(rCandidate.first.get());
2698
2699 if(pObj->IsLine())
2700 {
2701 nLine++;
2702 if ( nLine == nLineObjectCount )
2703 {
2706 }
2707 }
2708 }
2709 }
2710 break;
2711
2712 // switch start & end
2715 bAccent = true;
2716 [[fallthrough]];
2717 case mso_sptCallout2 :
2719 {
2720 sal_uInt32 nLine = 0;
2721
2722 for ( const std::pair< rtl::Reference<SdrPathObj>, double >& rCandidate : vObjectList )
2723 {
2724 SdrPathObj* pObj(rCandidate.first.get());
2725
2726 if(pObj->IsLine())
2727 {
2728 nLine++;
2729 if ( nLine == 1 )
2731 else if ( ( bAccent && ( nLine == nLineObjectCount - 1 ) ) || ( !bAccent && ( nLine == nLineObjectCount ) ) )
2733 else
2734 {
2737 }
2738 }
2739 }
2740 }
2741 break;
2742
2745 case mso_sptCallout3 :
2747 {
2748 sal_uInt32 nLine = 0;
2749
2750 for ( const std::pair< rtl::Reference<SdrPathObj>, double >& rCandidate : vObjectList )
2751 {
2752 SdrPathObj* pObj(rCandidate.first.get());
2753
2754 if(pObj->IsLine())
2755 {
2756 if ( nLine )
2757 {
2760 }
2761 else
2763
2764 nLine++;
2765 }
2766 }
2767 }
2768 break;
2769 }
2770}
2771
2773 SdrPathObj& rObj,
2774 double dBrightness,
2775 const SfxItemSet& rCustomShapeSet,
2776 sal_uInt32& nColorIndex,
2777 sal_uInt32 nColorCount)
2778{
2779 if ( rObj.IsLine() )
2780 return;
2781
2782 const drawing::FillStyle eFillStyle = rObj.GetMergedItem(XATTR_FILLSTYLE).GetValue();
2783 if (eFillStyle == drawing::FillStyle_NONE)
2784 return;
2785
2786 switch( eFillStyle )
2787 {
2788 default:
2789 case drawing::FillStyle_SOLID:
2790 {
2791 if ( nColorCount || 0.0 != dBrightness )
2792 {
2793 Color aFillColor = GetColorData(
2794 rCustomShapeSet.Get( XATTR_FILLCOLOR ).GetColorValue(),
2795 std::min(nColorIndex, nColorCount-1),
2796 dBrightness );
2797 rObj.SetMergedItem( XFillColorItem( "", aFillColor ) );
2798 }
2799 break;
2800 }
2801 case drawing::FillStyle_GRADIENT:
2802 {
2803 XGradient aXGradient(rObj.GetMergedItem(XATTR_FILLGRADIENT).GetGradientValue());
2804
2805 if ( nColorCount || 0.0 != dBrightness )
2806 {
2807 aXGradient.SetStartColor(
2809 aXGradient.GetStartColor(),
2810 std::min(nColorIndex, nColorCount-1),
2811 dBrightness ));
2812 aXGradient.SetEndColor(
2814 aXGradient.GetEndColor(),
2815 std::min(nColorIndex, nColorCount-1),
2816 dBrightness ));
2817 }
2818
2819 rObj.SetMergedItem( XFillGradientItem( "", aXGradient ) );
2820 break;
2821 }
2822 case drawing::FillStyle_HATCH:
2823 {
2824 XHatch aXHatch(rObj.GetMergedItem(XATTR_FILLHATCH).GetHatchValue());
2825
2826 if ( nColorCount || 0.0 != dBrightness )
2827 {
2828 aXHatch.SetColor(
2830 aXHatch.GetColor(),
2831 std::min(nColorIndex, nColorCount-1),
2832 dBrightness ));
2833 }
2834
2835 rObj.SetMergedItem( XFillHatchItem( "", aXHatch ) );
2836 break;
2837 }
2838 case drawing::FillStyle_BITMAP:
2839 {
2840 if ( nColorCount || 0.0 != dBrightness )
2841 {
2842 BitmapEx aBitmap(rObj.GetMergedItem(XATTR_FILLBITMAP).GetGraphicObject().GetGraphic().GetBitmapEx());
2843
2844 short nLuminancePercent = static_cast< short > ( GetLuminanceChange(
2845 std::min(nColorIndex, nColorCount-1)));
2846 aBitmap.Adjust( nLuminancePercent, 0, 0, 0, 0 );
2847
2848 rObj.SetMergedItem(XFillBitmapItem(OUString(), Graphic(aBitmap)));
2849 }
2850
2851 break;
2852 }
2853 }
2854
2855 if ( nColorIndex < nColorCount )
2856 nColorIndex++;
2857}
2858
2860{
2861 if ( !seqCoordinates.hasElements() )
2862 {
2863 return nullptr;
2864 }
2865
2866 std::vector< std::pair< rtl::Reference<SdrPathObj>, double > > vObjectList;
2867 const bool bSortFilledObjectsToBack(SortFilledObjectsToBackByDefault(eSpType));
2868 sal_Int32 nSubPathIndex(0);
2869 sal_Int32 nSrcPt(0);
2870 sal_Int32 nSegmentInd(0);
2872
2873 while( nSegmentInd <= seqSegments.getLength() )
2874 {
2876 nSrcPt,
2877 nSegmentInd,
2878 vObjectList,
2879 bLineGeometryNeededOnly,
2880 bSortFilledObjectsToBack,
2881 nSubPathIndex);
2882 nSubPathIndex++;
2883 }
2884
2885 if ( !vObjectList.empty() )
2886 {
2887 const SfxItemSet& rCustomShapeSet(mrSdrObjCustomShape.GetMergedItemSet());
2888 const sal_uInt32 nColorCount(nColorData >> 28);
2889 sal_uInt32 nColorIndex(0);
2890
2891 // #i37011# remove invisible objects
2892 std::vector< std::pair< rtl::Reference<SdrPathObj>, double> > vNewList;
2893
2894 for ( std::pair< rtl::Reference<SdrPathObj>, double >& rCandidate : vObjectList )
2895 {
2896 SdrPathObj* pObj(rCandidate.first.get());
2897 const drawing::LineStyle eLineStyle(pObj->GetMergedItem(XATTR_LINESTYLE).GetValue());
2898 const drawing::FillStyle eFillStyle(pObj->GetMergedItem(XATTR_FILLSTYLE).GetValue());
2899 const auto pText = pObj->getActiveText();
2900
2901 // #i40600# if bLineGeometryNeededOnly is set, linestyle does not matter
2902 if(pText || bLineGeometryNeededOnly || (drawing::LineStyle_NONE != eLineStyle) || (drawing::FillStyle_NONE != eFillStyle))
2903 vNewList.push_back(std::move(rCandidate));
2904 }
2905
2906 vObjectList = std::move(vNewList);
2907
2908 if(1 == vObjectList.size())
2909 {
2910 // a single object, correct some values
2912 *vObjectList.begin()->first,
2913 vObjectList.begin()->second,
2914 rCustomShapeSet,
2915 nColorIndex,
2916 nColorCount);
2917 }
2918 else
2919 {
2920 sal_Int32 nLineObjectCount(0);
2921
2922 // correct some values and collect content data
2923 for ( const std::pair< rtl::Reference<SdrPathObj>, double >& rCandidate : vObjectList )
2924 {
2925 SdrPathObj* pObj(rCandidate.first.get());
2926
2927 if(pObj->IsLine())
2928 {
2929 nLineObjectCount++;
2930 }
2931 else
2932 {
2934 *pObj,
2935 rCandidate.second,
2936 rCustomShapeSet,
2937 nColorIndex,
2938 nColorCount);
2939
2940 // OperationSmiley: when we have access to the SdrObjCustomShape and the
2941 // CustomShape is built with more than a single filled Geometry, use it
2942 // to define that all helper geometries defined here (SdrObjects currently)
2943 // will use the same FillGeometryDefinition (from the referenced SdrObjCustomShape).
2944 // This will all same-filled objects look like filled smoothly with the same style.
2946 }
2947 }
2948
2949 // #i88870# correct line arrows for callouts
2950 if ( nLineObjectCount )
2951 {
2953 eSpType,
2954 nLineObjectCount,
2955 vObjectList);
2956 }
2957
2958 // sort objects so that filled ones are in front. Necessary
2959 // for some strange objects
2960 if(bSortFilledObjectsToBack)
2961 {
2962 std::vector< std::pair< rtl::Reference<SdrPathObj>, double> > vTempList;
2963 vTempList.reserve(vObjectList.size());
2964
2965 for ( std::pair< rtl::Reference<SdrPathObj>, double >& rCandidate : vObjectList )
2966 {
2967 SdrPathObj* pObj(rCandidate.first.get());
2968 if ( !pObj->IsLine() )
2969 vTempList.push_back(std::move(rCandidate));
2970 }
2971
2972 for ( std::pair< rtl::Reference<SdrPathObj>, double >& rCandidate : vObjectList )
2973 {
2974 if ( rCandidate.first )
2975 vTempList.push_back(std::move(rCandidate));
2976 }
2977
2978 vObjectList = std::move(vTempList);
2979 }
2980 }
2981 }
2982
2983 // #i37011#
2984 if(!vObjectList.empty())
2985 {
2986 // copy remaining objects to pRet
2987 if(vObjectList.size() > 1)
2988 {
2990
2991 for ( std::pair< rtl::Reference<SdrPathObj>, double >& rCandidate : vObjectList )
2992 {
2993 pRet->GetSubList()->NbcInsertObject(rCandidate.first.get());
2994 }
2995 }
2996 else if(1 == vObjectList.size())
2997 {
2998 pRet = vObjectList.begin()->first;
2999 }
3000
3001 if(pRet)
3002 {
3003 // move to target position
3004 tools::Rectangle aCurRect(pRet->GetSnapRect());
3005 aCurRect.Move(aLogicRect.Left(), aLogicRect.Top());
3006 pRet->NbcSetSnapRect(aCurRect);
3007 }
3008 }
3009
3010 return pRet;
3011}
3012
3014{
3016
3017 if ( eSpType == mso_sptRectangle )
3018 {
3020 pRet->SetMergedItemSet( *this );
3021 }
3022 if ( !pRet )
3023 pRet = CreatePathObj( bLineGeometryNeededOnly );
3024
3025 return pRet;
3026}
3027
3029{
3030 if ( !pObj )
3031 return;
3032
3033 for ( const auto& rGluePoint : std::as_const(seqGluePoints) )
3034 {
3035 SdrGluePoint aGluePoint;
3036
3037 aGluePoint.SetPos( GetPoint( rGluePoint, true, true ) );
3038 aGluePoint.SetPercent( false );
3041 SdrGluePointList* pList = pObj->ForceGluePointList();
3042 if( pList )
3043 /* sal_uInt16 nId = */ pList->Insert( aGluePoint );
3044 }
3045}
3046
3048{
3049 return CreateObject( true );
3050}
3051
3052
3053/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static void CorrectCalloutArrows(MSO_SPT eSpType, sal_uInt32 nLineObjectCount, std::vector< std::pair< rtl::Reference< SdrPathObj >, double > > &vObjectList)
static double lcl_getNormalizedAngleRad(const double fCircleAngleDeg)
static double lcl_getYAdjustmentValue(std::u16string_view rShapeType, const sal_uInt32 nHandleIndex, const double fY, const double fW, const double fH)
static double lcl_getXAdjustmentValue(std::u16string_view rShapeType, const sal_uInt32 nHandleIndex, const double fX, const double fW, const double fH)
static double lcl_getAngleInOOXMLUnit(double fDY, double fDX)
static double lcl_getNormalizedCircleAngleRad(const double fWR, const double fHR, const double fEllipseAngleDeg)
static basegfx::B2DPolygon CreateArc(const tools::Rectangle &rRect, const Point &rStart, const Point &rEnd, const bool bClockwise)
static double lcl_getRadiusDistance(double fWR, double fHR, double fX, double fY)
@ RADIUS_RANGE_MAXIMUM
@ RADIUS_RANGE_MINIMUM
bool SortFilledObjectsToBackByDefault(MSO_SPT eSpType)
OptionalString sType
bool Adjust(short nLuminancePercent, short nContrastPercent, short nChannelRPercent, short nChannelGPercent, short nChannelBPercent, double fGamma=1.0, bool bInvert=false, bool msoBrightness=false)
sal_uInt8 GetBlue() const
sal_uInt8 GetRed() const
sal_uInt8 GetGreen() const
rtl::Reference< SdrObject > CreateLineGeometry()
css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > seqGluePoints
bool SetHandleControllerPosition(const sal_uInt32 nIndex, const css::awt::Point &rPosition)
css::uno::Sequence< OUString > seqEquations
bool GetHandlePosition(const sal_uInt32 nIndex, Point &rReturnPosition) const
SAL_DLLPRIVATE void AdaptObjColor(SdrPathObj &rObj, double dBrightness, const SfxItemSet &rCustomShapeSet, sal_uInt32 &nColorIndex, sal_uInt32 nColorCount)
SAL_DLLPRIVATE Color GetColorData(const Color &rFillColor, sal_uInt32 nIndex, double dBrightness) const
SAL_DLLPRIVATE void CreateSubPath(sal_Int32 &rSrcPt, sal_Int32 &rSegmentInd, std::vector< std::pair< rtl::Reference< SdrPathObj >, double > > &rObjectList, bool bLineGeometryNeededOnly, bool bSortFilledObjectsToBack, sal_Int32 nIndex)
virtual ~EnhancedCustomShape2d() override
SAL_DLLPRIVATE basegfx::B2DPoint GetPointAsB2DPoint(const css::drawing::EnhancedCustomShapeParameterPair &, const bool bScale=true, const bool bReplaceGeoSize=false) const
void GetParameter(double &rParameterReturnValue, const css::drawing::EnhancedCustomShapeParameter &, const bool bReplaceGeoWidth, const bool bReplaceGeoHeight) const
css::uno::Sequence< css::beans::PropertyValues > seqHandles
SAL_DLLPRIVATE double GetEnumFunc(const EnhancedCustomShape::ExpressionFunct eVal) const
std::vector< std::shared_ptr< EnhancedCustomShape::ExpressionNode > > vNodesSharedPtr
SAL_DLLPRIVATE Point GetPoint(const css::drawing::EnhancedCustomShapeParameterPair &, const bool bScale=true, const bool bReplaceGeoSize=false) const
SAL_DLLPRIVATE void SetPathSize(sal_Int32 nIndex=0)
SAL_DLLPRIVATE rtl::Reference< SdrObject > CreatePathObj(bool bLineGeometryNeededOnly)
EnhancedCustomShape2d(SdrObjCustomShape &rSdrObjCustomShape)
rtl::Reference< SdrObject > CreateObject(bool bLineGeometryNeededOnly)
SAL_DLLPRIVATE double GetAdjustValueAsDouble(const sal_Int32 nIndex) const
css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > seqTextFrames
static SAL_DLLPRIVATE bool ConvertSequenceToEnhancedCustomShape2dHandle(const css::beans::PropertyValues &rHandleProperties, EnhancedCustomShape2d::Handle &rDestinationHandle)
css::uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > seqAdjustmentValues
static void SetEnhancedCustomShapeParameter(css::drawing::EnhancedCustomShapeParameter &rParameter, const sal_Int32 nValue)
tools::Rectangle GetTextRect() const
static OUString GetEquation(const sal_uInt16 nFlags, sal_Int32 nPara1, sal_Int32 nPara2, sal_Int32 nPara3)
sal_Int32 GetLuminanceChange(sal_uInt32 nIndex) const
SAL_DLLPRIVATE void ApplyShapeAttributes(const SdrCustomShapeGeometryItem &rItem)
static SAL_DLLPRIVATE void SwapStartAndEndArrow(SdrObject *pObj)
static void SetEnhancedCustomShapeHandleParameter(css::drawing::EnhancedCustomShapeParameter &rParameter, const sal_Int32 nPara, const bool bIsSpecialValue, bool bHorz)
static SAL_DLLPRIVATE void AppendEnhancedCustomShapeEquationParameter(OUString &rParameter, const sal_Int32 nPara, const bool bIsSpecialValue)
std::vector< EquationResult > vEquationResults
css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > seqSegments
SAL_DLLPRIVATE double GetEquationValueAsDouble(const sal_Int32 nIndex) const
SAL_DLLPRIVATE bool SetAdjustValueAsDouble(const double &rValue, const sal_Int32 nIndex)
css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > seqCoordinates
void ApplyGluePoints(SdrObject *pObj)
SdrObjCustomShape & mrSdrObjCustomShape
css::uno::Sequence< css::awt::Size > seqSubViewSize
The transformation of a rectangle into a polygon, by using angle parameters from GeoStat.
Definition: svdtrans.hxx:201
double mfTanShearAngle
Definition: svdtrans.hxx:205
Degree100 nShearAngle
Definition: svdtrans.hxx:204
virtual const tools::Rectangle & GetSnapRect() const override
Definition: svdoattr.cxx:49
void SetPropertyValue(const css::beans::PropertyValue &rPropVal)
css::uno::Any * GetPropertyValueByName(const OUString &rPropName)
sal_uInt16 Insert(const SdrGluePoint &rGP)
Definition: svdglue.cxx:295
void SetPercent(bool bOn)
Definition: svdglue.hxx:124
void SetEscDir(SdrEscapeDirection nNewEsc)
Definition: svdglue.hxx:108
void SetPos(const Point &rNewPos)
Definition: svdglue.hxx:100
void SetAlign(SdrAlign nAlg)
Definition: svdglue.hxx:145
double GetObjectRotation() const
Definition: svdoashp.hxx:141
Abstract DrawObject.
Definition: svdobj.hxx:257
const SfxPoolItem & GetMergedItem(const sal_uInt16 nWhich) const
Definition: svdobj.cxx:2007
virtual SdrGluePointList * ForceGluePointList()
Definition: svdobj.cxx:2317
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:284
const SfxItemSet & GetMergedItemSet() const
Definition: svdobj.cxx:1972
void ClearMergedItem(const sal_uInt16 nWhich=0)
Definition: svdobj.cxx:1987
void SetMergedItem(const SfxPoolItem &rItem)
Definition: svdobj.cxx:1982
void setFillGeometryDefiningShape(const SdrObject *pNew)
Definition: svdobj.hxx:285
bool IsLine() const
Definition: svdopath.hxx:147
Rectangle objects (rectangle, circle, ...)
Definition: svdorect.hxx:39
virtual SdrText * getActiveText() const
returns the currently active text.
Definition: svdotext.cxx:2048
virtual const tools::Rectangle & GetLogicRect() const override
Definition: svdotxtr.cxx:69
const GeoStat & GetGeoStat() const
Definition: svdotext.hxx:390
sal_uInt16 ClearItem(sal_uInt16 nWhich=0)
const SfxPoolItem * Put(const SfxPoolItem &rItem, sal_uInt16 nWhich)
const SfxPoolItem & Get(sal_uInt16 nWhich, bool bSrchInParent=true) const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
const Color & GetEndColor() const
Definition: xgrad.hxx:70
const Color & GetStartColor() const
Definition: xgrad.hxx:69
void SetStartColor(const Color &rColor)
Definition: xgrad.hxx:58
void SetEndColor(const Color &rColor)
Definition: xgrad.hxx:59
const Color & GetColor() const
Definition: xhatch.hxx:52
void SetColor(const Color &rColor)
Definition: xhatch.hxx:47
void SetLineEndValue(const basegfx::B2DPolyPolygon &rPolyPolygon)
Definition: xlnedit.hxx:54
void SetLineStartValue(const basegfx::B2DPolyPolygon &rPolyPolygon)
Definition: xlnstit.hxx:54
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
void setClosed(bool bNew)
sal_uInt32 count() const
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const
void transform(const basegfx::B2DHomMatrix &rMatrix)
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
sal_uInt32 count() const
void setClosed(bool bNew)
void appendBezierSegment(const basegfx::B2DPoint &rNextControlPoint, const basegfx::B2DPoint &rPrevControlPoint, const basegfx::B2DPoint &rPoint)
double getBlue() const
void setBlue(double fNew)
double getRed() const
void setGreen(double fNew)
double getGreen() const
TYPE getX() const
TYPE getY() const
sal_uInt16 GetSize() const
constexpr Point Center() const
constexpr tools::Long GetWidth() const
constexpr void SetLeft(tools::Long v)
constexpr void SetTop(tools::Long v)
constexpr tools::Long Top() const
constexpr void SetRight(tools::Long v)
constexpr Size GetSize() const
void Move(tools::Long nHorzMoveDelta, tools::Long nVertMoveDelta)
constexpr tools::Long Right() const
constexpr void SetBottom(tools::Long v)
constexpr tools::Long GetHeight() const
constexpr tools::Long Left() const
constexpr tools::Long Bottom() const
int nCount
#define DBG_ASSERT(sCon, aError)
double toRadians(D x)
float u
UNKNOWN
sal_Int16 nValue
Definition: fmsrccfg.cxx:81
tools::Long FRound(double fVal)
sal_Int32 nIndex
uno_Any a
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
#define DFF_Prop_geoTop
Definition: msdffdef.hxx:146
#define DFF_Prop_adjust4Value
Definition: msdffdef.hxx:154
#define DFF_Prop_geoRight
Definition: msdffdef.hxx:147
#define DFF_Prop_adjust2Value
Definition: msdffdef.hxx:152
#define DFF_Prop_adjust3Value
Definition: msdffdef.hxx:153
#define DFF_Prop_geoLeft
Definition: msdffdef.hxx:145
#define DFF_Prop_adjustValue
Definition: msdffdef.hxx:151
MSO_SPT
Definition: msdffdef.hxx:274
@ mso_sptAccentCallout90
Definition: msdffdef.hxx:455
@ mso_sptCurvedUpArrow
Definition: msdffdef.hxx:380
@ mso_sptCallout3
Definition: msdffdef.hxx:319
@ mso_sptActionButtonBlank
Definition: msdffdef.hxx:465
@ mso_sptBorderCallout2
Definition: msdffdef.hxx:324
@ mso_sptAccentBorderCallout2
Definition: msdffdef.hxx:327
@ mso_sptAccentBorderCallout1
Definition: msdffdef.hxx:326
@ mso_sptActionButtonMovie
Definition: msdffdef.hxx:476
@ mso_sptEllipseRibbon2
Definition: msdffdef.hxx:384
@ mso_sptCallout1
Definition: msdffdef.hxx:317
@ mso_sptActionButtonSound
Definition: msdffdef.hxx:475
@ mso_sptBorderCallout3
Definition: msdffdef.hxx:325
@ mso_sptCallout2
Definition: msdffdef.hxx:318
@ mso_sptRibbon2
Definition: msdffdef.hxx:330
@ mso_sptSmileyFace
Definition: msdffdef.hxx:372
@ mso_sptAccentCallout1
Definition: msdffdef.hxx:320
@ mso_sptMin
Definition: msdffdef.hxx:275
@ mso_sptBevel
Definition: msdffdef.hxx:360
@ mso_sptActionButtonReturn
Definition: msdffdef.hxx:473
@ mso_sptAccentBorderCallout90
Definition: msdffdef.hxx:457
@ mso_sptHorizontalScroll
Definition: msdffdef.hxx:374
@ mso_sptAccentBorderCallout3
Definition: msdffdef.hxx:328
@ mso_sptActionButtonHome
Definition: msdffdef.hxx:466
@ mso_sptCurvedRightArrow
Definition: msdffdef.hxx:378
@ mso_sptFoldedCorner
Definition: msdffdef.hxx:341
@ mso_sptCallout90
Definition: msdffdef.hxx:454
@ mso_sptCurvedLeftArrow
Definition: msdffdef.hxx:379
@ mso_sptActionButtonDocument
Definition: msdffdef.hxx:474
@ mso_sptCan
Definition: msdffdef.hxx:298
@ mso_sptEllipseRibbon
Definition: msdffdef.hxx:383
@ mso_sptBorderCallout90
Definition: msdffdef.hxx:456
@ mso_sptCube
Definition: msdffdef.hxx:292
@ mso_sptRectangle
Definition: msdffdef.hxx:277
@ mso_sptActionButtonForwardNext
Definition: msdffdef.hxx:469
@ mso_sptActionButtonBeginning
Definition: msdffdef.hxx:472
@ mso_sptActionButtonInformation
Definition: msdffdef.hxx:468
@ mso_sptNil
Definition: msdffdef.hxx:482
@ mso_sptActionButtonEnd
Definition: msdffdef.hxx:471
@ mso_sptActionButtonHelp
Definition: msdffdef.hxx:467
@ mso_sptCurvedDownArrow
Definition: msdffdef.hxx:381
@ mso_sptVerticalScroll
Definition: msdffdef.hxx:373
@ mso_sptAccentCallout2
Definition: msdffdef.hxx:321
@ mso_sptActionButtonBackPrevious
Definition: msdffdef.hxx:470
@ mso_sptBorderCallout1
Definition: msdffdef.hxx:323
@ mso_sptRibbon
Definition: msdffdef.hxx:329
@ mso_sptAccentCallout3
Definition: msdffdef.hxx:322
#define DFF_Prop_geoBottom
Definition: msdffdef.hxx:148
#define DFF_Prop_adjust6Value
Definition: msdffdef.hxx:156
#define DFF_Prop_adjust8Value
Definition: msdffdef.hxx:158
#define DFF_Prop_adjust9Value
Definition: msdffdef.hxx:159
#define DFF_Prop_adjust10Value
Definition: msdffdef.hxx:160
#define DFF_Prop_adjust7Value
Definition: msdffdef.hxx:157
#define DFF_Prop_adjust5Value
Definition: msdffdef.hxx:155
SVXCORE_DLLPUBLIC MSO_SPT Get(const OUString &)
bool equalZero(const T &rfVal)
BColor hsv2rgb(const BColor &rHSVColor)
BColor rgb2hsv(const BColor &rRGBColor)
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
B2DPolygon createPolygonFromEllipseSegment(const B2DPoint &rCenter, double fRadiusX, double fRadiusY, double fStart, double fEnd)
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
B2DPolygon createPolygonFromEllipse(const B2DPoint &rCenter, double fRadiusX, double fRadiusY, sal_uInt32 nStartQuadrant=0)
void checkClosed(B2DPolygon &rCandidate)
constexpr double rad2deg(double v)
constexpr double deg2rad(double v)
ExpressionFunct
Value
int i
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
sal_uInt32 toUInt32(std::u16string_view str, sal_Int16 radix=10)
long Long
SwNodeOffset abs(const SwNodeOffset &a)
const char GetValue[]
#define Y
SdrOnOffItem makeSdrShadowItem(bool bShadow)
Definition: sdshitm.hxx:25
css::drawing::EnhancedCustomShapeParameter aXRangeMinimum
css::drawing::EnhancedCustomShapeParameter aYRangeMaximum
css::drawing::EnhancedCustomShapeParameter aXRangeMaximum
css::drawing::EnhancedCustomShapeParameterPair aPosition
css::drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum
css::drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum
css::drawing::EnhancedCustomShapeParameter aYRangeMinimum
css::drawing::EnhancedCustomShapeParameterPair aPolar
This exception is thrown, when the arithmetic expression parser failed to parse a string.
constexpr TypedWhichId< SdrOnOffItem > SDRATTR_SHADOW(SDRATTR_SHADOW_FIRST+0)
constexpr TypedWhichId< SdrCustomShapeGeometryItem > SDRATTR_CUSTOMSHAPE_GEOMETRY(SDRATTR_CUSTOMSHAPE_FIRST+2)
constexpr TypedWhichId< SvxWritingModeItem > SDRATTR_TEXTDIRECTION(SDRATTR_NOTPERSIST_FIRST+34)
@ Polygon
circle cut
@ PolyLine
polygon, PolyPolygon
void ShearPoint(Point &rPnt, const Point &rRef, double tn, bool bVShear=false)
Definition: svdtrans.hxx:109
void RotatePoint(Point &rPnt, const Point &rRef, double sn, double cs)
Definition: svdtrans.hxx:101
unsigned char sal_uInt8
#define SAL_MAX_INT32
constexpr TypedWhichId< XFillColorItem > XATTR_FILLCOLOR(XATTR_FILL_FIRST+1)
constexpr TypedWhichId< XLineStartWidthItem > XATTR_LINESTARTWIDTH(XATTR_LINE_FIRST+6)
constexpr TypedWhichId< XLineEndItem > XATTR_LINEEND(XATTR_LINE_FIRST+5)
constexpr TypedWhichId< XLineStartItem > XATTR_LINESTART(XATTR_LINE_FIRST+4)
constexpr TypedWhichId< XLineEndCenterItem > XATTR_LINEENDCENTER(XATTR_LINE_FIRST+9)
constexpr TypedWhichId< XFillHatchItem > XATTR_FILLHATCH(XATTR_FILL_FIRST+3)
constexpr TypedWhichId< XLineStartCenterItem > XATTR_LINESTARTCENTER(XATTR_LINE_FIRST+8)
constexpr TypedWhichId< XFillBitmapItem > XATTR_FILLBITMAP(XATTR_FILL_FIRST+4)
constexpr TypedWhichId< XLineStyleItem > XATTR_LINESTYLE(XATTR_LINE_FIRST)
constexpr TypedWhichId< XFillStyleItem > XATTR_FILLSTYLE(XATTR_FILL_FIRST)
constexpr TypedWhichId< XFillGradientItem > XATTR_FILLGRADIENT(XATTR_FILL_FIRST+2)
constexpr TypedWhichId< XLineEndWidthItem > XATTR_LINEENDWIDTH(XATTR_LINE_FIRST+7)
sal_Int32 nLength