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