1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <o3tl/string_view.hxx>
23 #include <svx/EnhancedCustomShape2d.hxx>
24 #include <svx/EnhancedCustomShapeGeometry.hxx>
25 #include <svx/EnhancedCustomShapeTypeNames.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/xhatch.hxx>
45 #include <svx/sdshitm.hxx>
46 #include <com/sun/star/awt/Size.hpp>
47 #include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
48 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
49 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
50 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
51 #include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
52 #include <basegfx/numeric/ftools.hxx>
53 #include <basegfx/color/bcolortools.hxx>
54 #include <basegfx/polygon/b2dpolygon.hxx>
55 #include <basegfx/polygon/b2dpolygontools.hxx>
56 #include <basegfx/matrix/b2dhommatrixtools.hxx>
57 #include <sal/log.hxx>
61 #include <string_view>
62 #include <unordered_set>
64 using namespace ::com::sun::star
;
65 using namespace ::com::sun::star::uno
;
66 using namespace ::com::sun::star::drawing
;
67 using namespace ::com::sun::star::drawing::EnhancedCustomShapeSegmentCommand
;
69 void EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( EnhancedCustomShapeParameter
& rParameter
, const sal_Int32 nValue
)
71 sal_uInt32 nDat
= static_cast<sal_uInt32
>(nValue
);
72 sal_Int32 nNewValue
= nValue
;
74 // check if this is a special point
75 if ( ( nDat
>> 16 ) == 0x8000 )
77 nNewValue
= static_cast<sal_uInt16
>(nDat
);
78 rParameter
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
81 rParameter
.Type
= EnhancedCustomShapeParameterType::NORMAL
;
82 rParameter
.Value
<<= nNewValue
;
85 OUString
EnhancedCustomShape2d::GetEquation( const sal_uInt16 nFlags
, sal_Int32 nP1
, sal_Int32 nP2
, sal_Int32 nP3
)
88 bool b1Special
= ( nFlags
& 0x2000 ) != 0;
89 bool b2Special
= ( nFlags
& 0x4000 ) != 0;
90 bool b3Special
= ( nFlags
& 0x8000 ) != 0;
91 switch( nFlags
& 0xff )
96 sal_Int32 nOptimize
= 0;
112 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
117 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
121 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
123 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
127 if ( b3Special
|| nP3
)
130 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
136 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
137 if ( b2Special
|| ( nP2
!= 1 ) )
140 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
142 if ( b3Special
|| ( ( nP3
!= 1 ) && ( nP3
!= 0 ) ) )
145 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
152 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
154 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
161 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
168 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
170 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
177 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
179 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
186 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
188 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
190 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
196 aEquation
+= "sqrt(";
197 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
199 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
201 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
203 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
205 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
207 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
213 aEquation
+= "atan2(";
214 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
216 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
217 aEquation
+= ")/(pi/180)";
222 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
223 aEquation
+= "*sin(";
224 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
225 aEquation
+= "*(pi/180))";
230 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
231 aEquation
+= "*cos(";
232 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
233 aEquation
+= "*(pi/180))";
238 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
239 aEquation
+= "*cos(atan2(";
240 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
242 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
248 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
249 aEquation
+= "*sin(atan2(";
250 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
252 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
258 aEquation
+= "sqrt(";
259 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
265 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
266 aEquation
+= "*sqrt(1-(";
267 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
269 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
272 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
274 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
280 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
281 aEquation
+= "*tan(";
282 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
288 aEquation
+= "sqrt(";
289 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
291 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
293 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
295 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
301 aEquation
+= "(cos(";
302 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
303 aEquation
+= "*(pi/180))*(";
304 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
305 aEquation
+= "-10800)+sin(";
306 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
307 aEquation
+= "*(pi/180))*(";
308 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
309 aEquation
+= "-10800))+10800";
314 aEquation
+= "-(sin(";
315 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
316 aEquation
+= "*(pi/180))*(";
317 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP1
, b1Special
);
318 aEquation
+= "-10800)-cos(";
319 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP3
, b3Special
);
320 aEquation
+= "*(pi/180))*(";
321 EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation
, nP2
, b2Special
);
322 aEquation
+= "-10800))+10800";
329 void EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( OUString
& rParameter
, const sal_Int32 nPara
, const bool bIsSpecialValue
)
331 if ( bIsSpecialValue
)
336 rParameter
+= OUString::number( nPara
& 0xff );
343 case DFF_Prop_adjustValue
:
344 case DFF_Prop_adjust2Value
:
345 case DFF_Prop_adjust3Value
:
346 case DFF_Prop_adjust4Value
:
347 case DFF_Prop_adjust5Value
:
348 case DFF_Prop_adjust6Value
:
349 case DFF_Prop_adjust7Value
:
350 case DFF_Prop_adjust8Value
:
351 case DFF_Prop_adjust9Value
:
352 case DFF_Prop_adjust10Value
:
355 rParameter
+= OUString::number( nPara
- DFF_Prop_adjustValue
);
359 case DFF_Prop_geoLeft
:
361 rParameter
+= "left";
364 case DFF_Prop_geoTop
:
369 case DFF_Prop_geoRight
:
371 rParameter
+= "right";
374 case DFF_Prop_geoBottom
:
376 rParameter
+= "bottom";
384 rParameter
+= OUString::number( nPara
);
388 void EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( EnhancedCustomShapeParameter
& rParameter
, const sal_Int32 nPara
, const bool bIsSpecialValue
, bool bHorz
)
390 sal_Int32 nValue
= 0;
391 if ( bIsSpecialValue
)
393 if ( ( nPara
>= 0x100 ) && ( nPara
<= 0x107 ) )
395 nValue
= nPara
& 0xff;
396 rParameter
.Type
= EnhancedCustomShapeParameterType::ADJUSTMENT
;
398 else if ( ( nPara
>= 3 ) && ( nPara
<= 0x82 ) )
401 rParameter
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
403 else if ( nPara
== 0 )
407 rParameter
.Type
= EnhancedCustomShapeParameterType::LEFT
;
409 rParameter
.Type
= EnhancedCustomShapeParameterType::TOP
;
411 else if ( nPara
== 1 )
415 rParameter
.Type
= EnhancedCustomShapeParameterType::RIGHT
;
417 rParameter
.Type
= EnhancedCustomShapeParameterType::BOTTOM
;
419 else if ( nPara
== 2 ) // means to be centered, but should not be
420 { // used in our implementation
422 rParameter
.Type
= EnhancedCustomShapeParameterType::NORMAL
;
427 rParameter
.Type
= EnhancedCustomShapeParameterType::NORMAL
;
433 rParameter
.Type
= EnhancedCustomShapeParameterType::NORMAL
;
435 rParameter
.Value
<<= nValue
;
438 bool EnhancedCustomShape2d::ConvertSequenceToEnhancedCustomShape2dHandle(
439 const css::beans::PropertyValues
& rHandleProperties
,
440 EnhancedCustomShape2d::Handle
& rDestinationHandle
)
442 bool bRetValue
= false;
443 if ( rHandleProperties
.hasElements() )
445 rDestinationHandle
.nFlags
= HandleFlags::NONE
;
446 for ( const css::beans::PropertyValue
& rPropVal
: rHandleProperties
)
448 if ( rPropVal
.Name
== "Position" )
450 if ( rPropVal
.Value
>>= rDestinationHandle
.aPosition
)
453 else if ( rPropVal
.Name
== "MirroredX" )
456 if ( rPropVal
.Value
>>= bMirroredX
)
459 rDestinationHandle
.nFlags
|= HandleFlags::MIRRORED_X
;
462 else if ( rPropVal
.Name
== "MirroredY" )
465 if ( rPropVal
.Value
>>= bMirroredY
)
468 rDestinationHandle
.nFlags
|= HandleFlags::MIRRORED_Y
;
471 else if ( rPropVal
.Name
== "Switched" )
474 if ( rPropVal
.Value
>>= bSwitched
)
477 rDestinationHandle
.nFlags
|= HandleFlags::SWITCHED
;
480 else if ( rPropVal
.Name
== "Polar" )
482 if ( rPropVal
.Value
>>= rDestinationHandle
.aPolar
)
483 rDestinationHandle
.nFlags
|= HandleFlags::POLAR
;
485 else if ( rPropVal
.Name
== "RefX" )
487 if ( rPropVal
.Value
>>= rDestinationHandle
.nRefX
)
488 rDestinationHandle
.nFlags
|= HandleFlags::REFX
;
490 else if ( rPropVal
.Name
== "RefY" )
492 if ( rPropVal
.Value
>>= rDestinationHandle
.nRefY
)
493 rDestinationHandle
.nFlags
|= HandleFlags::REFY
;
495 else if ( rPropVal
.Name
== "RefAngle" )
497 if ( rPropVal
.Value
>>= rDestinationHandle
.nRefAngle
)
498 rDestinationHandle
.nFlags
|= HandleFlags::REFANGLE
;
500 else if ( rPropVal
.Name
== "RefR" )
502 if ( rPropVal
.Value
>>= rDestinationHandle
.nRefR
)
503 rDestinationHandle
.nFlags
|= HandleFlags::REFR
;
505 else if ( rPropVal
.Name
== "RadiusRangeMinimum" )
507 if ( rPropVal
.Value
>>= rDestinationHandle
.aRadiusRangeMinimum
)
508 rDestinationHandle
.nFlags
|= HandleFlags::RADIUS_RANGE_MINIMUM
;
510 else if ( rPropVal
.Name
== "RadiusRangeMaximum" )
512 if ( rPropVal
.Value
>>= rDestinationHandle
.aRadiusRangeMaximum
)
513 rDestinationHandle
.nFlags
|= HandleFlags::RADIUS_RANGE_MAXIMUM
;
515 else if ( rPropVal
.Name
== "RangeXMinimum" )
517 if ( rPropVal
.Value
>>= rDestinationHandle
.aXRangeMinimum
)
518 rDestinationHandle
.nFlags
|= HandleFlags::RANGE_X_MINIMUM
;
520 else if ( rPropVal
.Name
== "RangeXMaximum" )
522 if ( rPropVal
.Value
>>= rDestinationHandle
.aXRangeMaximum
)
523 rDestinationHandle
.nFlags
|= HandleFlags::RANGE_X_MAXIMUM
;
525 else if ( rPropVal
.Name
== "RangeYMinimum" )
527 if ( rPropVal
.Value
>>= rDestinationHandle
.aYRangeMinimum
)
528 rDestinationHandle
.nFlags
|= HandleFlags::RANGE_Y_MINIMUM
;
530 else if ( rPropVal
.Name
== "RangeYMaximum" )
532 if ( rPropVal
.Value
>>= rDestinationHandle
.aYRangeMaximum
)
533 rDestinationHandle
.nFlags
|= HandleFlags::RANGE_Y_MAXIMUM
;
540 void EnhancedCustomShape2d::ApplyShapeAttributes( const SdrCustomShapeGeometryItem
& rGeometryItem
)
543 static constexpr OUStringLiteral
sAdjustmentValues( u
"AdjustmentValues" );
544 const Any
* pAny
= const_cast<SdrCustomShapeGeometryItem
&>(rGeometryItem
).GetPropertyValueByName( sAdjustmentValues
);
546 *pAny
>>= seqAdjustmentValues
;
550 static constexpr OUStringLiteral
sViewBox( u
"ViewBox" );
551 const Any
* pViewBox
= const_cast<SdrCustomShapeGeometryItem
&>(rGeometryItem
).GetPropertyValueByName( sViewBox
);
552 css::awt::Rectangle aViewBox
;
553 if ( pViewBox
&& (*pViewBox
>>= aViewBox
) )
555 nCoordLeft
= aViewBox
.X
;
556 nCoordTop
= aViewBox
.Y
;
557 nCoordWidthG
= std::abs( aViewBox
.Width
);
558 nCoordHeightG
= std::abs( aViewBox
.Height
);
560 static constexpr OUStringLiteral
sPath( u
"Path" );
561 static constexpr OUStringLiteral
sCoordinates( u
"Coordinates" );
562 static constexpr OUStringLiteral
sGluePoints( u
"GluePoints" );
563 static constexpr OUStringLiteral
sSegments( u
"Segments" );
564 static constexpr OUStringLiteral
sSubViewSize( u
"SubViewSize" );
565 static constexpr OUStringLiteral
sStretchX( u
"StretchX" );
566 static constexpr OUStringLiteral
sStretchY( u
"StretchY" );
567 static constexpr OUStringLiteral
sTextFrames( u
"TextFrames" );
568 static constexpr OUStringLiteral
sEquations( u
"Equations" );
569 static constexpr OUStringLiteral
sHandles( u
"Handles" );
573 pAny
= const_cast<SdrCustomShapeGeometryItem
&>(rGeometryItem
).GetPropertyValueByName( sPath
, sCoordinates
);
575 *pAny
>>= seqCoordinates
;
579 pAny
= const_cast<SdrCustomShapeGeometryItem
&>(rGeometryItem
).GetPropertyValueByName( sPath
, sGluePoints
);
581 *pAny
>>= seqGluePoints
;
585 pAny
= const_cast<SdrCustomShapeGeometryItem
&>(rGeometryItem
).GetPropertyValueByName( sPath
, sSegments
);
587 *pAny
>>= seqSegments
;
591 pAny
= const_cast<SdrCustomShapeGeometryItem
&>(rGeometryItem
).GetPropertyValueByName( sPath
, sSubViewSize
);
593 *pAny
>>= seqSubViewSize
;
597 pAny
= const_cast<SdrCustomShapeGeometryItem
&>(rGeometryItem
).GetPropertyValueByName( sPath
, sStretchX
);
600 sal_Int32 nStretchX
= 0;
601 if ( *pAny
>>= nStretchX
)
607 pAny
= const_cast<SdrCustomShapeGeometryItem
&>(rGeometryItem
).GetPropertyValueByName( sPath
, sStretchY
);
610 sal_Int32 nStretchY
= 0;
611 if ( *pAny
>>= nStretchY
)
617 pAny
= const_cast<SdrCustomShapeGeometryItem
&>(rGeometryItem
).GetPropertyValueByName( sPath
, sTextFrames
);
619 *pAny
>>= seqTextFrames
;
623 pAny
= const_cast<SdrCustomShapeGeometryItem
&>(rGeometryItem
).GetPropertyValueByName( sEquations
);
625 *pAny
>>= seqEquations
;
629 pAny
= const_cast<SdrCustomShapeGeometryItem
&>(rGeometryItem
).GetPropertyValueByName( sHandles
);
631 *pAny
>>= seqHandles
;
634 EnhancedCustomShape2d::~EnhancedCustomShape2d()
638 void EnhancedCustomShape2d::SetPathSize( sal_Int32 nIndex
)
640 sal_Int32 nWidth
= 0;
641 sal_Int32 nHeight
= 0;
643 if ( seqSubViewSize
.hasElements() && nIndex
< seqSubViewSize
.getLength() ) {
644 nWidth
= seqSubViewSize
[ nIndex
].Width
;
645 nHeight
= seqSubViewSize
[ nIndex
].Height
;
648 "set subpath " << nIndex
<< " size: " << nWidth
<< " x "
652 if ( nWidth
&& nHeight
) {
653 nCoordWidth
= nWidth
;
654 nCoordHeight
= nHeight
;
656 nCoordWidth
= nCoordWidthG
;
657 nCoordHeight
= nCoordHeightG
;
660 fXScale
= nCoordWidth
== 0 ? 0.0 : static_cast<double>(aLogicRect
.GetWidth()) / static_cast<double>(nCoordWidth
);
661 fYScale
= nCoordHeight
== 0 ? 0.0 : static_cast<double>(aLogicRect
.GetHeight()) / static_cast<double>(nCoordHeight
);
666 "ooxml shape, path width: " << nCoordWidth
<< " height: "
669 // Try to set up scale separately, if given only width or height
670 // This is possible case in OOXML when only width or height is non-zero
671 if ( nCoordWidth
== 0 )
674 fXScale
= static_cast<double>(aLogicRect
.GetWidth()) / static_cast<double>(nWidth
);
678 if ( nCoordHeight
== 0 )
681 fYScale
= static_cast<double>(aLogicRect
.GetHeight()) / static_cast<double>(nHeight
);
686 if ( static_cast<sal_uInt32
>(nXRef
) != 0x80000000 && aLogicRect
.GetHeight() )
688 fXRatio
= static_cast<double>(aLogicRect
.GetWidth()) / static_cast<double>(aLogicRect
.GetHeight());
696 if ( static_cast<sal_uInt32
>(nYRef
) != 0x80000000 && aLogicRect
.GetWidth() )
698 fYRatio
= static_cast<double>(aLogicRect
.GetHeight()) / static_cast<double>(aLogicRect
.GetWidth());
708 EnhancedCustomShape2d::EnhancedCustomShape2d(SdrObjCustomShape
& rSdrObjCustomShape
)
709 : SfxItemSet ( rSdrObjCustomShape
.GetMergedItemSet() ),
710 mrSdrObjCustomShape ( rSdrObjCustomShape
),
711 eSpType ( mso_sptNil
),
714 nCoordWidthG ( 21600 ),
715 nCoordHeightG ( 21600 ),
716 bOOXMLShape ( false ),
717 nXRef ( 0x80000000 ),
718 nYRef ( 0x80000000 ),
720 bFilled ( rSdrObjCustomShape
.GetMergedItem( XATTR_FILLSTYLE
).GetValue() != drawing::FillStyle_NONE
),
721 bStroked ( rSdrObjCustomShape
.GetMergedItem( XATTR_LINESTYLE
).GetValue() != drawing::LineStyle_NONE
),
725 // bTextFlow needs to be set before clearing the TextDirection Item
727 ClearItem( SDRATTR_TEXTDIRECTION
); //SJ: vertical writing is not required, by removing this item no outliner is created
729 // #i105323# For 2D AutoShapes, the shadow attribute does not need to be applied to any
730 // of the constructed helper SdrObjects. This would lead to problems since the shadow
731 // of one helper object would fall on one helper object behind it (e.g. with the
732 // eyes of the smiley shape). This is not wanted; instead a single shadow 'behind'
733 // the AutoShape visualisation is wanted. This is done with primitive functionality
734 // now in SdrCustomShapePrimitive2D::create2DDecomposition, but only for 2D objects
735 // (see there and in EnhancedCustomShape3d::Create3DObject to read more).
736 // This exception may be removed later when AutoShapes will create primitives directly.
737 // So, currently remove the ShadowAttribute from the ItemSet to not apply it to any
739 ClearItem(SDRATTR_SHADOW
);
741 Point
aP( mrSdrObjCustomShape
.GetSnapRect().Center() );
742 Size
aS( mrSdrObjCustomShape
.GetLogicRect().GetSize() );
743 aP
.AdjustX( -(aS
.Width() / 2) );
744 aP
.AdjustY( -(aS
.Height() / 2) );
745 aLogicRect
= tools::Rectangle( aP
, aS
);
748 const SdrCustomShapeGeometryItem
& rGeometryItem(mrSdrObjCustomShape
.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
));
749 static constexpr OUStringLiteral sType
= u
"Type";
750 const Any
* pAny
= rGeometryItem
.GetPropertyValueByName( sType
);
752 *pAny
>>= sShapeType
;
753 bOOXMLShape
= sShapeType
.startsWith("ooxml-");
754 SAL_INFO("svx", "shape type: " << sShapeType
<< " " << bOOXMLShape
);
756 eSpType
= EnhancedCustomShapeTypeNames::Get( sShapeType
);
758 static constexpr OUStringLiteral sMirroredX
= u
"MirroredX";
759 static constexpr OUStringLiteral sMirroredY
= u
"MirroredY";
760 pAny
= rGeometryItem
.GetPropertyValueByName( sMirroredX
);
763 pAny
= rGeometryItem
.GetPropertyValueByName( sMirroredY
);
767 nRotateAngle
= Degree100(static_cast<sal_Int32
>(mrSdrObjCustomShape
.GetObjectRotation() * 100.0));
769 /*const sal_Int32* pDefData =*/ ApplyShapeAttributes( rGeometryItem
);
774 case mso_sptCan
: nColorData
= 0x20400000; break;
775 case mso_sptCube
: nColorData
= 0x302e0000; break;
776 case mso_sptActionButtonBlank
: nColorData
= 0x502ce400; break;
777 case mso_sptActionButtonHome
: nColorData
= 0x702ce4ce; break;
778 case mso_sptActionButtonHelp
: nColorData
= 0x602ce4c0; break;
779 case mso_sptActionButtonInformation
: nColorData
= 0x702ce4c5; break;
780 case mso_sptActionButtonBackPrevious
: nColorData
= 0x602ce4c0; break;
781 case mso_sptActionButtonForwardNext
: nColorData
= 0x602ce4c0; break;
782 case mso_sptActionButtonBeginning
: nColorData
= 0x602ce4c0; break;
783 case mso_sptActionButtonEnd
: nColorData
= 0x602ce4c0; break;
784 case mso_sptActionButtonReturn
: nColorData
= 0x602ce4c0; break;
785 case mso_sptActionButtonDocument
: nColorData
= 0x702ce4ec; break;
786 case mso_sptActionButtonSound
: nColorData
= 0x602ce4c0; break;
787 case mso_sptActionButtonMovie
: nColorData
= 0x602ce4c0; break;
788 case mso_sptBevel
: nColorData
= 0x502ce400; break;
789 case mso_sptFoldedCorner
: nColorData
= 0x20e00000; break;
790 case mso_sptSmileyFace
: nColorData
= 0x20e00000; break;
793 // Because calculation method has changed in #i102797 original color encoding for
794 // Octagon Bevel and Diamond Bevel can no longer be used. We keep the color coding
795 // only for self-created shapes, as authors may have already considered the change.
796 // We use ColorData compatible to OOXML.
797 if (sShapeType
== "col-60da8460") // Octagon Bevel
799 nColorData
= 0x60ecc240;
801 else if (sShapeType
== "col-502ad400") // Diamond Bevel
803 nColorData
= 0x502ce400;
805 else if (sShapeType
.getLength() > 4 && sShapeType
.match( "col-" ))
807 nColorData
= o3tl::toUInt32(sShapeType
.subView( 4 ), 16);
811 case mso_sptCurvedLeftArrow
:
812 case mso_sptCurvedRightArrow
:
813 case mso_sptCurvedUpArrow
:
814 case mso_sptCurvedDownArrow
: nColorData
= 0x20d00000; break;
815 case mso_sptRibbon2
: nColorData
= 0x30ee0000; break;
816 case mso_sptRibbon
: nColorData
= 0x30ee0000; break;
818 case mso_sptEllipseRibbon2
: nColorData
= 0x30ee0000; break;
819 case mso_sptEllipseRibbon
: nColorData
= 0x30ee0000; break;
821 case mso_sptVerticalScroll
: nColorData
= 0x30ee0000; break;
822 case mso_sptHorizontalScroll
: nColorData
= 0x30ee0000; break;
827 sal_Int32 nLength
= seqEquations
.getLength();
832 vNodesSharedPtr
.resize( nLength
);
833 vEquationResults
.resize( nLength
);
834 for ( sal_Int32 i
= 0; i
< nLength
; i
++ )
836 vEquationResults
[ i
].bReady
= false;
839 vNodesSharedPtr
[ i
] = EnhancedCustomShape::FunctionParser::parseFunction( seqEquations
[ i
], *this );
841 catch ( EnhancedCustomShape::ParseError
& )
845 "error: equation number: " << i
<< ", parser failed ("
846 << seqEquations
[i
] << ")");
851 using EnhancedCustomShape::ExpressionFunct
;
853 double EnhancedCustomShape2d::GetEnumFunc( const ExpressionFunct eFunc
) const
858 case ExpressionFunct::EnumPi
: fRet
= M_PI
; break;
859 case ExpressionFunct::EnumLeft
: fRet
= static_cast<double>(nCoordLeft
); break;
860 case ExpressionFunct::EnumTop
: fRet
= static_cast<double>(nCoordTop
); break;
861 case ExpressionFunct::EnumRight
: fRet
= (static_cast<double>(nCoordLeft
) + static_cast<double>(nCoordWidth
)) * fXRatio
; break;
862 case ExpressionFunct::EnumBottom
: fRet
= (static_cast<double>(nCoordTop
) + static_cast<double>(nCoordHeight
)) * fYRatio
; break;
863 case ExpressionFunct::EnumXStretch
: fRet
= nXRef
; break;
864 case ExpressionFunct::EnumYStretch
: fRet
= nYRef
; break;
865 case ExpressionFunct::EnumHasStroke
: fRet
= bStroked
? 1.0 : 0.0; break;
866 case ExpressionFunct::EnumHasFill
: fRet
= bFilled
? 1.0 : 0.0; break;
867 case ExpressionFunct::EnumWidth
: fRet
= nCoordWidth
; break;
868 case ExpressionFunct::EnumHeight
: fRet
= nCoordHeight
; break;
869 case ExpressionFunct::EnumLogWidth
: fRet
= aLogicRect
.GetWidth(); break;
870 case ExpressionFunct::EnumLogHeight
: fRet
= aLogicRect
.GetHeight(); break;
875 double EnhancedCustomShape2d::GetAdjustValueAsDouble( const sal_Int32 nIndex
) const
877 double fNumber
= 0.0;
878 if ( nIndex
< seqAdjustmentValues
.getLength() )
880 if ( seqAdjustmentValues
[ nIndex
].Value
.getValueTypeClass() == TypeClass_DOUBLE
)
881 seqAdjustmentValues
[ nIndex
].Value
>>= fNumber
;
884 sal_Int32 nNumber
= 0;
885 seqAdjustmentValues
[ nIndex
].Value
>>= nNumber
;
886 fNumber
= static_cast<double>(nNumber
);
891 double EnhancedCustomShape2d::GetEquationValueAsDouble( const sal_Int32 nIndex
) const
893 double fNumber
= 0.0;
894 static sal_uInt32 nLevel
= 0;
895 if ( nIndex
< static_cast<sal_Int32
>(vNodesSharedPtr
.size()) )
897 if ( vNodesSharedPtr
[ nIndex
] ) {
901 if ( vEquationResults
[ nIndex
].bReady
)
902 fNumber
= vEquationResults
[ nIndex
].fValue
;
904 // cast to non const, so that we can optimize by caching
905 // equation results, without changing all the const in the stack
906 struct EquationResult
&aResult
= const_cast<EnhancedCustomShape2d
*>(this)->vEquationResults
[ nIndex
];
908 fNumber
= aResult
.fValue
= (*vNodesSharedPtr
[ nIndex
])();
909 aResult
.bReady
= true;
911 SAL_INFO("svx", "equation " << nLevel
<< " (level: " << seqEquations
[nIndex
] << "): "
912 << fNumber
<< " --> " << 180.0*fNumber
/10800000.0);
914 if ( !std::isfinite( fNumber
) )
919 SAL_WARN("svx", "EnhancedCustomShape2d::GetEquationValueAsDouble failed");
925 "?" << nIndex
<< " --> " << fNumber
<< " (angle: "
926 << 180.0*fNumber
/10800000.0 << ")");
932 bool EnhancedCustomShape2d::SetAdjustValueAsDouble( const double& rValue
, const sal_Int32 nIndex
)
934 bool bRetValue
= false;
935 if ( nIndex
< seqAdjustmentValues
.getLength() )
937 // updating our local adjustment sequence
938 auto pseqAdjustmentValues
= seqAdjustmentValues
.getArray();
939 pseqAdjustmentValues
[ nIndex
].Value
<<= rValue
;
940 pseqAdjustmentValues
[ nIndex
].State
= css::beans::PropertyState_DIRECT_VALUE
;
946 basegfx::B2DPoint
EnhancedCustomShape2d::GetPointAsB2DPoint( const css::drawing::EnhancedCustomShapeParameterPair
& rPair
,
947 const bool bScale
, const bool bReplaceGeoSize
) const
951 GetParameter(fValX
, rPair
.First
, bReplaceGeoSize
, false);
958 GetParameter(fValY
, rPair
.Second
, false, bReplaceGeoSize
);
964 return basegfx::B2DPoint(fValX
,fValY
);
967 Point
EnhancedCustomShape2d::GetPoint( const css::drawing::EnhancedCustomShapeParameterPair
& rPair
,
968 const bool bScale
, const bool bReplaceGeoSize
) const
970 basegfx::B2DPoint
aPoint(GetPointAsB2DPoint(rPair
, bScale
, bReplaceGeoSize
));
971 return Point(static_cast<tools::Long
>(aPoint
.getX()), static_cast<tools::Long
>(aPoint
.getY()));
974 void EnhancedCustomShape2d::GetParameter( double& rRetValue
, const EnhancedCustomShapeParameter
& rParameter
,
975 const bool bReplaceGeoWidth
, const bool bReplaceGeoHeight
) const
978 switch ( rParameter
.Type
)
980 case EnhancedCustomShapeParameterType::ADJUSTMENT
:
982 sal_Int32 nAdjustmentIndex
= 0;
983 if ( rParameter
.Value
>>= nAdjustmentIndex
)
985 rRetValue
= GetAdjustValueAsDouble( nAdjustmentIndex
);
989 case EnhancedCustomShapeParameterType::EQUATION
:
991 sal_Int32 nEquationIndex
= 0;
992 if ( rParameter
.Value
>>= nEquationIndex
)
994 rRetValue
= GetEquationValueAsDouble( nEquationIndex
);
998 case EnhancedCustomShapeParameterType::NORMAL
:
1000 if ( rParameter
.Value
.getValueTypeClass() == TypeClass_DOUBLE
)
1003 if ( rParameter
.Value
>>= fValue
)
1010 sal_Int32 nValue
= 0;
1011 if ( rParameter
.Value
>>= nValue
)
1014 if ( bReplaceGeoWidth
&& ( nValue
== nCoordWidth
) )
1015 rRetValue
*= fXRatio
;
1016 else if ( bReplaceGeoHeight
&& ( nValue
== nCoordHeight
) )
1017 rRetValue
*= fYRatio
;
1022 case EnhancedCustomShapeParameterType::LEFT
:
1027 case EnhancedCustomShapeParameterType::TOP
:
1032 case EnhancedCustomShapeParameterType::RIGHT
:
1034 rRetValue
= nCoordWidth
;
1037 case EnhancedCustomShapeParameterType::BOTTOM
:
1039 rRetValue
= nCoordHeight
;
1045 // nLumDat 28-31 = number of luminance entries in nLumDat
1046 // nLumDat 27-24 = nLumDatEntry 0
1047 // nLumDat 23-20 = nLumDatEntry 1 ...
1048 // each 4bit entry is to be interpreted as a 10 percent signed luminance changing
1049 sal_Int32
EnhancedCustomShape2d::GetLuminanceChange( sal_uInt32 nIndex
) const
1051 const sal_uInt32 nCount
= nColorData
>> 28;
1055 if ( nIndex
>= nCount
)
1056 nIndex
= nCount
- 1;
1058 const sal_Int32 nLumDat
= nColorData
<< ( ( 1 + nIndex
) << 2 );
1059 return ( nLumDat
>> 28 ) * 10;
1062 Color
EnhancedCustomShape2d::GetColorData( const Color
& rFillColor
, sal_uInt32 nIndex
, double dBrightness
) const
1064 if ( bOOXMLShape
|| ( mso_sptMin
== eSpType
/* ODF "non-primitive" */ ) )
1065 { //do LibreOffice way, using dBrightness
1066 if ( dBrightness
== 0.0)
1072 if (dBrightness
>=0.0)
1073 { //lighten, blending with white
1074 return Color( static_cast<sal_uInt8
>(static_cast< sal_Int32
>( std::clamp(rFillColor
.GetRed() * (1.0-dBrightness
) + dBrightness
* 255.0, 0.0, 255.0) )),
1075 static_cast<sal_uInt8
>(static_cast< sal_Int32
>( std::clamp(rFillColor
.GetGreen() * (1.0-dBrightness
) + dBrightness
* 255.0, 0.0, 255.0) )),
1076 static_cast<sal_uInt8
>(static_cast< sal_Int32
>( std::clamp(rFillColor
.GetBlue() * (1.0-dBrightness
) + dBrightness
* 255.0, 0.0, 255.0) )) );
1079 { //darken (indicated by negative sign), blending with black
1080 return Color( static_cast<sal_uInt8
>(static_cast< sal_Int32
>( std::clamp(rFillColor
.GetRed() * (1.0+dBrightness
), 0.0, 255.0) )),
1081 static_cast<sal_uInt8
>(static_cast< sal_Int32
>( std::clamp(rFillColor
.GetGreen() * (1.0+dBrightness
), 0.0, 255.0) )),
1082 static_cast<sal_uInt8
>(static_cast< sal_Int32
>( std::clamp(rFillColor
.GetBlue() * (1.0+dBrightness
), 0.0, 255.0) )) );
1087 { //do OpenOffice way, using nColorData
1088 const sal_Int32 nLuminance
= GetLuminanceChange(nIndex
);
1092 basegfx::BColor aHSVColor
=
1093 basegfx::utils::rgb2hsv(
1094 basegfx::BColor(rFillColor
.GetRed()/255.0,
1095 rFillColor
.GetGreen()/255.0,
1096 rFillColor
.GetBlue()/255.0));
1098 if( nLuminance
> 0 )
1101 aHSVColor
.getGreen() * (1.0-nLuminance
/100.0));
1104 (1.0-nLuminance
/100.0)*aHSVColor
.getBlue());
1106 else if( nLuminance
< 0 )
1109 (1.0+nLuminance
/100.0)*aHSVColor
.getBlue());
1112 aHSVColor
= basegfx::utils::hsv2rgb(aHSVColor
);
1113 return Color( static_cast<sal_uInt8
>(static_cast< sal_Int32
>( std::clamp(aHSVColor
.getRed(),0.0,1.0) * 255.0 + 0.5 )),
1114 static_cast<sal_uInt8
>(static_cast< sal_Int32
>( std::clamp(aHSVColor
.getGreen(),0.0,1.0) * 255.0 + 0.5 )),
1115 static_cast<sal_uInt8
>(static_cast< sal_Int32
>( std::clamp(aHSVColor
.getBlue(),0.0,1.0) * 255.0 + 0.5 )) );
1119 tools::Rectangle
EnhancedCustomShape2d::GetTextRect() const
1121 if ( !seqTextFrames
.hasElements() )
1123 sal_Int32 nIndex
= 0;
1124 Point
aTopLeft( GetPoint( seqTextFrames
[ nIndex
].TopLeft
, !bOOXMLShape
, true ) );
1125 Point
aBottomRight( GetPoint( seqTextFrames
[ nIndex
].BottomRight
, !bOOXMLShape
, true ) );
1126 tools::Rectangle
aRect( aTopLeft
, aBottomRight
);
1129 aRect
.SetLeft(aLogicRect
.GetWidth() - 1 - aBottomRight
.X());
1130 aRect
.SetRight( aLogicRect
.GetWidth() - 1 - aTopLeft
.X());
1134 aRect
.SetTop(aLogicRect
.GetHeight() - 1 - aBottomRight
.Y());
1135 aRect
.SetBottom(aLogicRect
.GetHeight() - 1 - aTopLeft
.Y());
1137 SAL_INFO("svx", aRect
.GetWidth() << " x " << aRect
.GetHeight());
1138 if( aRect
.GetWidth() <= 1 || aRect
.GetHeight() <= 1 )
1140 aRect
.Move( aLogicRect
.Left(), aLogicRect
.Top() );
1145 sal_uInt32
EnhancedCustomShape2d::GetHdlCount() const
1147 return seqHandles
.getLength();
1150 bool EnhancedCustomShape2d::GetHandlePosition( const sal_uInt32 nIndex
, Point
& rReturnPosition
) const
1152 bool bRetValue
= false;
1153 if ( nIndex
< GetHdlCount() )
1156 if ( ConvertSequenceToEnhancedCustomShape2dHandle( seqHandles
[ nIndex
], aHandle
) )
1158 if ( aHandle
.nFlags
& HandleFlags::POLAR
)
1160 Point
aReferencePoint( GetPoint( aHandle
.aPolar
) );
1164 GetParameter( fRadius
, aHandle
.aPosition
.First
, false, false );
1165 GetParameter( fAngle
, aHandle
.aPosition
.Second
, false, false );
1167 double a
= basegfx::deg2rad(360.0 - fAngle
);
1168 double dx
= fRadius
* fXScale
;
1169 double fX
= dx
* cos( a
);
1170 double fY
=-dx
* sin( a
);
1173 FRound( fX
+ aReferencePoint
.X() ),
1174 basegfx::fTools::equalZero(fXScale
) ? aReferencePoint
.Y() :
1175 FRound( ( fY
* fYScale
) / fXScale
+ aReferencePoint
.Y() ) );
1179 if ( aHandle
.nFlags
& HandleFlags::SWITCHED
)
1181 if ( aLogicRect
.GetHeight() > aLogicRect
.GetWidth() )
1183 css::drawing::EnhancedCustomShapeParameter aFirst
= aHandle
.aPosition
.First
;
1184 css::drawing::EnhancedCustomShapeParameter aSecond
= aHandle
.aPosition
.Second
;
1185 aHandle
.aPosition
.First
= aSecond
;
1186 aHandle
.aPosition
.Second
= aFirst
;
1190 rReturnPosition
= GetPoint(aHandle
.aPosition
, false /*bScale*/);
1192 rReturnPosition
= GetPoint(aHandle
.aPosition
, true /*bScale*/);
1194 const GeoStat
aGeoStat(mrSdrObjCustomShape
.GetGeoStat());
1195 if ( aGeoStat
.nShearAngle
)
1197 double nTan
= aGeoStat
.mfTanShearAngle
;
1198 if (bFlipV
!= bFlipH
)
1200 ShearPoint( rReturnPosition
, Point( aLogicRect
.GetWidth() / 2, aLogicRect
.GetHeight() / 2 ), nTan
);
1204 double a
= toRadians(nRotateAngle
);
1205 RotatePoint( rReturnPosition
, Point( aLogicRect
.GetWidth() / 2, aLogicRect
.GetHeight() / 2 ), sin( a
), cos( a
) );
1208 rReturnPosition
.setX( aLogicRect
.GetWidth() - rReturnPosition
.X() );
1210 rReturnPosition
.setY( aLogicRect
.GetHeight() - rReturnPosition
.Y() );
1211 rReturnPosition
.Move( aLogicRect
.Left(), aLogicRect
.Top() );
1218 static double lcl_getXAdjustmentValue(std::u16string_view rShapeType
, const sal_uInt32 nHandleIndex
,
1219 const double fX
, const double fW
, const double fH
)
1221 // degenerated shapes are not worth to calculate special case for each shape type
1222 if (fW
<= 0.0 || fH
<= 0.0)
1225 // pattern (w - x) / ss * 100000 or (r - x) / ss * 100000
1226 if ((rShapeType
== u
"ooxml-bentArrow" && nHandleIndex
== 2) || (rShapeType
== u
"ooxml-chevron")
1227 || (rShapeType
== u
"ooxml-curvedRightArrow") || (rShapeType
== u
"ooxml-foldedCorner")
1228 || (rShapeType
== u
"ooxml-homePlate") || (rShapeType
== u
"ooxml-notchedRightArrow")
1229 || (rShapeType
== u
"ooxml-nonIsoscelesTrapezoid" && nHandleIndex
== 1)
1230 || (rShapeType
== u
"ooxml-rightArrow")
1231 || (rShapeType
== u
"ooxml-rightArrowCallout" && nHandleIndex
== 2)
1232 || (rShapeType
== u
"ooxml-round1Rect")
1233 || (rShapeType
== u
"ooxml-round2DiagRect" && nHandleIndex
== 1)
1234 || (rShapeType
== u
"ooxml-round2SameRect" && nHandleIndex
== 0)
1235 || (rShapeType
== u
"ooxml-snip1Rect")
1236 || (rShapeType
== u
"ooxml-snip2DiagRect" && nHandleIndex
== 1)
1237 || (rShapeType
== u
"ooxml-snip2SameRect" && nHandleIndex
== 0)
1238 || (rShapeType
== u
"ooxml-snipRoundRect" && nHandleIndex
== 1)
1239 || (rShapeType
== u
"ooxml-swooshArrow") || (rShapeType
== u
"ooxml-stripedRightArrow"))
1240 return (fW
- fX
) / std::min(fW
, fH
) * 100000.0;
1242 // pattern x / ss * 100000 or (x - l) / ss * 100000
1243 if ((rShapeType
== u
"ooxml-bentArrow" && nHandleIndex
== 0)
1244 || (rShapeType
== u
"ooxml-bentArrow" && nHandleIndex
== 3)
1245 || (rShapeType
== u
"ooxml-corner")
1246 || (rShapeType
== u
"ooxml-curvedDownArrow") || (rShapeType
== u
"ooxml-curvedLeftArrow")
1247 || (rShapeType
== u
"ooxml-curvedUpArrow") || (rShapeType
== u
"ooxml-leftArrow")
1248 || (rShapeType
== u
"ooxml-leftArrowCallout" && nHandleIndex
== 2)
1249 || (rShapeType
== u
"ooxml-leftRightArrow")
1250 || (rShapeType
== u
"ooxml-leftRightArrowCallout" && nHandleIndex
== 2)
1251 || (rShapeType
== u
"ooxml-leftRightRibbon")
1252 || (rShapeType
== u
"ooxml-nonIsoscelesTrapezoid" && nHandleIndex
== 0)
1253 || (rShapeType
== u
"ooxml-parallelogram")
1254 || (rShapeType
== u
"ooxml-round2DiagRect" && nHandleIndex
== 0)
1255 || (rShapeType
== u
"ooxml-round2SameRect" && nHandleIndex
== 1)
1256 || (rShapeType
== u
"ooxml-roundRect")
1257 || (rShapeType
== u
"ooxml-snip2DiagRect" && nHandleIndex
== 0)
1258 || (rShapeType
== u
"ooxml-snip2SameRect" && nHandleIndex
== 1)
1259 || (rShapeType
== u
"ooxml-snipRoundRect" && nHandleIndex
== 0)
1260 || (rShapeType
== u
"ooxml-uturnArrow" && nHandleIndex
== 0)
1261 || (rShapeType
== u
"ooxml-uturnArrow" && nHandleIndex
== 3))
1262 return fX
/ std::min(fW
, fH
) * 100000.0;
1264 // pattern (hc - x) / ss * 200000
1265 if ((rShapeType
== u
"ooxml-downArrowCallout" && nHandleIndex
== 0)
1266 || (rShapeType
== u
"ooxml-leftRightUpArrow" && nHandleIndex
== 0)
1267 || (rShapeType
== u
"ooxml-quadArrow" && nHandleIndex
== 0)
1268 || (rShapeType
== u
"ooxml-quadArrowCallout" && nHandleIndex
== 0)
1269 || (rShapeType
== u
"ooxml-upArrowCallout" && nHandleIndex
== 0)
1270 || (rShapeType
== u
"ooxml-upDownArrowCallout" && nHandleIndex
== 0))
1271 return (fW
/ 2.0 - fX
) / std::min(fW
, fH
) * 200000.0;
1273 // pattern (hc - x) / ss * 100000
1274 if ((rShapeType
== u
"ooxml-downArrowCallout" && nHandleIndex
== 1)
1275 || (rShapeType
== u
"ooxml-leftRightUpArrow" && nHandleIndex
== 1)
1276 || (rShapeType
== u
"ooxml-quadArrow" && nHandleIndex
== 1)
1277 || (rShapeType
== u
"ooxml-quadArrowCallout" && nHandleIndex
== 1)
1278 || (rShapeType
== u
"ooxml-upArrowCallout" && nHandleIndex
== 1)
1279 || (rShapeType
== u
"ooxml-upDownArrowCallout" && nHandleIndex
== 1))
1280 return (fW
/ 2.0 - fX
) / std::min(fW
, fH
) * 100000.0;
1282 // pattern (w - x) / ss * 50000 or (r - x) / ss * 50000
1283 if ((rShapeType
== u
"ooxml-bentUpArrow") || (rShapeType
== u
"ooxml-leftUpArrow")
1284 || (rShapeType
== u
"ooxml-uturnArrow" && nHandleIndex
== 1))
1285 return (fW
- fX
) / std::min(fW
, fH
) * 50000.0;
1287 // pattern x / ss * 200000
1288 if (rShapeType
== u
"ooxml-nonIsoscelesTrapezoid" && nHandleIndex
== 0)
1289 return fX
/ std::min(fW
, fH
) * 200000.0;
1291 // pattern (hc - x) / w * 200000
1292 if ((rShapeType
== u
"ooxml-downArrow" && nHandleIndex
== 0)
1293 || (rShapeType
== u
"ooxml-ellipseRibbon") || (rShapeType
== u
"ooxml-ellipseRibbon2")
1294 || (rShapeType
== u
"ooxml-leftRightArrowCallout" && nHandleIndex
== 3)
1295 || (rShapeType
== u
"ooxml-ribbon") || (rShapeType
== u
"ooxml-ribbon2")
1296 || (rShapeType
== u
"ooxml-upArrow" && nHandleIndex
== 0)
1297 || (rShapeType
== u
"ooxml-upDownArrow" && nHandleIndex
== 0))
1298 return (fW
/ 2.0 - fX
) / fW
* 200000.0;
1300 // pattern (x - hc) / w * 100000
1301 if ((rShapeType
== u
"ooxml-cloudCallout") || (rShapeType
== u
"ooxml-doubleWave")
1302 || (rShapeType
== u
"ooxml-wave") || (rShapeType
== u
"ooxml-wedgeEllipseCallout")
1303 || (rShapeType
== u
"ooxml-wedgeRectCallout")
1304 || (rShapeType
== u
"ooxml-wedgeRoundRectCallout"))
1305 return (fX
- fW
/ 2.0) / fW
* 100000.0;
1307 // pattern (x - hc) / w * 200000
1308 if (rShapeType
== u
"ooxml-teardrop")
1309 return (fX
- fW
/ 2.0) / fW
* 200000.0;
1311 // pattern (w - x) / w * 100000 or (r - x) / w * 100000
1312 if (rShapeType
== u
"ooxml-leftArrowCallout" && nHandleIndex
== 3)
1313 return (fW
- fX
) / fW
* 100000.0;
1315 // pattern (hc - x) / h * 100000
1316 if (rShapeType
== u
"ooxml-mathDivide")
1317 return (fW
/ 2.0 - fX
) / fH
* 100000.0;
1319 // pattern x / w * 100000, simple scaling
1320 if (o3tl::starts_with(rShapeType
, u
"ooxml-"))
1321 return fX
/ fW
* 100000.0;
1323 return fX
; // method is unknown
1326 static double lcl_getYAdjustmentValue(std::u16string_view rShapeType
, const sal_uInt32 nHandleIndex
,
1327 const double fY
, const double fW
, const double fH
)
1329 // degenerated shapes are not worth to calculate a special case for each shape type
1330 if (fW
<= 0.0 || fH
<= 0.0)
1333 // pattern (vc - y) / ss * 100000
1334 if ((rShapeType
== u
"ooxml-leftArrowCallout" && nHandleIndex
== 1)
1335 || (rShapeType
== u
"ooxml-leftRightArrowCallout" && nHandleIndex
== 1)
1336 || (rShapeType
== u
"ooxml-rightArrowCallout" && nHandleIndex
== 1))
1337 return (fH
/ 2.0 - fY
) / std::min(fW
, fH
) * 100000.0;
1339 // pattern (vc - y) / ss * 200000
1340 if ((rShapeType
== u
"ooxml-curvedLeftArrow") || (rShapeType
== u
"ooxml-curvedRightArrow")
1341 || (rShapeType
== u
"ooxml-leftArrowCallout" && nHandleIndex
== 0)
1342 || (rShapeType
== u
"ooxml-leftRightArrowCallout" && nHandleIndex
== 0)
1343 || (rShapeType
== u
"ooxml-mathPlus")
1344 || (rShapeType
== u
"ooxml-rightArrowCallout" && nHandleIndex
== 0))
1345 return (fH
/ 2.0 - fY
) / std::min(fW
, fH
) * 200000.0;
1347 // pattern (h - y) / ss * 100000 or (b - y) / ss * 100000
1348 if ((rShapeType
== u
"ooxml-bentUpArrow" && nHandleIndex
== 0) || (rShapeType
== u
"ooxml-corner")
1349 || (rShapeType
== u
"ooxml-curvedDownArrow") || (rShapeType
== u
"ooxml-downArrow")
1350 || (rShapeType
== u
"ooxml-downArrowCallout" && nHandleIndex
== 2)
1351 || (rShapeType
== u
"ooxml-uturnArrow" && nHandleIndex
== 2))
1352 return (fH
- fY
) / std::min(fW
, fH
) * 100000.0;
1354 // pattern (h - y) / ss * 200000 or (b - y) / ss * 200000
1355 if (rShapeType
== u
"ooxml-leftUpArrow" && nHandleIndex
== 0) // - adj2 * 2 outside
1356 return (fH
- fY
) / std::min(fW
, fH
) * 200000.0;
1358 // pattern y / ss * 100000 or (y - t) / ss * 100000
1359 if ((rShapeType
== u
"ooxml-bentUpArrow" && nHandleIndex
== 2)
1360 || (rShapeType
== u
"ooxml-bracePair") || (rShapeType
== u
"ooxml-bracketPair")
1361 || (rShapeType
== u
"ooxml-can") || (rShapeType
== u
"ooxml-cube")
1362 || (rShapeType
== u
"ooxml-curvedUpArrow") || (rShapeType
== u
"ooxml-halfFrame")
1363 || (rShapeType
== u
"ooxml-leftBrace" && nHandleIndex
== 0)
1364 || (rShapeType
== u
"ooxml-leftBracket") || (rShapeType
== u
"ooxml-leftRightUpArrow")
1365 || (rShapeType
== u
"ooxml-leftUpArrow" && nHandleIndex
== 2)
1366 || (rShapeType
== u
"ooxml-mathMultiply") || (rShapeType
== u
"ooxml-quadArrow")
1367 || (rShapeType
== u
"ooxml-quadArrowCallout" && nHandleIndex
== 2)
1368 || (rShapeType
== u
"ooxml-rightBrace" && nHandleIndex
== 0)
1369 || (rShapeType
== u
"ooxml-rightBracket") || (rShapeType
== u
"ooxml-upArrow")
1370 || (rShapeType
== u
"ooxml-upArrowCallout" && nHandleIndex
== 2)
1371 || (rShapeType
== u
"ooxml-upDownArrow")
1372 || (rShapeType
== u
"ooxml-upDownArrowCallout" && nHandleIndex
== 2)
1373 || (rShapeType
== u
"ooxml-verticalScroll"))
1374 return fY
/ std::min(fW
, fH
) * 100000.0;
1376 // pattern y / ss * 50000
1377 if (rShapeType
== u
"ooxml-bentArrow")
1378 return fY
/ std::min(fW
, fH
) * 50000.0;
1380 // pattern (vc - y) / h * 100000
1381 if ((rShapeType
== u
"ooxml-mathDivide" && nHandleIndex
== 1) // -adj1 / 2 - adj3 outside
1382 || (rShapeType
== u
"ooxml-mathEqual" && nHandleIndex
== 0) // -adj2 / 2 outside
1383 || (rShapeType
== u
"ooxml-mathNotEqual" && nHandleIndex
== 0) // -adj3 / 2 outside
1384 || (rShapeType
== u
"ooxml-star4") || (rShapeType
== u
"ooxml-star6")
1385 || (rShapeType
== u
"ooxml-star8") || (rShapeType
== u
"ooxml-star10")
1386 || (rShapeType
== u
"ooxml-star12") || (rShapeType
== u
"ooxml-star16")
1387 || (rShapeType
== u
"ooxml-star24") || (rShapeType
== u
"ooxml-star32"))
1388 return (fH
/ 2.0 - fY
) / fH
* 100000.0;
1390 // pattern (vc - y) / h * 200000
1391 if ((rShapeType
== u
"ooxml-leftArrow") || (rShapeType
== u
"ooxml-leftRightArrow")
1392 || (rShapeType
== u
"ooxml-mathDivide" && nHandleIndex
== 0)
1393 || (rShapeType
== u
"ooxml-mathEqual" && nHandleIndex
== 1)
1394 || (rShapeType
== u
"ooxml-mathMinus") || (rShapeType
== u
"ooxml-notchedRightArrow")
1395 || (rShapeType
== u
"ooxml-mathNotEqual" && nHandleIndex
== 2)
1396 || (rShapeType
== u
"ooxml-quadArrowCallout" && nHandleIndex
== 3)
1397 || (rShapeType
== u
"ooxml-rightArrow") || (rShapeType
== u
"ooxml-stripedRightArrow")
1398 || (rShapeType
== u
"ooxml-upDownArrowCallout" && nHandleIndex
== 3))
1399 return (fH
/ 2.0 - fY
) / fH
* 200000.0;
1401 // pattern (y - vc) / h * 100000
1402 if ((rShapeType
== u
"ooxml-cloudCallout") || (rShapeType
== u
"ooxml-wedgeEllipseCallout")
1403 || (rShapeType
== u
"ooxml-wedgeRectCallout")
1404 || (rShapeType
== u
"ooxml-wedgeRoundRectCallout"))
1405 return (fY
- fH
/ 2.0) / fH
* 100000.0;
1407 // pattern (h - y) / h * 100000 or (b - y) / h * 100000
1408 if ((rShapeType
== u
"ooxml-ellipseRibbon" && nHandleIndex
== 2)
1409 || (rShapeType
== u
"ooxml-ellipseRibbon2" && nHandleIndex
== 0)
1410 || (rShapeType
== u
"ooxml-ribbon2")
1411 || (rShapeType
== u
"ooxml-upArrowCallout" && nHandleIndex
== 3))
1412 return (fH
- fY
) / fH
* 100000.0;
1414 // special pattern smiley
1415 if (rShapeType
== u
"ooxml-smileyFace")
1416 return (fY
- fH
* 16515.0 / 21600.0) / fH
* 100000.0;
1418 // special pattern for star with odd number of tips, because center of star not center of shape
1419 if (rShapeType
== u
"ooxml-star5")
1420 return (fH
/ 2.0 - fY
* 100000.0 / 110557.0) / fH
* 100000.0;
1421 if (rShapeType
== u
"ooxml-star7")
1422 return (fH
/ 2.0 - fY
* 100000.0 / 105210.0) / fH
* 100000.0;
1424 // special pattern swooshArrow
1425 if (rShapeType
== u
"ooxml-swooshArrow")
1426 return (fY
- std::min(fW
, fH
) / 8.0) / fH
* 100000.0;
1428 // special pattern leftRightRibbon
1429 if (rShapeType
== u
"ooxml-leftRightRibbon")
1430 return fY
/ fH
* 200000 - 100000;
1432 // pattern y / h * 100000, simple scaling
1433 if (o3tl::starts_with(rShapeType
, u
"ooxml-"))
1434 return fY
/ fH
* 100000.0;
1436 return fY
; // method is unknown
1439 static double lcl_getAngleInOOXMLUnit(double fDY
, double fDX
)
1441 if (fDX
!= 0.0 || fDY
!= 0.0)
1443 double fAngleRad(atan2(fDY
, fDX
));
1444 double fAngle
= basegfx::rad2deg(fAngleRad
);
1445 // atan2 returns angle in ]-pi; pi], OOXML preset shapes use [0;360[.
1448 // OOXML uses angle unit 1/60000 degree.
1452 return 0.0; // no angle defined for origin in polar coordinate system
1455 static double lcl_getRadiusDistance(double fWR
, double fHR
, double fX
, double fY
)
1457 // Get D so, that point (fX|fY) is on the ellipse, that has width fWR-D and
1458 // height fHR-D and center in origin.
1459 // Get solution of ellipse equation (fX/(fWR-D))^2 + (fY/(fHR-D)^2 = 1 by solving
1460 // fX^2*(fHR-D)^2 + fY^2*(fWR-D)^2 - (fWR-D)^2 * (fHR-D)^2 = 0 with Newton-method.
1462 return std::min(fHR
- fY
, fWR
);
1464 return std::min(fWR
- fX
, fHR
);
1466 double fD
= std::min(fWR
, fHR
) - std::hypot(fX
, fY
); // iteration start value
1472 const double fOldD(fD
);
1473 const double fWRmD(fWR
- fD
);
1474 const double fHRmD(fHR
- fD
);
1476 = fX
* fX
* fHRmD
* fHRmD
+ fY
* fY
* fWRmD
* fWRmD
- fWRmD
* fWRmD
* fHRmD
* fHRmD
;
1478 = 2.0 * (fHRmD
* (fWRmD
* fWRmD
- fX
* fX
) + fWRmD
* (fHRmD
* fHRmD
- fY
* fY
));
1479 if (fDenominator
!= 0.0)
1481 fD
= fD
- fNumerator
/ fDenominator
;
1482 bFound
= fabs(fOldD
- fD
) < 1.0E-12;
1485 fD
= fD
* 0.9; // new start value
1486 } while (nIter
< 50 && !bFound
);
1490 bool EnhancedCustomShape2d::SetHandleControllerPosition( const sal_uInt32 nIndex
, const css::awt::Point
& rPosition
)
1492 // The method name is misleading. Essentially it calculates the adjustment values from a given
1495 // For ooxml-foo shapes, the way to calculate the adjustment value from the handle position depends on
1496 // the type of the shape, therefore need 'Type'.
1497 OUString
sShapeType("non-primitive"); // default for ODF
1498 const SdrCustomShapeGeometryItem
& rGeometryItem(mrSdrObjCustomShape
.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
));
1499 const Any
* pAny
= rGeometryItem
.GetPropertyValueByName("Type");
1501 *pAny
>>= sShapeType
;
1503 bool bRetValue
= false;
1504 if ( nIndex
< GetHdlCount() )
1507 if ( ConvertSequenceToEnhancedCustomShape2dHandle( seqHandles
[ nIndex
], aHandle
) )
1509 Point
aP( rPosition
.X
, rPosition
.Y
);
1510 // apply the negative object rotation to the controller position
1512 aP
.Move( -aLogicRect
.Left(), -aLogicRect
.Top() );
1514 aP
.setX( aLogicRect
.GetWidth() - aP
.X() );
1516 aP
.setY( aLogicRect
.GetHeight() - aP
.Y() );
1519 double a
= -toRadians(nRotateAngle
);
1520 RotatePoint( aP
, Point( aLogicRect
.GetWidth() / 2, aLogicRect
.GetHeight() / 2 ), sin( a
), cos( a
) );
1522 const GeoStat
aGeoStat(mrSdrObjCustomShape
.GetGeoStat());
1523 if ( aGeoStat
.nShearAngle
)
1525 double nTan
= -aGeoStat
.mfTanShearAngle
;
1526 if (bFlipV
!= bFlipH
)
1528 ShearPoint( aP
, Point( aLogicRect
.GetWidth() / 2, aLogicRect
.GetHeight() / 2 ), nTan
);
1531 double fPos1
= aP
.X(); //( bFlipH ) ? aLogicRect.GetWidth() - aP.X() : aP.X();
1532 double fPos2
= aP
.Y(); //( bFlipV ) ? aLogicRect.GetHeight() -aP.Y() : aP.Y();
1533 fPos1
= !basegfx::fTools::equalZero(fXScale
) ? (fPos1
/ fXScale
) : SAL_MAX_INT32
;
1534 fPos2
= !basegfx::fTools::equalZero(fYScale
) ? (fPos2
/ fYScale
) : SAL_MAX_INT32
;
1535 // revert -nCoordLeft and -nCoordTop aus GetPoint()
1536 fPos1
+= nCoordLeft
;
1539 // Used for scaling the adjustment values based on handle positions
1543 if ( nCoordWidth
|| nCoordHeight
)
1545 fWidth
= nCoordWidth
;
1546 fHeight
= nCoordHeight
;
1550 fWidth
= aLogicRect
.GetWidth();
1551 fHeight
= aLogicRect
.GetHeight();
1554 if ( aHandle
.nFlags
& HandleFlags::SWITCHED
)
1556 if ( aLogicRect
.GetHeight() > aLogicRect
.GetWidth() )
1560 double fTmp
= fWidth
;
1568 sal_Int32 nFirstAdjustmentValue
= -1, nSecondAdjustmentValue
= -1;
1570 // ODF shapes are expected to use a direct binding between position and adjustment
1571 // values. OOXML preset shapes use known formulas. These are calculated backward to
1572 // get the adjustment values. So far we do not have a general method to calculate
1573 // the adjustment values for any shape from the handle position.
1574 if ( aHandle
.aPosition
.First
.Type
== EnhancedCustomShapeParameterType::ADJUSTMENT
)
1575 aHandle
.aPosition
.First
.Value
>>= nFirstAdjustmentValue
;
1576 if ( aHandle
.aPosition
.Second
.Type
== EnhancedCustomShapeParameterType::ADJUSTMENT
)
1577 aHandle
.aPosition
.Second
.Value
>>= nSecondAdjustmentValue
;
1579 if ( aHandle
.nFlags
& ( HandleFlags::POLAR
| HandleFlags::REFR
| HandleFlags::REFANGLE
))
1582 if (aHandle
.nFlags
& HandleFlags::REFR
)
1583 nFirstAdjustmentValue
= aHandle
.nRefR
;
1584 if (aHandle
.nFlags
& HandleFlags::REFANGLE
)
1585 nSecondAdjustmentValue
= aHandle
.nRefAngle
;
1588 double fRadius(0.0);
1589 // 'then' treats only shapes of type "ooxml-foo", fontwork shapes have been mapped
1590 // to MS binary import and will be treated in 'else'.
1593 // DrawingML polar handles set REFR or REFANGLE instead of POLAR
1594 // use the shape center instead.
1595 double fDX
= fPos1
- fWidth
/ 2.0;
1596 double fDY
= fPos2
- fHeight
/ 2.0;
1598 // There exists no common pattern. 'radius' or 'angle' might have special meaning.
1599 if (sShapeType
== "ooxml-blockArc" && nIndex
== 1)
1601 // usual angle, special radius
1602 fAngle
= lcl_getAngleInOOXMLUnit(fDY
, fDX
);
1603 // The value connected to REFR is the _difference_ between the outer
1604 // ellipse given by shape width and height and the inner ellipse through
1605 // the handle position.
1606 double fRadiusDifference
1607 = lcl_getRadiusDistance(fWidth
/ 2.0, fHeight
/ 2.0, fDX
, fDY
);
1608 double fss(std::min(fWidth
, fHeight
));
1610 fRadius
= fRadiusDifference
* 100000.0 / fss
;
1612 else if (sShapeType
== "ooxml-donut" || sShapeType
== "ooxml-noSmoking")
1614 // no angle adjustment, radius bound to x-coordinate of handle
1615 double fss(std::min(fWidth
, fHeight
));
1617 fRadius
= fPos1
* 100000.0 / fss
;
1619 else if ((sShapeType
== "ooxml-circularArrow"
1620 || sShapeType
== "ooxml-leftRightCircularArrow"
1621 || sShapeType
== "ooxml-leftCircularArrow")
1624 // The value adj2 is the increase compared to the angle in adj3
1625 double fHandleAngle
= lcl_getAngleInOOXMLUnit(fDY
, fDX
);
1626 if (sShapeType
== "ooxml-leftCircularArrow")
1627 fAngle
= GetAdjustValueAsDouble(2) - fHandleAngle
;
1629 fAngle
= fHandleAngle
- GetAdjustValueAsDouble(2);
1630 if (fAngle
< 0.0) // 0deg to 360deg cut
1631 fAngle
+= 21600000.0;
1634 else if ((sShapeType
== "ooxml-circularArrow"
1635 || sShapeType
== "ooxml-leftCircularArrow"
1636 || sShapeType
== "ooxml-leftRightCircularArrow")
1639 // The value adj1 connected to REFR is the thickness of the arc. The adjustvalue adj5
1640 // has the _difference_ between the outer ellipse given by shape width and height
1641 // and the middle ellipse of the arc. The handle is on the outer side of the
1642 // arc. So we calculate the difference between the ellipse through the handle
1643 // and the outer ellipse and subtract then.
1644 double fRadiusDifferenceHandle
1645 = lcl_getRadiusDistance(fWidth
/ 2.0, fHeight
/ 2.0, fDX
, fDY
);
1646 double fadj5(GetAdjustValueAsDouble(4));
1647 double fss(std::min(fWidth
, fHeight
));
1650 fadj5
= fadj5
* fss
/ 100000.0;
1651 fRadius
= 2.0 * (fadj5
- fRadiusDifferenceHandle
);
1652 fRadius
= fRadius
* 100000.0 / fss
;
1654 // ToDo: Get angle adj3 exact. Use approximation for now
1655 fAngle
= lcl_getAngleInOOXMLUnit(fDY
, fDX
);
1657 else if ((sShapeType
== "ooxml-circularArrow"
1658 || sShapeType
== "ooxml-leftCircularArrow"
1659 || sShapeType
== "ooxml-leftRightCircularArrow")
1662 // ToDo: Getting handle position from adjustment value adj5 is complex.
1663 // Analytical or numerical solution for backward calculation is missing.
1664 // Approximation for now, using a line from center through handle position.
1665 double fAngleRad(0.0);
1666 if (fDX
!= 0.0 || fDY
!= 0.0)
1667 fAngleRad
= atan2(fDY
, fDX
);
1668 double fHelpX
= cos(fAngleRad
) * fHeight
/ 2.0;
1669 double fHelpY
= sin(fAngleRad
) * fWidth
/ 2.0;
1670 if (fHelpX
!= 0.0 || fHelpY
!= 0.0)
1672 double fHelpAngle
= atan2(fHelpY
, fHelpX
);
1673 double fOuterX
= fWidth
/ 2.0 * cos(fHelpAngle
);
1674 double fOuterY
= fHeight
/ 2.0 * sin(fHelpAngle
);
1675 double fOuterRadius
= std::hypot(fOuterX
, fOuterY
);
1676 double fHandleRadius
= std::hypot(fDX
, fDY
);
1677 fRadius
= (fOuterRadius
- fHandleRadius
) / 2.0;
1678 double fss(std::min(fWidth
, fHeight
));
1680 fRadius
= fRadius
* 100000.0 / fss
;
1684 else if (sShapeType
== "ooxml-mathNotEqual" && nIndex
== 1)
1686 double fadj1(GetAdjustValueAsDouble(0));
1687 double fadj3(GetAdjustValueAsDouble(2));
1688 fadj1
= fadj1
* fHeight
/ 100000.0;
1689 fadj3
= fadj3
* fHeight
/ 100000.0;
1690 double fDYRefHorizBar
= fDY
+ fadj1
+ fadj3
;
1691 if (fDX
!= 0.0 || fDYRefHorizBar
!= 0.0)
1693 double fRawAngleDeg
= basegfx::rad2deg(atan2(fDYRefHorizBar
, fDX
));
1694 fAngle
= (fRawAngleDeg
+ 180.0) * 60000.0;
1700 // no special meaning of radius or angle, suitable for "ooxml-arc",
1701 // "ooxml-chord", "ooxml-pie" and circular arrows value adj4.
1702 fAngle
= lcl_getAngleInOOXMLUnit(fDY
, fDX
);
1703 fRadius
= std::hypot(fDX
, fDY
);
1704 double fss(std::min(fWidth
, fHeight
));
1706 fRadius
= fRadius
* 100000.0 / fss
;
1709 else // e.g. shapes from ODF, MS binary import or shape type "fontwork-foo"
1711 double fXRef
, fYRef
;
1712 if (aHandle
.nFlags
& HandleFlags::POLAR
)
1714 GetParameter(fXRef
, aHandle
.aPolar
.First
, false, false);
1715 GetParameter(fYRef
, aHandle
.aPolar
.Second
, false, false);
1719 fXRef
= fWidth
/ 2.0;
1720 fYRef
= fHeight
/ 2.0;
1722 const double fDX
= fPos1
- fXRef
;
1723 const double fDY
= fPos2
- fYRef
;
1724 // ToDo: MS binary uses fixed-point number for the angle. Make sure conversion
1725 // to double is done in import and export.
1726 // ToDo: Angle unit is degree, but range ]-180;180] or [0;360[? Assume ]-180;180].
1727 if (fDX
!= 0.0 || fDY
!= 0.0)
1729 fRadius
= std::hypot(fDX
, fDY
);
1730 fAngle
= basegfx::rad2deg(atan2(fDY
, fDX
));
1734 // All formats can restrict the radius to a range
1735 if ( aHandle
.nFlags
& HandleFlags::RADIUS_RANGE_MINIMUM
)
1738 GetParameter( fMin
, aHandle
.aRadiusRangeMinimum
, false, false );
1739 if ( fRadius
< fMin
)
1742 if ( aHandle
.nFlags
& HandleFlags::RADIUS_RANGE_MAXIMUM
)
1745 GetParameter( fMax
, aHandle
.aRadiusRangeMaximum
, false, false );
1746 if ( fRadius
> fMax
)
1750 if ( nFirstAdjustmentValue
>= 0 )
1751 SetAdjustValueAsDouble( fRadius
, nFirstAdjustmentValue
);
1752 if ( nSecondAdjustmentValue
>= 0 )
1753 SetAdjustValueAsDouble( fAngle
, nSecondAdjustmentValue
);
1757 // Calculating the adjustment values follows in most cases some patterns, which only
1758 // need width and height of the shape and handle position. These patterns are calculated
1759 // in the static, local methods. More complex calculations or additional steps are
1761 // Values for corner cases like 'root(negative)' or 'div zero' are meaningless dummies.
1762 // Identifiers often refer to guide names in OOXML shape definitions.
1763 double fAdjustX
= fPos1
;
1764 double fAdjustY
= fPos2
;
1765 if (aHandle
.nFlags
& HandleFlags::REFX
)
1767 nFirstAdjustmentValue
= aHandle
.nRefX
;
1768 if ((sShapeType
== "ooxml-gear6") || (sShapeType
== "ooxml-gear9"))
1770 // special, needs angle calculations
1771 double fss(std::min(fWidth
, fHeight
));
1772 double fadj1(GetAdjustValueAsDouble(0)); // from point D6 or D9
1773 double fth(fadj1
* fss
/ 100000.0); // radius difference
1774 double frw(fWidth
/ 2.0 - fth
); // inner ellipse
1775 double frh(fHeight
/ 2.0 - fth
);
1776 double fDX(fPos1
- fWidth
/ 2.0);
1777 double fDY(fPos2
- fHeight
/ 2.0);
1778 double fbA(-1.7); // effective angle for point A6 or A9, dummy value
1779 if (fDX
!= 0.0 || fDY
!= 0.0)
1780 fbA
= atan2(fDY
, fDX
);
1781 double faA(fbA
); // corresponding circle angle, dummy value
1782 double ftmpX(frh
* cos(fbA
));
1783 double ftmpY(frw
* sin(fbA
));
1784 if (ftmpX
!= 0.0 || ftmpY
!= 0.0)
1785 faA
= atan2(ftmpY
, ftmpX
); // range ]-pi..pi], here -pi < faA < -pi/2
1786 // screen 270 deg = mathematic coordinate system -pi/2
1787 double fha(-M_PI_2
- faA
); // positive circle angle difference to 270 deg
1788 if (abs(fha
) == M_PI_2
) // should not happen, but ensure no tan(90deg)
1789 fha
= 0.12; // dummy value
1790 double flFD(2 * std::min(frw
, frh
) * tan(fha
) - fth
);
1792 fAdjustX
= flFD
/ fss
* 100000.0;
1797 = lcl_getXAdjustmentValue(sShapeType
, nIndex
, fPos1
, fWidth
, fHeight
);
1798 if ((sShapeType
== "ooxml-curvedDownArrow")
1799 || (sShapeType
== "ooxml-curvedUpArrow"))
1801 double fss(std::min(fWidth
, fHeight
));
1804 double fadj3(GetAdjustValueAsDouble(2));
1805 double fHScaled(100000.0 * fHeight
/ fss
);
1806 double fRadicand(fHScaled
* fHScaled
- fadj3
* fadj3
);
1807 double fSqrt
= fRadicand
>= 0.0 ? sqrt(fRadicand
) : 0.0;
1808 double fPart(200000.0 * fWidth
/ fss
* (fSqrt
+ fHScaled
));
1809 fAdjustX
= fPart
- 4.0 * fHScaled
* fAdjustX
;
1813 double fadj2(GetAdjustValueAsDouble(1));
1814 fAdjustX
= fAdjustX
- fadj2
* (fSqrt
+ fHScaled
);
1815 double fDenominator(fSqrt
- 3.0 * fHScaled
);
1816 fAdjustX
/= fDenominator
!= 0.0 ? fDenominator
: 1.0;
1820 // nIndex == 1, calculate adj2
1821 double fadj1(GetAdjustValueAsDouble(0));
1822 fAdjustX
= fAdjustX
- fadj1
* (fSqrt
- fHScaled
);
1823 double fDenominator(fSqrt
+ 3.0 * fHScaled
);
1824 fAdjustX
/= fDenominator
!= 0.0 ? fDenominator
: 1.0;
1831 if (aHandle
.nFlags
& HandleFlags::REFY
)
1833 nSecondAdjustmentValue
= aHandle
.nRefY
;
1834 if ((sShapeType
== "ooxml-gear6") || (sShapeType
== "ooxml-gear9"))
1836 // special, acts more like a polar handle radius
1837 double fDX
= fPos1
- fWidth
/ 2.0;
1838 double fDY
= fPos2
- fHeight
/ 2.0;
1839 double fRadiusDifference
1840 = lcl_getRadiusDistance(fWidth
/ 2.0, fHeight
/ 2.0, fDX
, fDY
);
1841 double fss(std::min(fWidth
, fHeight
));
1843 fAdjustY
= fRadiusDifference
/ fss
* 100000.0;
1848 = lcl_getYAdjustmentValue(sShapeType
, nIndex
, fPos2
, fWidth
, fHeight
);
1849 if (sShapeType
== "ooxml-mathDivide" && nIndex
== 1)
1850 fAdjustY
= fAdjustY
- GetAdjustValueAsDouble(0) / 2.0
1851 - GetAdjustValueAsDouble(2);
1852 else if (sShapeType
== "ooxml-mathEqual" && nIndex
== 0)
1853 fAdjustY
-= GetAdjustValueAsDouble(1) / 2.0;
1854 else if (sShapeType
== "ooxml-mathNotEqual" && nIndex
== 0)
1855 fAdjustY
-= GetAdjustValueAsDouble(2) / 2.0;
1856 else if (sShapeType
== "ooxml-leftUpArrow" && nIndex
== 0)
1857 fAdjustY
-= GetAdjustValueAsDouble(1) * 2.0;
1858 else if ((sShapeType
== "ooxml-curvedRightArrow")
1859 || (sShapeType
== "ooxml-curvedLeftArrow"))
1861 double fss(std::min(fWidth
, fHeight
));
1864 double fadj3(GetAdjustValueAsDouble(2));
1865 double fWScaled(100000.0 * fWidth
/ fss
);
1866 double fRadicand(fWScaled
* fWScaled
- fadj3
* fadj3
);
1867 double fSqrt
= fRadicand
>= 0.0 ? sqrt(fRadicand
) : 0.0;
1871 double fadj2(GetAdjustValueAsDouble(1));
1872 fAdjustY
= fWScaled
* (2.0 * fAdjustY
- fadj2
);
1873 fAdjustY
+= (200000.0 / fss
* fHeight
- fadj2
) * fSqrt
;
1874 double fDenominator(fSqrt
+ fWScaled
);
1875 fAdjustY
/= fDenominator
!= 0.0 ? fDenominator
: 1.0;
1879 // nIndex == 1, calculate adj2
1880 double fadj1(GetAdjustValueAsDouble(0));
1881 fAdjustY
= fWScaled
* (2.0 * fAdjustY
+ fadj1
);
1882 fAdjustY
+= (200000.0 / fss
* fHeight
- fadj1
) * fSqrt
;
1883 double fDenominator(fSqrt
+ 3.0 * fWScaled
);
1884 fAdjustY
/= fDenominator
!= 0.0 ? fDenominator
: 1.0;
1888 else if (sShapeType
== "ooxml-uturnArrow" && nIndex
== 2)
1890 double fss(std::min(fWidth
, fHeight
));
1893 double fadj5(GetAdjustValueAsDouble(4));
1894 fAdjustY
+= fHeight
/ fss
* (fadj5
- 100000.0);
1897 else if (sShapeType
== "ooxml-leftRightRibbon")
1900 fAdjustY
= GetAdjustValueAsDouble(2) - fAdjustY
;
1902 fAdjustY
= GetAdjustValueAsDouble(0) + fAdjustY
;
1907 if ( nFirstAdjustmentValue
>= 0 )
1909 if ( aHandle
.nFlags
& HandleFlags::RANGE_X_MINIMUM
) // check if horizontal handle needs to be within a range
1912 GetParameter( fXMin
, aHandle
.aXRangeMinimum
, false, false );
1913 if (fAdjustX
< fXMin
)
1916 if ( aHandle
.nFlags
& HandleFlags::RANGE_X_MAXIMUM
) // check if horizontal handle needs to be within a range
1919 GetParameter( fXMax
, aHandle
.aXRangeMaximum
, false, false );
1920 if (fAdjustX
> fXMax
)
1923 SetAdjustValueAsDouble(fAdjustX
, nFirstAdjustmentValue
);
1925 if ( nSecondAdjustmentValue
>= 0 )
1927 if ( aHandle
.nFlags
& HandleFlags::RANGE_Y_MINIMUM
) // check if vertical handle needs to be within a range
1930 GetParameter( fYMin
, aHandle
.aYRangeMinimum
, false, false );
1931 if (fAdjustY
< fYMin
)
1934 if ( aHandle
.nFlags
& HandleFlags::RANGE_Y_MAXIMUM
) // check if vertical handle needs to be within a range
1937 GetParameter( fYMax
, aHandle
.aYRangeMaximum
, false, false );
1938 if (fAdjustY
> fYMax
)
1941 SetAdjustValueAsDouble(fAdjustY
, nSecondAdjustmentValue
);
1944 // and writing them back into the GeometryItem
1945 SdrCustomShapeGeometryItem
aGeometryItem(mrSdrObjCustomShape
.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
));
1946 css::beans::PropertyValue aPropVal
;
1947 aPropVal
.Name
= "AdjustmentValues";
1948 aPropVal
.Value
<<= seqAdjustmentValues
;
1949 aGeometryItem
.SetPropertyValue( aPropVal
);
1950 mrSdrObjCustomShape
.SetMergedItem( aGeometryItem
);
1957 void EnhancedCustomShape2d::SwapStartAndEndArrow( SdrObject
* pObj
) //#108274
1959 XLineStartItem aLineStart
;
1960 aLineStart
.SetLineStartValue(pObj
->GetMergedItem( XATTR_LINEEND
).GetLineEndValue());
1961 XLineStartWidthItem
aLineStartWidth(pObj
->GetMergedItem( XATTR_LINEENDWIDTH
).GetValue());
1962 XLineStartCenterItem
aLineStartCenter(pObj
->GetMergedItem( XATTR_LINEENDCENTER
).GetValue());
1964 XLineEndItem aLineEnd
;
1965 aLineEnd
.SetLineEndValue(pObj
->GetMergedItem( XATTR_LINESTART
).GetLineStartValue());
1966 XLineEndWidthItem
aLineEndWidth(pObj
->GetMergedItem( XATTR_LINESTARTWIDTH
).GetValue());
1967 XLineEndCenterItem
aLineEndCenter(pObj
->GetMergedItem( XATTR_LINESTARTCENTER
).GetValue());
1969 pObj
->SetMergedItem( aLineStart
);
1970 pObj
->SetMergedItem( aLineStartWidth
);
1971 pObj
->SetMergedItem( aLineStartCenter
);
1972 pObj
->SetMergedItem( aLineEnd
);
1973 pObj
->SetMergedItem( aLineEndWidth
);
1974 pObj
->SetMergedItem( aLineEndCenter
);
1977 static basegfx::B2DPolygon
CreateArc( const tools::Rectangle
& rRect
, const Point
& rStart
, const Point
& rEnd
, const bool bClockwise
)
1979 tools::Rectangle
aRect( rRect
);
1980 Point
aStart( rStart
);
1983 sal_Int32 bSwapStartEndAngle
= 0;
1985 if ( aRect
.Left() > aRect
.Right() )
1986 bSwapStartEndAngle
^= 0x01;
1987 if ( aRect
.Top() > aRect
.Bottom() )
1988 bSwapStartEndAngle
^= 0x11;
1989 if ( bSwapStartEndAngle
)
1992 if ( bSwapStartEndAngle
& 1 )
1994 Point
aTmp( aStart
);
2000 tools::Polygon
aTempPoly( aRect
, aStart
, aEnd
, PolyStyle::Arc
);
2001 basegfx::B2DPolygon aRetval
;
2005 for ( sal_uInt16 j
= aTempPoly
.GetSize(); j
--; )
2007 aRetval
.append(basegfx::B2DPoint(aTempPoly
[ j
].X(), aTempPoly
[ j
].Y()));
2012 for ( sal_uInt16 j
= 0; j
< aTempPoly
.GetSize(); j
++ )
2014 aRetval
.append(basegfx::B2DPoint(aTempPoly
[ j
].X(), aTempPoly
[ j
].Y()));
2021 static double lcl_getNormalizedCircleAngleRad(const double fWR
, const double fHR
, const double fEllipseAngleDeg
)
2024 double fEAngleDeg(fmod(fEllipseAngleDeg
, 360.0));
2025 if (fEAngleDeg
< 0.0)
2026 fEAngleDeg
+= 360.0;
2027 if (fEAngleDeg
== 0.0 || fEAngleDeg
== 90.0 || fEAngleDeg
== 180.0 || fEAngleDeg
== 270.0)
2028 return basegfx::deg2rad(fEAngleDeg
);
2029 const double fX(fHR
* cos(basegfx::deg2rad(fEAngleDeg
)));
2030 const double fY(fWR
* sin(basegfx::deg2rad(fEAngleDeg
)));
2031 if (fX
!= 0.0 || fY
!= 0.0)
2033 fRet
= atan2(fY
, fX
);
2040 static double lcl_getNormalizedAngleRad(const double fCircleAngleDeg
)
2042 double fRet(fmod(fCircleAngleDeg
, 360.0));
2045 return basegfx::deg2rad(fRet
);
2048 void EnhancedCustomShape2d::CreateSubPath(
2050 sal_Int32
& rSegmentInd
,
2051 std::vector
< std::pair
< rtl::Reference
<SdrPathObj
>, double> >& rObjectList
,
2052 const bool bLineGeometryNeededOnly
,
2053 const bool bSortFilledObjectsToBack
,
2056 bool bNoFill
= false;
2057 bool bNoStroke
= false;
2058 double dBrightness
= 0.0; //no blending
2060 basegfx::B2DPolyPolygon aNewB2DPolyPolygon
;
2061 basegfx::B2DPolygon aNewB2DPolygon
;
2063 SetPathSize( nIndex
);
2065 sal_Int32 nSegInfoSize
= seqSegments
.getLength();
2066 if ( !nSegInfoSize
)
2068 for ( const EnhancedCustomShapeParameterPair
& rCoordinate
: std::as_const(seqCoordinates
) )
2070 const Point
aTempPoint(GetPoint( rCoordinate
, true, true ));
2071 aNewB2DPolygon
.append(basegfx::B2DPoint(aTempPoint
.X(), aTempPoint
.Y()));
2074 aNewB2DPolygon
.setClosed(true);
2078 sal_Int32 nCoordSize
= seqCoordinates
.getLength();
2079 for ( ;rSegmentInd
< nSegInfoSize
; )
2081 sal_Int16 nCommand
= seqSegments
[ rSegmentInd
].Command
;
2082 sal_Int16 nPntCount
= seqSegments
[ rSegmentInd
++ ].Count
;
2093 dBrightness
= -0.4; //use sign to distinguish DARKEN from LIGHTEN
2106 if(aNewB2DPolygon
.count() > 1)
2108 // #i76201# Add conversion to closed polygon when first and last points are equal
2109 basegfx::utils::checkClosed(aNewB2DPolygon
);
2110 aNewB2DPolyPolygon
.append(aNewB2DPolygon
);
2113 aNewB2DPolygon
.clear();
2115 if ( rSrcPt
< nCoordSize
)
2117 const Point
aTempPoint(GetPoint( seqCoordinates
[ rSrcPt
++ ], true, true ));
2120 "moveTo: " << aTempPoint
.X() << ","
2122 aNewB2DPolygon
.append(basegfx::B2DPoint(aTempPoint
.X(), aTempPoint
.Y()));
2130 if(aNewB2DPolygon
.count())
2132 if(aNewB2DPolygon
.count() > 1)
2134 aNewB2DPolygon
.setClosed(true);
2135 aNewB2DPolyPolygon
.append(aNewB2DPolygon
);
2138 aNewB2DPolygon
.clear();
2144 for ( sal_uInt16 i
= 0; ( i
< nPntCount
) && ( ( rSrcPt
+ 2 ) < nCoordSize
); i
++ )
2146 const Point
aControlA(GetPoint( seqCoordinates
[ rSrcPt
++ ], true, true ));
2147 const Point
aControlB(GetPoint( seqCoordinates
[ rSrcPt
++ ], true, true ));
2148 const Point
aEnd(GetPoint( seqCoordinates
[ rSrcPt
++ ], true, true ));
2150 DBG_ASSERT(aNewB2DPolygon
.count(), "EnhancedCustomShape2d::CreateSubPath: Error in adding control point (!)");
2151 aNewB2DPolygon
.appendBezierSegment(
2152 basegfx::B2DPoint(aControlA
.X(), aControlA
.Y()),
2153 basegfx::B2DPoint(aControlB
.X(), aControlB
.Y()),
2154 basegfx::B2DPoint(aEnd
.X(), aEnd
.Y()));
2159 case ANGLEELLIPSE
: // command U
2160 case ANGLEELLIPSETO
: // command T
2162 // Some shapes will need special handling, decide on property 'Type'.
2164 SdrCustomShapeGeometryItem
& rGeometryItem
= const_cast<SdrCustomShapeGeometryItem
&>(mrSdrObjCustomShape
.GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY
));
2165 Any
* pAny
= rGeometryItem
.GetPropertyValueByName("Type");
2168 // User defined shapes in MS binary format, which contain command U or T after import
2169 // in LibreOffice, starts with "mso".
2170 const bool bIsFromBinaryImport(sShpType
.startsWith("mso"));
2171 // The only own or imported preset shapes with U command are those listed below.
2172 // Command T is not used in preset shapes.
2173 const std::unordered_set
<OUString
> aPresetShapesWithU
=
2174 { "ellipse", "ring", "smiley", "sun", "forbidden", "flowchart-connector",
2175 "flowchart-summing-junction", "flowchart-or", "cloud-callout"};
2176 std::unordered_set
<OUString
>::const_iterator aIter
= aPresetShapesWithU
.find(sShpType
);
2177 const bool bIsPresetShapeWithU(aIter
!= aPresetShapesWithU
.end());
2179 for (sal_uInt16 i
= 0; (i
< nPntCount
) && ((rSrcPt
+ 2) < nCoordSize
); i
++)
2181 // ANGLEELLIPSE is the same as ANGLEELLIPSETO, only that it
2182 // makes an implicit MOVETO. That ends the previous subpath.
2183 if (ANGLEELLIPSE
== nCommand
)
2185 if (aNewB2DPolygon
.count() > 1)
2187 // #i76201# Add conversion to closed polygon when first and last points are equal
2188 basegfx::utils::checkClosed(aNewB2DPolygon
);
2189 aNewB2DPolyPolygon
.append(aNewB2DPolygon
);
2191 aNewB2DPolygon
.clear();
2194 // Read all parameters, but do not finally handle them.
2195 basegfx::B2DPoint
aCenter(GetPointAsB2DPoint(seqCoordinates
[ rSrcPt
], true, true));
2196 double fWR
; // horizontal ellipse radius
2197 double fHR
; // vertical ellipse radius
2198 GetParameter(fWR
, seqCoordinates
[rSrcPt
+ 1].First
, true, false);
2199 GetParameter(fHR
, seqCoordinates
[rSrcPt
+ 1].Second
, false, true);
2201 GetParameter(fStartAngle
, seqCoordinates
[rSrcPt
+ 2].First
, false, false);
2203 GetParameter(fEndAngle
, seqCoordinates
[rSrcPt
+ 2].Second
, false, false);
2204 // Increasing here allows flat case differentiation tree by using 'continue'.
2207 double fScaledWR(fWR
* fXScale
);
2208 double fScaledHR(fHR
* fYScale
);
2209 if (fScaledWR
== 0.0 && fScaledHR
== 0.0)
2211 // degenerated ellipse, add center point
2212 aNewB2DPolygon
.append(aCenter
);
2216 if (bIsFromBinaryImport
)
2218 // If a shape comes from MS binary ('escher') import, the angles are in degrees*2^16
2219 // and the second angle is not an end angle, but a swing angle.
2220 // MS Word shows this behavior: 0deg right, 90deg top, 180deg left and 270deg
2221 // bottom. Third and forth parameter are horizontal and vertical radius, not width
2222 // and height as noted in VML spec. A positive swing angle goes counter-clock
2223 // wise (in user view). The swing angle might go several times around in case
2224 // abs(swing angle) >= 360deg. Stroke accumulates, so that e.g. dash-dot might fill the
2225 // gaps of previous turn. Fill does not accumulate but uses even-odd rule, semi-transparent
2226 // fill does not become darker. The start and end points of the arc are calculated by
2227 // using the angles on a circle and then scaling the circle to the ellipse. Caution, that
2228 // is different from angle handling in ARCANGLETO and ODF.
2229 // The following implementation generates such rendering. It is only for rendering legacy
2230 // MS shapes and independent of the meaning of commands U and T in ODF specification.
2232 // The WordArt shape 'RingOutside' has already angles in degree, all other need
2233 // conversion from fixed-point number.
2234 double fSwingAngle
= fEndAngle
;
2235 if (sShpType
!= "mso-spt143")
2237 fStartAngle
/= 65536.0;
2238 fSwingAngle
= fEndAngle
/ 65536.0;
2240 // Convert orientation
2241 fStartAngle
= -fStartAngle
;
2242 fSwingAngle
= -fSwingAngle
;
2244 fEndAngle
= fStartAngle
+ fSwingAngle
;
2245 if (fSwingAngle
< 0.0)
2246 std::swap(fStartAngle
, fEndAngle
);
2247 double fFrom(fStartAngle
);
2248 double fTo(fFrom
+ 180.0);
2249 basegfx::B2DPolygon aTempB2DPolygon
;
2250 double fS
; // fFrom in radians in [0..2Pi[
2251 double fE
; // fTo or fEndAngle in radians in [0..2PI[
2252 while (fTo
< fEndAngle
)
2254 fS
= lcl_getNormalizedAngleRad(fFrom
);
2255 fE
= lcl_getNormalizedAngleRad(fTo
);
2256 aTempB2DPolygon
.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fScaledWR
, fScaledHR
, fS
,fE
));
2260 fS
= lcl_getNormalizedAngleRad(fFrom
);
2261 fE
= lcl_getNormalizedAngleRad(fEndAngle
);
2262 aTempB2DPolygon
.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fScaledWR
, fScaledHR
,fS
, fE
));
2263 if (fSwingAngle
< 0)
2264 aTempB2DPolygon
.flip();
2265 aNewB2DPolygon
.append(aTempB2DPolygon
);
2269 // The not yet handled shapes are own preset shapes, or preset shapes from MS binary import, or user
2270 // defined shapes, or foreign shapes. Shapes from OOXML import do not use ANGLEELLIPSE or
2271 // ANGLEELLIPSETO, but use ARCANGLETO.
2272 if (bIsPresetShapeWithU
)
2274 // Besides "cloud-callout" all preset shapes have angle values '0 360'.
2275 // The imported "cloud-callout" has angle values '0 360' too, only our own "cloud-callout"
2276 // has values '0 23592960'. But that is fixedfloat and means 360*2^16. Thus all these shapes
2277 // have a full ellipse with start at 0deg.
2278 aNewB2DPolygon
.append(basegfx::utils::createPolygonFromEllipse(aCenter
, fScaledWR
, fScaledHR
));
2282 // In all other cases, full ODF conform handling is necessary. ODF rules:
2283 // Third and forth parameter are horizontal and vertical radius.
2284 // An angle determines the start or end point of the segment by intersection of the second angle
2285 // leg with the ellipse. The first angle leg is always the positive x-axis. For the position
2286 // of the intersection points the angle is used modulo 360deg in range [0deg..360deg[.
2287 // The position of range [0deg..360deg[ is the same as in command ARCANGLETO, with 0deg right,
2288 // 90deg bottom, 180deg left and 270deg top. Only if abs(end angle - start angle) == 360 deg,
2289 // a full ellipse is drawn. The segment is always drawn clock wise (in user view) from start
2290 // point to end point. The end point of the segment becomes the new "current" point.
2292 if (fabs(fabs(fEndAngle
- fStartAngle
) - 360.0) < 1.0E-15)
2294 // draw full ellipse
2295 // Because createPolygonFromEllipseSegment cannot create full ellipse and
2296 // createPolygonFromEllipse has no varying starts, we use two half ellipses.
2297 const double fS(lcl_getNormalizedCircleAngleRad(fWR
, fHR
, fStartAngle
));
2298 const double fH(lcl_getNormalizedCircleAngleRad(fWR
, fHR
, fStartAngle
+ 180.0));
2299 const double fE(lcl_getNormalizedCircleAngleRad(fWR
, fHR
, fEndAngle
));
2300 aNewB2DPolygon
.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fScaledWR
, fScaledHR
, fS
, fH
));
2301 aNewB2DPolygon
.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fScaledWR
, fScaledHR
, fH
, fE
));
2305 // remaining cases with central segment angle < 360
2306 double fS(lcl_getNormalizedCircleAngleRad(fWR
, fHR
, fStartAngle
));
2307 double fE(lcl_getNormalizedCircleAngleRad(fWR
, fHR
, fEndAngle
));
2308 aNewB2DPolygon
.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fScaledWR
, fScaledHR
, fS
, fE
));
2313 case QUADRATICCURVETO
:
2315 for ( sal_Int32
i(0); ( i
< nPntCount
) && ( rSrcPt
+ 1 < nCoordSize
); i
++ )
2317 DBG_ASSERT(aNewB2DPolygon
.count(), "EnhancedCustomShape2d::CreateSubPath: Error no previous point for Q (!)");
2318 if (aNewB2DPolygon
.count() > 0)
2320 const basegfx::B2DPoint
aPreviousEndPoint(aNewB2DPolygon
.getB2DPoint(aNewB2DPolygon
.count()-1));
2321 const basegfx::B2DPoint
aControlQ(GetPointAsB2DPoint( seqCoordinates
[ rSrcPt
++ ], true, true ));
2322 const basegfx::B2DPoint
aEnd(GetPointAsB2DPoint( seqCoordinates
[ rSrcPt
++ ], true, true ));
2323 const basegfx::B2DPoint
aControlA((aPreviousEndPoint
+ (aControlQ
* 2)) / 3);
2324 const basegfx::B2DPoint
aControlB(((aControlQ
* 2) + aEnd
) / 3);
2325 aNewB2DPolygon
.appendBezierSegment(aControlA
, aControlB
, aEnd
);
2327 else // no previous point; ill structured path, but try to draw as much as possible
2329 rSrcPt
++; // skip control point
2330 const basegfx::B2DPoint
aEnd(GetPointAsB2DPoint( seqCoordinates
[ rSrcPt
++ ], true, true ));
2331 aNewB2DPolygon
.append(aEnd
);
2339 for ( sal_Int32
i(0); ( i
< nPntCount
) && ( rSrcPt
< nCoordSize
); i
++ )
2341 const Point
aTempPoint(GetPoint( seqCoordinates
[ rSrcPt
++ ], true, true ));
2344 "lineTo: " << aTempPoint
.X() << ","
2346 aNewB2DPolygon
.append(basegfx::B2DPoint(aTempPoint
.X(), aTempPoint
.Y()));
2354 case CLOCKWISEARCTO
:
2356 bool bClockwise
= ( nCommand
== CLOCKWISEARC
) || ( nCommand
== CLOCKWISEARCTO
);
2357 bool bImplicitMoveTo
= (nCommand
== ARC
) || (nCommand
== CLOCKWISEARC
);
2358 sal_uInt32 nXor
= bClockwise
? 3 : 2;
2359 for ( sal_uInt16 i
= 0; ( i
< nPntCount
) && ( ( rSrcPt
+ 3 ) < nCoordSize
); i
++ )
2361 if (bImplicitMoveTo
)
2363 if (aNewB2DPolygon
.count() > 1)
2365 // #i76201# Add conversion to closed polygon when first and last
2367 basegfx::utils::checkClosed(aNewB2DPolygon
);
2368 aNewB2DPolyPolygon
.append(aNewB2DPolygon
);
2370 aNewB2DPolygon
.clear();
2372 tools::Rectangle aRect
= tools::Rectangle::Normalize( GetPoint( seqCoordinates
[ rSrcPt
], true, true ), GetPoint( seqCoordinates
[ rSrcPt
+ 1 ], true, true ) );
2373 if ( aRect
.GetWidth() && aRect
.GetHeight() )
2375 Point
aStart( GetPoint( seqCoordinates
[ static_cast<sal_uInt16
>( rSrcPt
+ nXor
) ], true, true ) );
2376 Point
aEnd( GetPoint( seqCoordinates
[ static_cast<sal_uInt16
>( rSrcPt
+ ( nXor
^ 1 ) ) ], true, true ) );
2377 aNewB2DPolygon
.append(CreateArc( aRect
, aStart
, aEnd
, bClockwise
));
2386 double fWR
, fHR
; // in Shape coordinate system
2387 double fStartAngle
, fSwingAngle
; // in deg
2389 for ( sal_uInt16 i
= 0; ( i
< nPntCount
) && ( rSrcPt
+ 1 < nCoordSize
); i
++ )
2391 basegfx::B2DPoint aTempPair
;
2392 aTempPair
= GetPointAsB2DPoint(seqCoordinates
[static_cast<sal_uInt16
>(rSrcPt
)], false /*bScale*/, false /*bReplaceGeoSize*/);
2393 fWR
= aTempPair
.getX();
2394 fHR
= aTempPair
.getY();
2395 aTempPair
= GetPointAsB2DPoint(seqCoordinates
[static_cast<sal_uInt16
>(rSrcPt
+ 1)], false /*bScale*/, false /*bReplaceGeoSize*/);
2396 fStartAngle
= aTempPair
.getX();
2397 fSwingAngle
= aTempPair
.getY();
2399 // tdf#122323 MS Office clamps the swing angle to [-360,360]. Such restriction
2400 // is neither in OOXML nor in ODF. Nevertheless, to be compatible we do it for
2401 // "ooxml-foo" shapes. Those shapes have their origin in MS Office.
2404 fSwingAngle
= std::clamp(fSwingAngle
, -360.0, 360.0);
2407 SAL_INFO("svx", "ARCANGLETO scale: " << fWR
<< "x" << fHR
<< " angles: " << fStartAngle
<< "," << fSwingAngle
);
2409 if (aNewB2DPolygon
.count() > 0) // otherwise no "current point"
2411 // use similar methods as in command U
2412 basegfx::B2DPolygon aTempB2DPolygon
;
2414 if (fWR
== 0.0 && fHR
== 0.0)
2416 // degenerated ellipse, add this one point
2417 aTempB2DPolygon
.append(basegfx::B2DPoint(0.0, 0.0));
2421 double fEndAngle
= fStartAngle
+ fSwingAngle
;
2422 // Generate arc with ellipse left|top = 0|0.
2423 basegfx::B2DPoint
aCenter(fWR
, fHR
);
2424 if (fSwingAngle
< 0.0)
2425 std::swap(fStartAngle
, fEndAngle
);
2426 double fS
; // fFrom in radians in [0..2Pi[
2427 double fE
; // fTo or fEndAngle in radians in [0..2PI[
2428 double fFrom(fStartAngle
);
2429 // createPolygonFromEllipseSegment expects angles in [0..2PI[.
2430 if (fSwingAngle
>= 360.0 || fSwingAngle
<= -360.0)
2432 double fTo(fFrom
+ 180.0);
2433 while (fTo
< fEndAngle
)
2435 fS
= lcl_getNormalizedCircleAngleRad(fWR
, fHR
, fFrom
);
2436 fE
= lcl_getNormalizedCircleAngleRad(fWR
, fHR
, fTo
);
2437 aTempB2DPolygon
.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fWR
, fHR
, fS
,fE
));
2442 fS
= lcl_getNormalizedCircleAngleRad(fWR
, fHR
, fFrom
);
2443 fE
= lcl_getNormalizedCircleAngleRad(fWR
, fHR
, fEndAngle
);
2444 aTempB2DPolygon
.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fWR
, fHR
,fS
, fE
));
2445 if (fSwingAngle
< 0)
2446 aTempB2DPolygon
.flip();
2447 aTempB2DPolygon
.removeDoublePoints();
2449 // Scale arc to 1/100mm
2450 basegfx::B2DHomMatrix aMatrix
= basegfx::utils::createScaleB2DHomMatrix(fXScale
, fYScale
);
2451 aTempB2DPolygon
.transform(aMatrix
);
2453 // Now that we have the arc, move it to the "current point".
2454 basegfx::B2DPoint
aCurrentPointB2D( aNewB2DPolygon
.getB2DPoint(aNewB2DPolygon
.count() - 1 ) );
2455 const double fDx(aCurrentPointB2D
.getX() - aTempB2DPolygon
.getB2DPoint(0).getX());
2456 const double fDy(aCurrentPointB2D
.getY() - aTempB2DPolygon
.getB2DPoint(0).getY());
2457 aMatrix
= basegfx::utils::createTranslateB2DHomMatrix(fDx
, fDy
);
2458 aTempB2DPolygon
.transform(aMatrix
);
2459 aNewB2DPolygon
.append(aTempB2DPolygon
);
2467 case ELLIPTICALQUADRANTX
:
2468 case ELLIPTICALQUADRANTY
:
2470 if (nPntCount
&& (rSrcPt
< nCoordSize
))
2472 // The arc starts at the previous point and ends at the point given in the parameter.
2473 basegfx::B2DPoint aStart
;
2474 basegfx::B2DPoint aEnd
;
2478 aStart
= GetPointAsB2DPoint(seqCoordinates
[rSrcPt
- 1], true, true);
2481 { // no previous point, path is ill-structured. But we want to show as much as possible.
2482 // Thus make a moveTo to the point given as parameter and continue from there.
2483 aStart
= GetPointAsB2DPoint(seqCoordinates
[static_cast<sal_uInt16
>(rSrcPt
)], true, true);
2484 aNewB2DPolygon
.append(aStart
);
2488 // If there are several points, then the direction changes with every point.
2489 bool bIsXDirection(nCommand
== ELLIPTICALQUADRANTX
);
2490 basegfx::B2DPolygon aArc
;
2491 for ( ; ( i
< nPntCount
) && ( rSrcPt
< nCoordSize
); i
++ )
2493 aEnd
= GetPointAsB2DPoint(seqCoordinates
[rSrcPt
], true, true);
2494 basegfx::B2DPoint aCenter
;
2495 double fRadiusX
= fabs(aEnd
.getX() - aStart
.getX());
2496 double fRadiusY
= fabs(aEnd
.getY() - aStart
.getY());
2499 aCenter
= basegfx::B2DPoint(aStart
.getX(),aEnd
.getY());
2500 if (aEnd
.getX()<aStart
.getX())
2502 if (aEnd
.getY()<aStart
.getY()) // left, up
2504 aArc
= basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fRadiusX
, fRadiusY
, M_PI_2
, M_PI
);
2508 aArc
= basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fRadiusX
, fRadiusY
, M_PI
, 1.5*M_PI
);
2512 else // aEnd.getX()>=aStart.getX()
2514 if (aEnd
.getY()<aStart
.getY()) // right, up
2516 aArc
= basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fRadiusX
, fRadiusY
, 0.0, M_PI_2
);
2521 aArc
= basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fRadiusX
, fRadiusY
, 1.5*M_PI
, 2*M_PI
);
2527 aCenter
= basegfx::B2DPoint(aEnd
.getX(),aStart
.getY());
2528 if (aEnd
.getX()<aStart
.getX())
2530 if (aEnd
.getY()<aStart
.getY()) // up, left
2532 aArc
= basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fRadiusX
, fRadiusY
, 1.5*M_PI
, 2*M_PI
);
2537 aArc
= basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fRadiusX
, fRadiusY
, 0.0, M_PI_2
);
2540 else // aEnd.getX()>=aStart.getX()
2542 if (aEnd
.getY()<aStart
.getY()) // up, right
2544 aArc
= basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fRadiusX
, fRadiusY
, M_PI
, 1.5*M_PI
);
2548 aArc
= basegfx::utils::createPolygonFromEllipseSegment(aCenter
, fRadiusX
, fRadiusY
, M_PI_2
, M_PI
);
2553 aNewB2DPolygon
.append(aArc
);
2555 bIsXDirection
= !bIsXDirection
;
2559 // else error in path syntax, do nothing
2563 #ifdef DBG_CUSTOMSHAPE
2567 SAL_WARN( "svx", "CustomShapes::unknown PolyFlagValue :" << nCommand
);
2572 if ( nCommand
== ENDSUBPATH
)
2576 if ( rSegmentInd
== nSegInfoSize
)
2579 if(aNewB2DPolygon
.count() > 1)
2581 // #i76201# Add conversion to closed polygon when first and last points are equal
2582 basegfx::utils::checkClosed(aNewB2DPolygon
);
2583 aNewB2DPolyPolygon
.append(aNewB2DPolygon
);
2586 if(!aNewB2DPolyPolygon
.count())
2590 bool bForceCreateTwoObjects(false);
2592 if(!bSortFilledObjectsToBack
&& !aNewB2DPolyPolygon
.isClosed() && !bNoStroke
)
2594 bForceCreateTwoObjects
= true;
2597 if(bLineGeometryNeededOnly
)
2599 bForceCreateTwoObjects
= true;
2604 if(bForceCreateTwoObjects
|| bSortFilledObjectsToBack
)
2606 if(bFilled
&& !bNoFill
)
2608 basegfx::B2DPolyPolygon
aClosedPolyPolygon(aNewB2DPolyPolygon
);
2609 aClosedPolyPolygon
.setClosed(true);
2610 rtl::Reference
<SdrPathObj
> pFill(new SdrPathObj(
2611 mrSdrObjCustomShape
.getSdrModelFromSdrObject(),
2612 SdrObjKind::Polygon
,
2613 std::move(aClosedPolyPolygon
)));
2614 SfxItemSet
aTempSet(*this);
2615 aTempSet
.Put(makeSdrShadowItem(false));
2616 aTempSet
.Put(XLineStyleItem(drawing::LineStyle_NONE
));
2617 pFill
->SetMergedItemSet(aTempSet
);
2618 rObjectList
.push_back(std::pair
< rtl::Reference
<SdrPathObj
>, double >(std::move(pFill
), dBrightness
));
2623 // there is no reason to use OBJ_PLIN here when the polygon is actually closed,
2624 // the non-fill is defined by XFILL_NONE. Since SdrPathObj::ImpForceKind() needs
2625 // to correct the polygon (here: open it) using the type, the last edge may get lost.
2626 // Thus, use a type that fits the polygon
2627 rtl::Reference
<SdrPathObj
> pStroke(new SdrPathObj(
2628 mrSdrObjCustomShape
.getSdrModelFromSdrObject(),
2629 aNewB2DPolyPolygon
.isClosed() ? SdrObjKind::Polygon
: SdrObjKind::PolyLine
,
2630 aNewB2DPolyPolygon
));
2631 SfxItemSet
aTempSet(*this);
2632 aTempSet
.Put(makeSdrShadowItem(false));
2633 aTempSet
.Put(XFillStyleItem(drawing::FillStyle_NONE
));
2634 pStroke
->SetMergedItemSet(aTempSet
);
2635 rObjectList
.push_back(std::pair
< rtl::Reference
<SdrPathObj
>, double >(std::move(pStroke
), dBrightness
));
2640 rtl::Reference
<SdrPathObj
> pObj
;
2641 SfxItemSet
aTempSet(*this);
2642 aTempSet
.Put(makeSdrShadowItem(false));
2646 // see comment above about OBJ_PLIN
2647 pObj
= new SdrPathObj(
2648 mrSdrObjCustomShape
.getSdrModelFromSdrObject(),
2649 aNewB2DPolyPolygon
.isClosed() ? SdrObjKind::Polygon
: SdrObjKind::PolyLine
,
2650 aNewB2DPolyPolygon
);
2651 aTempSet
.Put(XFillStyleItem(drawing::FillStyle_NONE
));
2655 aNewB2DPolyPolygon
.setClosed(true);
2656 pObj
= new SdrPathObj(
2657 mrSdrObjCustomShape
.getSdrModelFromSdrObject(),
2658 SdrObjKind::Polygon
,
2659 aNewB2DPolyPolygon
);
2664 aTempSet
.Put(XLineStyleItem(drawing::LineStyle_NONE
));
2667 pObj
->SetMergedItemSet(aTempSet
);
2668 rObjectList
.push_back(std::pair
< rtl::Reference
<SdrPathObj
>, double >(std::move(pObj
), dBrightness
));
2672 static void CorrectCalloutArrows(
2674 sal_uInt32 nLineObjectCount
,
2675 std::vector
< std::pair
< rtl::Reference
<SdrPathObj
>, double> >& vObjectList
)
2677 bool bAccent
= false;
2680 case mso_sptCallout1
:
2681 case mso_sptBorderCallout1
:
2682 case mso_sptCallout90
:
2683 case mso_sptBorderCallout90
:
2687 case mso_sptAccentCallout1
:
2688 case mso_sptAccentBorderCallout1
:
2689 case mso_sptAccentCallout90
:
2690 case mso_sptAccentBorderCallout90
:
2692 sal_uInt32 nLine
= 0;
2694 for ( const std::pair
< rtl::Reference
<SdrPathObj
>, double >& rCandidate
: vObjectList
)
2696 SdrPathObj
* pObj(rCandidate
.first
.get());
2701 if ( nLine
== nLineObjectCount
)
2703 pObj
->ClearMergedItem( XATTR_LINESTART
);
2704 pObj
->ClearMergedItem( XATTR_LINEEND
);
2711 // switch start & end
2712 case mso_sptAccentCallout2
:
2713 case mso_sptAccentBorderCallout2
:
2716 case mso_sptCallout2
:
2717 case mso_sptBorderCallout2
:
2719 sal_uInt32 nLine
= 0;
2721 for ( const std::pair
< rtl::Reference
<SdrPathObj
>, double >& rCandidate
: vObjectList
)
2723 SdrPathObj
* pObj(rCandidate
.first
.get());
2729 pObj
->ClearMergedItem( XATTR_LINEEND
);
2730 else if ( ( bAccent
&& ( nLine
== nLineObjectCount
- 1 ) ) || ( !bAccent
&& ( nLine
== nLineObjectCount
) ) )
2731 pObj
->ClearMergedItem( XATTR_LINESTART
);
2734 pObj
->ClearMergedItem( XATTR_LINESTART
);
2735 pObj
->ClearMergedItem( XATTR_LINEEND
);
2742 case mso_sptAccentCallout3
:
2743 case mso_sptAccentBorderCallout3
:
2744 case mso_sptCallout3
:
2745 case mso_sptBorderCallout3
:
2747 sal_uInt32 nLine
= 0;
2749 for ( const std::pair
< rtl::Reference
<SdrPathObj
>, double >& rCandidate
: vObjectList
)
2751 SdrPathObj
* pObj(rCandidate
.first
.get());
2757 pObj
->ClearMergedItem( XATTR_LINESTART
);
2758 pObj
->ClearMergedItem( XATTR_LINEEND
);
2761 EnhancedCustomShape2d::SwapStartAndEndArrow( pObj
);
2771 void EnhancedCustomShape2d::AdaptObjColor(
2774 const SfxItemSet
& rCustomShapeSet
,
2775 sal_uInt32
& nColorIndex
,
2776 sal_uInt32 nColorCount
)
2778 if ( rObj
.IsLine() )
2781 const drawing::FillStyle eFillStyle
= rObj
.GetMergedItem(XATTR_FILLSTYLE
).GetValue();
2782 if (eFillStyle
== drawing::FillStyle_NONE
)
2785 switch( eFillStyle
)
2788 case drawing::FillStyle_SOLID
:
2790 if ( nColorCount
|| 0.0 != dBrightness
)
2792 Color aFillColor
= GetColorData(
2793 rCustomShapeSet
.Get( XATTR_FILLCOLOR
).GetColorValue(),
2794 std::min(nColorIndex
, nColorCount
-1),
2796 rObj
.SetMergedItem( XFillColorItem( "", aFillColor
) );
2800 case drawing::FillStyle_GRADIENT
:
2802 basegfx::BGradient
aBGradient(rObj
.GetMergedItem(XATTR_FILLGRADIENT
).GetGradientValue());
2804 if ( nColorCount
|| 0.0 != dBrightness
)
2806 basegfx::BColorStops
aColorStops(aBGradient
.GetColorStops());
2807 for (auto& candidate
: aColorStops
)
2809 candidate
= basegfx::BColorStop(
2810 candidate
.getStopOffset(),
2812 Color(candidate
.getStopColor()),
2813 std::min(nColorIndex
, nColorCount
-1),
2814 dBrightness
).getBColor());
2816 aBGradient
.SetColorStops(aColorStops
);
2819 rObj
.SetMergedItem( XFillGradientItem( "", aBGradient
) );
2822 case drawing::FillStyle_HATCH
:
2824 XHatch
aXHatch(rObj
.GetMergedItem(XATTR_FILLHATCH
).GetHatchValue());
2826 if ( nColorCount
|| 0.0 != dBrightness
)
2831 std::min(nColorIndex
, nColorCount
-1),
2835 rObj
.SetMergedItem( XFillHatchItem( "", aXHatch
) );
2838 case drawing::FillStyle_BITMAP
:
2840 if ( nColorCount
|| 0.0 != dBrightness
)
2842 BitmapEx
aBitmap(rObj
.GetMergedItem(XATTR_FILLBITMAP
).GetGraphicObject().GetGraphic().GetBitmapEx());
2844 short nLuminancePercent
= static_cast< short > ( GetLuminanceChange(
2845 std::min(nColorIndex
, nColorCount
-1)));
2846 aBitmap
.Adjust( nLuminancePercent
, 0, 0, 0, 0 );
2848 rObj
.SetMergedItem(XFillBitmapItem(OUString(), Graphic(aBitmap
)));
2855 if ( nColorIndex
< nColorCount
)
2859 rtl::Reference
<SdrObject
> EnhancedCustomShape2d::CreatePathObj( bool bLineGeometryNeededOnly
)
2861 if ( !seqCoordinates
.hasElements() )
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);
2871 rtl::Reference
<SdrObject
> pRet
;
2873 while( nSegmentInd
<= seqSegments
.getLength() )
2879 bLineGeometryNeededOnly
,
2880 bSortFilledObjectsToBack
,
2885 if ( !vObjectList
.empty() )
2887 const SfxItemSet
& rCustomShapeSet(mrSdrObjCustomShape
.GetMergedItemSet());
2888 const sal_uInt32
nColorCount(nColorData
>> 28);
2889 sal_uInt32
nColorIndex(0);
2891 // #i37011# remove invisible objects
2892 std::vector
< std::pair
< rtl::Reference
<SdrPathObj
>, double> > vNewList
;
2894 for ( std::pair
< rtl::Reference
<SdrPathObj
>, double >& rCandidate
: vObjectList
)
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();
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
));
2906 vObjectList
= std::move(vNewList
);
2908 if(1 == vObjectList
.size())
2910 // a single object, correct some values
2912 *vObjectList
.begin()->first
,
2913 vObjectList
.begin()->second
,
2920 sal_Int32
nLineObjectCount(0);
2922 // correct some values and collect content data
2923 for ( const std::pair
< rtl::Reference
<SdrPathObj
>, double >& rCandidate
: vObjectList
)
2925 SdrPathObj
* pObj(rCandidate
.first
.get());
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.
2945 pObj
->setFillGeometryDefiningShape(&mrSdrObjCustomShape
);
2949 // #i88870# correct line arrows for callouts
2950 if ( nLineObjectCount
)
2952 CorrectCalloutArrows(
2958 // sort objects so that filled ones are in front. Necessary
2959 // for some strange objects
2960 if(bSortFilledObjectsToBack
)
2962 std::vector
< std::pair
< rtl::Reference
<SdrPathObj
>, double> > vTempList
;
2963 vTempList
.reserve(vObjectList
.size());
2965 for ( std::pair
< rtl::Reference
<SdrPathObj
>, double >& rCandidate
: vObjectList
)
2967 SdrPathObj
* pObj(rCandidate
.first
.get());
2968 if ( !pObj
->IsLine() )
2969 vTempList
.push_back(std::move(rCandidate
));
2972 for ( std::pair
< rtl::Reference
<SdrPathObj
>, double >& rCandidate
: vObjectList
)
2974 if ( rCandidate
.first
)
2975 vTempList
.push_back(std::move(rCandidate
));
2978 vObjectList
= std::move(vTempList
);
2984 if(!vObjectList
.empty())
2986 // copy remaining objects to pRet
2987 if(vObjectList
.size() > 1)
2989 pRet
= new SdrObjGroup(mrSdrObjCustomShape
.getSdrModelFromSdrObject());
2991 for ( std::pair
< rtl::Reference
<SdrPathObj
>, double >& rCandidate
: vObjectList
)
2993 pRet
->GetSubList()->NbcInsertObject(rCandidate
.first
.get());
2996 else if(1 == vObjectList
.size())
2998 pRet
= vObjectList
.begin()->first
;
3003 // move to target position
3004 tools::Rectangle
aCurRect(pRet
->GetSnapRect());
3005 aCurRect
.Move(aLogicRect
.Left(), aLogicRect
.Top());
3006 pRet
->NbcSetSnapRect(aCurRect
);
3013 rtl::Reference
<SdrObject
> EnhancedCustomShape2d::CreateObject( bool bLineGeometryNeededOnly
)
3015 rtl::Reference
<SdrObject
> pRet
;
3017 if ( eSpType
== mso_sptRectangle
)
3019 pRet
= new SdrRectObj(mrSdrObjCustomShape
.getSdrModelFromSdrObject(), aLogicRect
);
3020 pRet
->SetMergedItemSet( *this );
3023 pRet
= CreatePathObj( bLineGeometryNeededOnly
);
3028 void EnhancedCustomShape2d::ApplyGluePoints( SdrObject
* pObj
)
3033 for ( const auto& rGluePoint
: std::as_const(seqGluePoints
) )
3035 SdrGluePoint aGluePoint
;
3037 aGluePoint
.SetPos( GetPoint( rGluePoint
, !bOOXMLShape
, true ) );
3038 aGluePoint
.SetPercent( false );
3039 aGluePoint
.SetAlign( SdrAlign::VERT_TOP
| SdrAlign::HORZ_LEFT
);
3040 aGluePoint
.SetEscDir( SdrEscapeDirection::SMART
);
3041 SdrGluePointList
* pList
= pObj
->ForceGluePointList();
3043 /* sal_uInt16 nId = */ pList
->Insert( aGluePoint
);
3047 rtl::Reference
<SdrObject
> EnhancedCustomShape2d::CreateLineGeometry()
3049 return CreateObject( true );
3053 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */