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