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