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