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