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