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