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 "ximpcustomshape.hxx"
21 #include <o3tl/any.hxx>
22 #include <rtl/math.hxx>
23 #include <rtl/ustrbuf.hxx>
24 #include <rtl/ustring.hxx>
25 #include <com/sun/star/uno/Reference.h>
26 #include <com/sun/star/awt/Rectangle.hpp>
27 #include <com/sun/star/xml/sax/XAttributeList.hpp>
28 #include <xmloff/xmltoken.hxx>
29 #include <EnhancedCustomShapeToken.hxx>
30 #include <xmloff/xmlimp.hxx>
31 #include <xmloff/namespacemap.hxx>
32 #include <xmloff/xmluconv.hxx>
33 #include <xmloff/xmlement.hxx>
34 #include <xexptran.hxx>
35 #include <com/sun/star/drawing/Direction3D.hpp>
36 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
37 #include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
38 #include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
39 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
40 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
41 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
42 #include <com/sun/star/drawing/EnhancedCustomShapeTextPathMode.hpp>
43 #include <com/sun/star/drawing/ProjectionMode.hpp>
44 #include <com/sun/star/drawing/Position3D.hpp>
45 #include <sax/tools/converter.hxx>
46 #include <comphelper/sequence.hxx>
47 #include <memory>
48 #include <string_view>
49 #include <unordered_map>
50 
51 using namespace ::com::sun::star;
52 using namespace ::xmloff::token;
53 using namespace ::xmloff::EnhancedCustomShapeToken;
54 
55 
XMLEnhancedCustomShapeContext(SvXMLImport & rImport,css::uno::Reference<css::drawing::XShape> & rxShape,std::vector<css::beans::PropertyValue> & rCustomShapeGeometry)56 XMLEnhancedCustomShapeContext::XMLEnhancedCustomShapeContext( SvXMLImport& rImport,
57             css::uno::Reference< css::drawing::XShape >& rxShape,
58             std::vector< css::beans::PropertyValue >& rCustomShapeGeometry ) :
59         SvXMLImportContext( rImport ),
60         mrUnitConverter( rImport.GetMM100UnitConverter() ),
61         mrxShape( rxShape ),
62         mrCustomShapeGeometry( rCustomShapeGeometry )
63 {
64 }
65 
66 const SvXMLEnumMapEntry<sal_uInt16> aXML_GluePointEnumMap[] =
67 {
68     { XML_NONE,         0 },
69     { XML_SEGMENTS,     1 },
70     { XML_NONE,         2 },
71     { XML_RECTANGLE,    3 },
72     { XML_TOKEN_INVALID, 0 }
73 };
GetBool(std::vector<css::beans::PropertyValue> & rDest,std::string_view rValue,const EnhancedCustomShapeTokenEnum eDestProp)74 static void GetBool( std::vector< css::beans::PropertyValue >& rDest,
75                         std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp )
76 {
77     bool bAttrBool;
78     if (::sax::Converter::convertBool( bAttrBool, rValue ))
79     {
80         beans::PropertyValue aProp;
81         aProp.Name = EASGet( eDestProp );
82         aProp.Value <<= bAttrBool;
83         rDest.push_back( aProp );
84     }
85 }
86 
GetInt32(std::vector<css::beans::PropertyValue> & rDest,std::string_view rValue,const EnhancedCustomShapeTokenEnum eDestProp)87 static void GetInt32( std::vector< css::beans::PropertyValue >& rDest,
88                         std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp )
89 {
90     sal_Int32 nAttrNumber;
91     if (::sax::Converter::convertNumber( nAttrNumber, rValue ))
92     {
93         beans::PropertyValue aProp;
94         aProp.Name = EASGet( eDestProp );
95         aProp.Value <<= nAttrNumber;
96         rDest.push_back( aProp );
97     }
98 }
99 
GetDouble(std::vector<css::beans::PropertyValue> & rDest,std::string_view rValue,const EnhancedCustomShapeTokenEnum eDestProp)100 static void GetDouble( std::vector< css::beans::PropertyValue >& rDest,
101                         std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp )
102 {
103     double fAttrDouble;
104     if (::sax::Converter::convertDouble( fAttrDouble, rValue ))
105     {
106         beans::PropertyValue aProp;
107         aProp.Name = EASGet( eDestProp );
108         aProp.Value <<= fAttrDouble;
109         rDest.push_back( aProp );
110     }
111 }
112 
GetString(std::vector<css::beans::PropertyValue> & rDest,const OUString & rValue,const EnhancedCustomShapeTokenEnum eDestProp)113 static void GetString( std::vector< css::beans::PropertyValue >& rDest,
114                         const OUString& rValue, const EnhancedCustomShapeTokenEnum eDestProp )
115 {
116     beans::PropertyValue aProp;
117     aProp.Name = EASGet( eDestProp );
118     aProp.Value <<= rValue;
119     rDest.push_back( aProp );
120 }
121 
122 template<typename EnumT>
GetEnum(std::vector<css::beans::PropertyValue> & rDest,std::string_view rValue,const EnhancedCustomShapeTokenEnum eDestProp,const SvXMLEnumMapEntry<EnumT> & rMap)123 static void GetEnum( std::vector< css::beans::PropertyValue >& rDest,
124                          std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp,
125                         const SvXMLEnumMapEntry<EnumT>& rMap )
126 {
127     EnumT eKind;
128     if( SvXMLUnitConverter::convertEnum( eKind, rValue, &rMap ) )
129     {
130         beans::PropertyValue aProp;
131         aProp.Name = EASGet( eDestProp );
132         aProp.Value <<= static_cast<sal_Int16>(eKind);
133         rDest.push_back( aProp );
134     }
135 }
136 
GetDoublePercentage(std::vector<css::beans::PropertyValue> & rDest,std::string_view rValue,const EnhancedCustomShapeTokenEnum eDestProp)137 static void GetDoublePercentage( std::vector< css::beans::PropertyValue >& rDest,
138                          std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp )
139 {
140     sal_Int16 const eSrcUnit = ::sax::Converter::GetUnitFromString(
141             rValue, util::MeasureUnit::MM_100TH);
142     if (util::MeasureUnit::PERCENT != eSrcUnit)
143         return;
144 
145     rtl_math_ConversionStatus eStatus;
146     double fAttrDouble = rtl_math_stringToDouble(rValue.data(),
147                                              rValue.data() + rValue.size(),
148                                              '.', ',', &eStatus, nullptr);
149     if ( eStatus == rtl_math_ConversionStatus_Ok )
150     {
151         beans::PropertyValue aProp;
152         aProp.Name = EASGet( eDestProp );
153         aProp.Value <<= fAttrDouble;
154         rDest.push_back( aProp );
155     }
156 }
157 
GetB3DVector(std::vector<css::beans::PropertyValue> & rDest,std::string_view rValue,const EnhancedCustomShapeTokenEnum eDestProp)158 static void GetB3DVector( std::vector< css::beans::PropertyValue >& rDest,
159                          std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp )
160 {
161     ::basegfx::B3DVector aB3DVector;
162     if ( SvXMLUnitConverter::convertB3DVector( aB3DVector, rValue ) )
163     {
164         drawing::Direction3D aDirection3D( aB3DVector.getX(), aB3DVector.getY(), aB3DVector.getZ() );
165         beans::PropertyValue aProp;
166         aProp.Name = EASGet( eDestProp );
167         aProp.Value <<= aDirection3D;
168         rDest.push_back( aProp );
169     }
170 }
171 
GetEquationName(const OUString & rEquation,const sal_Int32 nStart,OUString & rEquationName)172 static bool GetEquationName( const OUString& rEquation, const sal_Int32 nStart, OUString& rEquationName )
173 {
174     sal_Int32 nIndex = nStart;
175     while( nIndex < rEquation.getLength() )
176     {
177         sal_Unicode nChar = rEquation[ nIndex ];
178         if (
179             ( ( nChar >= 'a' ) && ( nChar <= 'z' ) )
180             || ( ( nChar >= 'A' ) && ( nChar <= 'Z' ) )
181             || ( ( nChar >= '0' ) && ( nChar <= '9' ) )
182             )
183         {
184             nIndex++;
185         }
186         else
187             break;
188     }
189     bool bValid = ( nIndex - nStart ) != 0;
190     if ( bValid )
191         rEquationName = rEquation.copy( nStart, nIndex - nStart );
192     return bValid;
193 }
194 
GetNextParameter(css::drawing::EnhancedCustomShapeParameter & rParameter,sal_Int32 & nIndex,const OUString & rParaString)195 static bool GetNextParameter( css::drawing::EnhancedCustomShapeParameter& rParameter, sal_Int32& nIndex, const OUString& rParaString )
196 {
197     if ( nIndex >= rParaString.getLength() )
198         return false;
199 
200     bool bValid = true;
201     bool bNumberRequired = true;
202     bool bMustBePositiveWholeNumbered = false;
203 
204     rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::NORMAL;
205     if ( rParaString[ nIndex ] == '$' )
206     {
207         rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::ADJUSTMENT;
208         bMustBePositiveWholeNumbered = true;
209         nIndex++;
210     }
211     else if ( rParaString[ nIndex ] == '?' )
212     {
213         nIndex++;
214         bNumberRequired = false;
215         OUString aEquationName;
216         bValid = GetEquationName( rParaString, nIndex, aEquationName );
217         if ( bValid )
218         {
219             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::EQUATION;
220             rParameter.Value <<= aEquationName;
221             nIndex += aEquationName.getLength();
222         }
223     }
224     else if ( rParaString[ nIndex ] > '9' )
225     {
226         bNumberRequired = false;
227         if ( rParaString.matchIgnoreAsciiCase( "left", nIndex ) )
228         {
229             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::LEFT;
230             nIndex += 4;
231         }
232         else if ( rParaString.matchIgnoreAsciiCase( "top", nIndex ) )
233         {
234             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::TOP;
235             nIndex += 3;
236         }
237         else if ( rParaString.matchIgnoreAsciiCase( "right", nIndex ) )
238         {
239             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::RIGHT;
240             nIndex += 5;
241         }
242         else if ( rParaString.matchIgnoreAsciiCase( "bottom", nIndex ) )
243         {
244             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::BOTTOM;
245             nIndex += 6;
246         }
247         else if ( rParaString.matchIgnoreAsciiCase( "xstretch", nIndex ) )
248         {
249             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::XSTRETCH;
250             nIndex += 8;
251         }
252         else if ( rParaString.matchIgnoreAsciiCase( "ystretch", nIndex ) )
253         {
254             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::YSTRETCH;
255             nIndex += 8;
256         }
257         else if ( rParaString.matchIgnoreAsciiCase( "hasstroke", nIndex ) )
258         {
259             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::HASSTROKE;
260             nIndex += 9;
261         }
262         else if ( rParaString.matchIgnoreAsciiCase( "hasfill", nIndex ) )
263         {
264             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::HASFILL;
265             nIndex += 7;
266         }
267         else if ( rParaString.matchIgnoreAsciiCase( "width", nIndex ) )
268         {
269             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::WIDTH;
270             nIndex += 5;
271         }
272         else if ( rParaString.matchIgnoreAsciiCase( "height", nIndex ) )
273         {
274             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::HEIGHT;
275             nIndex += 6;
276         }
277         else if ( rParaString.matchIgnoreAsciiCase( "logwidth", nIndex ) )
278         {
279             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::LOGWIDTH;
280             nIndex += 8;
281         }
282         else if ( rParaString.matchIgnoreAsciiCase( "logheight", nIndex ) )
283         {
284             rParameter.Type = css::drawing::EnhancedCustomShapeParameterType::LOGHEIGHT;
285             nIndex += 9;
286         }
287         else
288             bValid = false;
289     }
290     if ( bValid )
291     {
292         if ( bNumberRequired )
293         {
294             sal_Int32 nStartIndex = nIndex;
295             sal_Int32 nEIndex = 0;  // index of "E" in double
296 
297             bool bE = false;    // set if a double is including a "E" statement
298             bool bENum = false; // there is at least one number after "E"
299             bool bDot = false;  // set if there is a dot included
300             bool bEnd = false;  // set for each value that can not be part of a double/integer
301 
302             while( ( nIndex < rParaString.getLength() ) && bValid )
303             {
304                 switch( rParaString[ nIndex ] )
305                 {
306                     case '.' :
307                     {
308                         if ( bMustBePositiveWholeNumbered )
309                             bValid = false;
310                         else
311                         {
312                             if ( bDot )
313                                 bValid = false;
314                             else
315                                 bDot = true;
316                         }
317                     }
318                     break;
319                     case '-' :
320                     {
321                         if ( bMustBePositiveWholeNumbered )
322                             bValid = false;
323                         else
324                         {
325                             if ( nStartIndex == nIndex )
326                                bValid = true;
327                             else if ( bE )
328                             {
329                                 if ( nEIndex + 1 == nIndex )
330                                     bValid = true;
331                                 else if ( bENum )
332                                     bEnd = true;
333                                 else
334                                     bValid = false;
335                             }
336                         }
337                     }
338                     break;
339 
340                     case 'e' :
341                     case 'E' :
342                     {
343                         if ( bMustBePositiveWholeNumbered )
344                             bEnd = true;
345                         else
346                         {
347                             if ( !bE )
348                             {
349                                 bE = true;
350                                 nEIndex = nIndex;
351                             }
352                             else
353                                 bEnd = true;
354                         }
355                     }
356                     break;
357                     case '0' :
358                     case '1' :
359                     case '2' :
360                     case '3' :
361                     case '4' :
362                     case '5' :
363                     case '6' :
364                     case '7' :
365                     case '8' :
366                     case '9' :
367                     {
368                         if ( bE && ! bENum )
369                             bENum = true;
370                     }
371                     break;
372                     default:
373                         bEnd = true;
374                 }
375                 if ( !bEnd )
376                     nIndex++;
377                 else
378                     break;
379             }
380             if ( nIndex == nStartIndex )
381                 bValid = false;
382             if ( bValid )
383             {
384                 OUString aNumber( rParaString.copy( nStartIndex, nIndex - nStartIndex ) );
385                 if ( bE || bDot )
386                 {
387                     double fAttrDouble;
388                     if (::sax::Converter::convertDouble(fAttrDouble, aNumber))
389                         rParameter.Value <<= fAttrDouble;
390                     else
391                         bValid = false;
392                 }
393                 else
394                 {
395                     sal_Int32 nValue;
396                     if (::sax::Converter::convertNumber(nValue, aNumber))
397                         rParameter.Value <<= nValue;
398                     else
399                         bValid = false;
400                 }
401             }
402         }
403     }
404     if ( bValid )
405     {
406         // skipping white spaces and commas (#i121507#)
407         const sal_Unicode aSpace(' ');
408         const sal_Unicode aCommata(',');
409 
410         while(nIndex < rParaString.getLength())
411         {
412             const sal_Unicode aCandidate(rParaString[nIndex]);
413 
414             if(aSpace == aCandidate || aCommata == aCandidate)
415             {
416                 nIndex++;
417             }
418             else
419             {
420                 break;
421             }
422         }
423     }
424     return bValid;
425 }
426 
GetPosition3D(std::vector<css::beans::PropertyValue> & rDest,std::string_view rValue,const EnhancedCustomShapeTokenEnum eDestProp,SvXMLUnitConverter & rUnitConverter)427 static void GetPosition3D( std::vector< css::beans::PropertyValue >& rDest,                     // e.g. draw:extrusion-viewpoint
428                         std::string_view rValue, const EnhancedCustomShapeTokenEnum eDestProp,
429                         SvXMLUnitConverter& rUnitConverter )
430 {
431     drawing::Position3D aPosition3D;
432     if ( rUnitConverter.convertPosition3D( aPosition3D, rValue ) )
433     {
434         beans::PropertyValue aProp;
435         aProp.Name = EASGet( eDestProp );
436         aProp.Value <<= aPosition3D;
437         rDest.push_back( aProp );
438     }
439 }
440 
GetDoubleSequence(std::vector<css::beans::PropertyValue> & rDest,const OUString & rValue,const EnhancedCustomShapeTokenEnum eDestProp)441 static void GetDoubleSequence( std::vector< css::beans::PropertyValue >& rDest,                 // e.g. draw:glue-point-leaving-directions
442                         const OUString& rValue, const EnhancedCustomShapeTokenEnum eDestProp )
443 {
444     std::vector< double > vDirection;
445     sal_Int32 nIndex = 0;
446     do
447     {
448         double fAttrDouble;
449         OUString aToken( rValue.getToken( 0, ',', nIndex ) );
450         if (!::sax::Converter::convertDouble( fAttrDouble, aToken ))
451             break;
452         else
453             vDirection.push_back( fAttrDouble );
454     }
455     while ( nIndex >= 0 );
456 
457     if ( !vDirection.empty() )
458     {
459         beans::PropertyValue aProp;
460         aProp.Name = EASGet( eDestProp );
461         aProp.Value <<= comphelper::containerToSequence(vDirection);
462         rDest.push_back( aProp );
463     }
464 }
465 
GetSizeSequence(std::vector<css::beans::PropertyValue> & rDest,const OUString & rValue,const EnhancedCustomShapeTokenEnum eDestProp)466 static void GetSizeSequence( std::vector< css::beans::PropertyValue >& rDest,
467                       const OUString& rValue, const EnhancedCustomShapeTokenEnum eDestProp )
468 {
469     std::vector< sal_Int32 > vNum;
470     sal_Int32 nIndex = 0;
471     do
472     {
473         sal_Int32 n;
474         OUString aToken( rValue.getToken( 0, ' ', nIndex ) );
475         if (!::sax::Converter::convertNumber( n, aToken ))
476             break;
477         else
478             vNum.push_back( n );
479     }
480     while ( nIndex >= 0 );
481 
482     if ( vNum.empty() )
483         return;
484 
485     uno::Sequence< awt::Size > aSizeSeq((vNum.size() + 1) / 2);
486     std::vector< sal_Int32 >::const_iterator aIter = vNum.begin();
487     std::vector< sal_Int32 >::const_iterator aEnd = vNum.end();
488     awt::Size* pValues = aSizeSeq.getArray();
489 
490     while ( aIter != aEnd ) {
491         pValues->Width = *aIter++;
492         if ( aIter != aEnd )
493             pValues->Height = *aIter++;
494         pValues ++;
495     }
496 
497     beans::PropertyValue aProp;
498     aProp.Name = EASGet( eDestProp );
499     aProp.Value <<= aSizeSeq;
500     rDest.push_back( aProp );
501 }
502 
GetEnhancedParameter(std::vector<css::beans::PropertyValue> & rDest,const OUString & rValue,const EnhancedCustomShapeTokenEnum eDestProp)503 static void GetEnhancedParameter( std::vector< css::beans::PropertyValue >& rDest,              // e.g. draw:handle-position
504                         const OUString& rValue, const EnhancedCustomShapeTokenEnum eDestProp )
505 {
506     sal_Int32 nIndex = 0;
507     css::drawing::EnhancedCustomShapeParameter aParameter;
508     if ( GetNextParameter( aParameter, nIndex, rValue ) )
509     {
510         beans::PropertyValue aProp;
511         aProp.Name = EASGet( eDestProp );
512         aProp.Value <<= aParameter;
513         rDest.push_back( aProp );
514     }
515 }
516 
GetEnhancedParameterPair(std::vector<css::beans::PropertyValue> & rDest,const OUString & rValue,const EnhancedCustomShapeTokenEnum eDestProp)517 static void GetEnhancedParameterPair( std::vector< css::beans::PropertyValue >& rDest,          // e.g. draw:handle-position
518                         const OUString& rValue, const EnhancedCustomShapeTokenEnum eDestProp )
519 {
520     sal_Int32 nIndex = 0;
521     css::drawing::EnhancedCustomShapeParameterPair aParameterPair;
522     if ( GetNextParameter( aParameterPair.First, nIndex, rValue )
523         && GetNextParameter( aParameterPair.Second, nIndex, rValue ) )
524     {
525         beans::PropertyValue aProp;
526         aProp.Name = EASGet( eDestProp );
527         aProp.Value <<= aParameterPair;
528         rDest.push_back( aProp );
529     }
530 }
531 
GetEnhancedParameterPairSequence(std::vector<css::beans::PropertyValue> & rDest,const OUString & rValue,const EnhancedCustomShapeTokenEnum eDestProp)532 static sal_Int32 GetEnhancedParameterPairSequence( std::vector< css::beans::PropertyValue >& rDest,     // e.g. draw:glue-points
533                         const OUString& rValue, const EnhancedCustomShapeTokenEnum eDestProp )
534 {
535     std::vector< css::drawing::EnhancedCustomShapeParameterPair > vParameter;
536     css::drawing::EnhancedCustomShapeParameterPair aParameter;
537 
538     sal_Int32 nIndex = 0;
539     while ( GetNextParameter( aParameter.First, nIndex, rValue )
540             && GetNextParameter( aParameter.Second, nIndex, rValue ) )
541     {
542         vParameter.push_back( aParameter );
543     }
544     if ( !vParameter.empty() )
545     {
546         beans::PropertyValue aProp;
547         aProp.Name = EASGet( eDestProp );
548         aProp.Value <<= comphelper::containerToSequence(vParameter);
549         rDest.push_back( aProp );
550     }
551     return vParameter.size();
552 }
553 
GetEnhancedRectangleSequence(std::vector<css::beans::PropertyValue> & rDest,const OUString & rValue,const EnhancedCustomShapeTokenEnum eDestProp)554 static void GetEnhancedRectangleSequence( std::vector< css::beans::PropertyValue >& rDest,      // e.g. draw:text-areas
555                         const OUString& rValue, const EnhancedCustomShapeTokenEnum eDestProp )
556 {
557     std::vector< css::drawing::EnhancedCustomShapeTextFrame > vTextFrame;
558     css::drawing::EnhancedCustomShapeTextFrame aParameter;
559 
560     sal_Int32 nIndex = 0;
561 
562     while ( GetNextParameter( aParameter.TopLeft.First, nIndex, rValue )
563             && GetNextParameter( aParameter.TopLeft.Second, nIndex, rValue )
564             && GetNextParameter( aParameter.BottomRight.First, nIndex, rValue )
565             && GetNextParameter( aParameter.BottomRight.Second, nIndex, rValue ) )
566     {
567         vTextFrame.push_back( aParameter );
568     }
569     if ( !vTextFrame.empty() )
570     {
571         beans::PropertyValue aProp;
572         aProp.Name = EASGet( eDestProp );
573         aProp.Value <<= comphelper::containerToSequence(vTextFrame);
574         rDest.push_back( aProp );
575     }
576 }
577 
GetEnhancedPath(std::vector<css::beans::PropertyValue> & rDest,const OUString & rValue)578 static void GetEnhancedPath( std::vector< css::beans::PropertyValue >& rDest,                   // e.g. draw:enhanced-path
579                         const OUString& rValue )
580 {
581     std::vector< css::drawing::EnhancedCustomShapeParameterPair >    vCoordinates;
582     std::vector< css::drawing::EnhancedCustomShapeSegment >      vSegments;
583 
584     sal_Int32 nIndex = 0;
585     sal_Int32 nParameterCount = 0;
586 
587     sal_Int32 nParametersNeeded = 1;
588     sal_Int16 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::MOVETO;
589 
590     bool bValid = true;
591 
592     while( bValid && ( nIndex < rValue.getLength() ) )
593     {
594         switch( rValue[ nIndex ] )
595         {
596             case 'M' :
597             {
598                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::MOVETO;
599                 nParametersNeeded = 1;
600                 nIndex++;
601             }
602             break;
603             case 'L' :
604             {
605                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::LINETO;
606                 nParametersNeeded = 1;
607                 nIndex++;
608             }
609             break;
610             case 'C' :
611             {
612                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::CURVETO;
613                 nParametersNeeded = 3;
614                 nIndex++;
615             }
616             break;
617             case 'Z' :
618             {
619                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH;
620                 nParametersNeeded = 0;
621                 nIndex++;
622             }
623             break;
624             case 'N' :
625             {
626                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
627                 nParametersNeeded = 0;
628                 nIndex++;
629             }
630             break;
631             case 'F' :
632             {
633                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::NOFILL;
634                 nParametersNeeded = 0;
635                 nIndex++;
636             }
637             break;
638             case 'S' :
639             {
640                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::NOSTROKE;
641                 nParametersNeeded = 0;
642                 nIndex++;
643             }
644             break;
645             case 'T' :
646             {
647                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO;
648                 nParametersNeeded = 3;
649                 nIndex++;
650             }
651             break;
652             case 'U' :
653             {
654                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE;
655                 nParametersNeeded = 3;
656                 nIndex++;
657             }
658             break;
659             case 'A' :
660             {
661                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ARCTO;
662                 nParametersNeeded = 4;
663                 nIndex++;
664             }
665             break;
666             case 'B' :
667             {
668                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ARC;
669                 nParametersNeeded = 4;
670                 nIndex++;
671             }
672             break;
673             case 'G' :
674             {
675                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ARCANGLETO;
676                 nParametersNeeded = 2;
677                 nIndex++;
678             }
679             break;
680             case 'H' :
681             {
682                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::DARKEN;
683                 nParametersNeeded = 0;
684                 nIndex++;
685             }
686             break;
687             case 'I' :
688             {
689                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::DARKENLESS;
690                 nParametersNeeded = 0;
691                 nIndex++;
692             }
693             break;
694             case 'J' :
695             {
696                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::LIGHTEN;
697                 nParametersNeeded = 0;
698                 nIndex++;
699             }
700             break;
701             case 'K' :
702             {
703                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::LIGHTENLESS;
704                 nParametersNeeded = 0;
705                 nIndex++;
706             }
707             break;
708             case 'W' :
709             {
710                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO;
711                 nParametersNeeded = 4;
712                 nIndex++;
713             }
714             break;
715             case 'V' :
716             {
717                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC;
718                 nParametersNeeded = 4;
719                 nIndex++;
720             }
721             break;
722             case 'X' :
723             {
724                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX;
725                 nParametersNeeded = 1;
726                 nIndex++;
727             }
728             break;
729             case 'Y' :
730             {
731                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY;
732                 nParametersNeeded = 1;
733                 nIndex++;
734             }
735             break;
736             case 'Q' :
737             {
738                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::QUADRATICCURVETO;
739                 nParametersNeeded = 2;
740                 nIndex++;
741             }
742             break;
743             case ' ' :
744             {
745                 nIndex++;
746             }
747             break;
748 
749             case '$' :
750             case '?' :
751             case '0' :
752             case '1' :
753             case '2' :
754             case '3' :
755             case '4' :
756             case '5' :
757             case '6' :
758             case '7' :
759             case '8' :
760             case '9' :
761             case '.' :
762             case '-' :
763             {
764                 css::drawing::EnhancedCustomShapeParameterPair aPair;
765                 if ( GetNextParameter( aPair.First, nIndex, rValue ) &&
766                         GetNextParameter( aPair.Second, nIndex, rValue ) )
767                 {
768                     vCoordinates.push_back( aPair );
769                     nParameterCount++;
770                 }
771                 else
772                     bValid = false;
773             }
774             break;
775             default:
776                 nIndex++;
777             break;
778         }
779         if ( !nParameterCount && !nParametersNeeded )
780         {
781             css::drawing::EnhancedCustomShapeSegment aSegment;
782             aSegment.Command = nLatestSegmentCommand;
783             aSegment.Count = 0;
784             vSegments.push_back( aSegment );
785             nParametersNeeded = 0x7fffffff;
786         }
787         else if ( nParameterCount >= nParametersNeeded )
788         {
789             // Special rule for moveto in ODF 1.2 section 19.145
790             // "If a moveto is followed by multiple pairs of coordinates, they are treated as lineto."
791             if ( nLatestSegmentCommand == css::drawing::EnhancedCustomShapeSegmentCommand::MOVETO )
792             {
793                 css::drawing::EnhancedCustomShapeSegment aSegment;
794                 aSegment.Command = css::drawing::EnhancedCustomShapeSegmentCommand::MOVETO;
795                 aSegment.Count = 1;
796                 vSegments.push_back( aSegment );
797                 nIndex--;
798                 nLatestSegmentCommand = css::drawing::EnhancedCustomShapeSegmentCommand::LINETO;
799                 nParametersNeeded = 1;
800             }
801             else
802             {
803                 // General rule in ODF 1.2. section 19.145
804                 // "If a command is repeated multiple times, all repeated command characters
805                 // except the first one may be omitted." Thus check if the last command is identical,
806                 // if so, we just need to increment the count
807                 if ( !vSegments.empty() && ( vSegments[ vSegments.size() - 1 ].Command == nLatestSegmentCommand ) )
808                     vSegments[ vSegments.size() -1 ].Count++;
809                 else
810                 {
811                     css::drawing::EnhancedCustomShapeSegment aSegment;
812                     aSegment.Command = nLatestSegmentCommand;
813                     aSegment.Count = 1;
814                     vSegments.push_back( aSegment );
815                 }
816             }
817             nParameterCount = 0;
818         }
819     }
820 
821     // adding the Coordinates property
822     beans::PropertyValue aProp;
823     aProp.Name = EASGet( EAS_Coordinates );
824     aProp.Value <<= comphelper::containerToSequence(vCoordinates);
825     rDest.push_back( aProp );
826 
827     // adding the Segments property
828     aProp.Name = EASGet( EAS_Segments );
829     aProp.Value <<= comphelper::containerToSequence(vSegments);
830     rDest.push_back( aProp );
831 }
832 
GetAdjustmentValues(std::vector<css::beans::PropertyValue> & rDest,const OUString & rValue)833 static void GetAdjustmentValues( std::vector< css::beans::PropertyValue >& rDest,               // draw:adjustments
834                         const OUString& rValue )
835 {
836     std::vector< css::drawing::EnhancedCustomShapeAdjustmentValue > vAdjustmentValue;
837     css::drawing::EnhancedCustomShapeParameter aParameter;
838     sal_Int32 nIndex = 0;
839     while ( GetNextParameter( aParameter, nIndex, rValue ) )
840     {
841         css::drawing::EnhancedCustomShapeAdjustmentValue aAdj;
842         if ( aParameter.Type == css::drawing::EnhancedCustomShapeParameterType::NORMAL )
843         {
844             aAdj.Value = aParameter.Value;
845             aAdj.State = beans::PropertyState_DIRECT_VALUE;
846         }
847         else
848             aAdj.State = beans::PropertyState_DEFAULT_VALUE;    // this should not be, but better than setting nothing
849 
850         vAdjustmentValue.push_back( aAdj );
851     }
852 
853     sal_Int32 nAdjustmentValues = vAdjustmentValue.size();
854     if ( nAdjustmentValues )
855     {
856         beans::PropertyValue aProp;
857         aProp.Name = EASGet( EAS_AdjustmentValues );
858         aProp.Value <<= comphelper::containerToSequence(vAdjustmentValue);
859         rDest.push_back( aProp );
860     }
861 }
862 
startFastElement(sal_Int32,const css::uno::Reference<css::xml::sax::XFastAttributeList> & xAttrList)863 void XMLEnhancedCustomShapeContext::startFastElement(
864     sal_Int32 /*nElement*/,
865     const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
866 {
867     sal_Int32               nAttrNumber;
868     for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
869     {
870         switch( EASGet( aIter.getToken() ) )
871         {
872             case EAS_type :
873                 GetString( mrCustomShapeGeometry, aIter.toString(), EAS_Type );
874             break;
875             case EAS_mirror_horizontal :
876                 GetBool( mrCustomShapeGeometry, aIter.toView(), EAS_MirroredX );
877             break;
878             case EAS_mirror_vertical :
879                 GetBool( mrCustomShapeGeometry, aIter.toView(), EAS_MirroredY );
880             break;
881             case EAS_viewBox :
882             {
883                 SdXMLImExViewBox aViewBox( aIter.toString(), GetImport().GetMM100UnitConverter() );
884                 awt::Rectangle aRect( aViewBox.GetX(), aViewBox.GetY(), aViewBox.GetWidth(), aViewBox.GetHeight() );
885                 beans::PropertyValue aProp;
886                 aProp.Name = EASGet( EAS_ViewBox );
887                 aProp.Value <<= aRect;
888                 mrCustomShapeGeometry.push_back( aProp );
889             }
890             break;
891             case EAS_sub_view_size:
892                 GetSizeSequence( maPath, aIter.toString(), EAS_SubViewSize );
893             break;
894             case EAS_text_rotate_angle :
895                 GetDouble( mrCustomShapeGeometry, aIter.toView(), EAS_TextRotateAngle );
896             break;
897             case EAS_extrusion_allowed :
898                 GetBool( maPath, aIter.toView(), EAS_ExtrusionAllowed );
899             break;
900             case EAS_text_path_allowed :
901                 GetBool( maPath, aIter.toView(), EAS_TextPathAllowed );
902             break;
903             case EAS_concentric_gradient_fill_allowed :
904                 GetBool( maPath, aIter.toView(), EAS_ConcentricGradientFillAllowed );
905             break;
906             case EAS_extrusion :
907                 GetBool( maExtrusion, aIter.toView(), EAS_Extrusion );
908             break;
909             case EAS_extrusion_brightness :
910                 GetDoublePercentage( maExtrusion, aIter.toView(), EAS_Brightness );
911             break;
912             case EAS_extrusion_depth :
913             {
914                 OUString rValue = aIter.toString();
915                 sal_Int32 nIndex = 0;
916                 css::drawing::EnhancedCustomShapeParameterPair aParameterPair;
917                 css::drawing::EnhancedCustomShapeParameter& rDepth = aParameterPair.First;
918                 if ( GetNextParameter( rDepth, nIndex, rValue ) )
919                 {
920                     css::drawing::EnhancedCustomShapeParameter& rFraction = aParameterPair.Second;
921                     // try to catch the unit for the depth
922                     sal_Int16 const eSrcUnit(
923                         ::sax::Converter::GetUnitFromString(
924                             rValue, util::MeasureUnit::MM_100TH));
925 
926                     OUStringBuffer aUnitStr;
927                     double fFactor = ::sax::Converter::GetConversionFactor(
928                         aUnitStr, util::MeasureUnit::MM_100TH, eSrcUnit);
929                     if ( ( fFactor != 1.0 ) && ( fFactor != 0.0 ) )
930                     {
931                         double fDepth(0.0);
932                         if ( rDepth.Value >>= fDepth )
933                         {
934                             fDepth /= fFactor;
935                             rDepth.Value <<= fDepth;
936                         }
937                     }
938                     if ( rValue.matchIgnoreAsciiCase( aUnitStr.toString(), nIndex ) )
939                         nIndex += aUnitStr.getLength();
940 
941                     // skipping white spaces
942                     while( ( nIndex < rValue.getLength() ) && rValue[ nIndex ] == ' ' )
943                         nIndex++;
944 
945                     if ( GetNextParameter( rFraction, nIndex, rValue ) )
946                     {
947                         beans::PropertyValue aProp;
948                         aProp.Name = EASGet( EAS_Depth );
949                         aProp.Value <<= aParameterPair;
950                         maExtrusion.push_back( aProp );
951                     }
952                 }
953             }
954             break;
955             case EAS_extrusion_diffusion :
956                 GetDoublePercentage( maExtrusion, aIter.toView(), EAS_Diffusion );
957             break;
958             case EAS_extrusion_number_of_line_segments :
959                 GetInt32( maExtrusion, aIter.toView(), EAS_NumberOfLineSegments );
960             break;
961             case EAS_extrusion_light_face :
962                 GetBool( maExtrusion, aIter.toView(), EAS_LightFace );
963             break;
964             case EAS_extrusion_first_light_harsh :
965                 GetBool( maExtrusion, aIter.toView(), EAS_FirstLightHarsh );
966             break;
967             case EAS_extrusion_second_light_harsh :
968                 GetBool( maExtrusion, aIter.toView(), EAS_SecondLightHarsh );
969             break;
970             case EAS_extrusion_first_light_level :
971                 GetDoublePercentage( maExtrusion, aIter.toView(), EAS_FirstLightLevel );
972             break;
973             case EAS_extrusion_second_light_level :
974                 GetDoublePercentage( maExtrusion, aIter.toView(), EAS_SecondLightLevel );
975             break;
976             case EAS_extrusion_first_light_direction :
977                 GetB3DVector( maExtrusion, aIter.toView(), EAS_FirstLightDirection );
978             break;
979             case EAS_extrusion_second_light_direction :
980                 GetB3DVector( maExtrusion, aIter.toView(), EAS_SecondLightDirection );
981             break;
982             case EAS_extrusion_metal :
983                 GetBool( maExtrusion, aIter.toView(), EAS_Metal );
984             break;
985             case EAS_shade_mode :
986             {
987                 drawing::ShadeMode eShadeMode( drawing::ShadeMode_FLAT );
988                 if( IsXMLToken( aIter, XML_PHONG ) )
989                     eShadeMode = drawing::ShadeMode_PHONG;
990                 else if ( IsXMLToken( aIter, XML_GOURAUD ) )
991                     eShadeMode = drawing::ShadeMode_SMOOTH;
992                 else if ( IsXMLToken( aIter, XML_DRAFT ) )
993                     eShadeMode = drawing::ShadeMode_DRAFT;
994 
995                 beans::PropertyValue aProp;
996                 aProp.Name = EASGet( EAS_ShadeMode );
997                 aProp.Value <<= eShadeMode;
998                 maExtrusion.push_back( aProp );
999             }
1000             break;
1001             case EAS_extrusion_rotation_angle :
1002                 GetEnhancedParameterPair( maExtrusion, aIter.toString(), EAS_RotateAngle );
1003             break;
1004             case EAS_extrusion_rotation_center :
1005                 GetB3DVector( maExtrusion, aIter.toView(), EAS_RotationCenter );
1006             break;
1007             case EAS_extrusion_shininess :
1008                 GetDoublePercentage( maExtrusion, aIter.toView(), EAS_Shininess );
1009             break;
1010             case EAS_extrusion_skew :
1011                 GetEnhancedParameterPair( maExtrusion, aIter.toString(), EAS_Skew );
1012             break;
1013             case EAS_extrusion_specularity :
1014                 GetDoublePercentage( maExtrusion, aIter.toView(), EAS_Specularity );
1015             break;
1016             case EAS_projection :
1017             {
1018                 drawing::ProjectionMode eProjectionMode( drawing::ProjectionMode_PERSPECTIVE );
1019                 if( IsXMLToken( aIter, XML_PARALLEL ) )
1020                     eProjectionMode = drawing::ProjectionMode_PARALLEL;
1021 
1022                 beans::PropertyValue aProp;
1023                 aProp.Name = EASGet( EAS_ProjectionMode );
1024                 aProp.Value <<= eProjectionMode;
1025                 maExtrusion.push_back( aProp );
1026             }
1027             break;
1028             case EAS_extrusion_viewpoint :
1029                 GetPosition3D( maExtrusion, aIter.toView(), EAS_ViewPoint, mrUnitConverter );
1030             break;
1031             case EAS_extrusion_origin :
1032                 GetEnhancedParameterPair( maExtrusion, aIter.toString(), EAS_Origin );
1033             break;
1034             case EAS_extrusion_color :
1035                 GetBool( maExtrusion, aIter.toView(), EAS_Color );
1036             break;
1037             case EAS_enhanced_path :
1038                 GetEnhancedPath( maPath, aIter.toString() );
1039             break;
1040             case EAS_path_stretchpoint_x :
1041             {
1042                 if (::sax::Converter::convertNumber(nAttrNumber, aIter.toView()))
1043                 {
1044                     beans::PropertyValue aProp;
1045                     aProp.Name = EASGet( EAS_StretchX );
1046                     aProp.Value <<= nAttrNumber;
1047                     maPath.push_back( aProp );
1048                 }
1049             }
1050             break;
1051             case EAS_path_stretchpoint_y :
1052             {
1053                 if (::sax::Converter::convertNumber(nAttrNumber, aIter.toView()))
1054                 {
1055                     beans::PropertyValue aProp;
1056                     aProp.Name = EASGet( EAS_StretchY );
1057                     aProp.Value <<= nAttrNumber;
1058                     maPath.push_back( aProp );
1059                 }
1060             }
1061             break;
1062             case EAS_text_areas :
1063                 GetEnhancedRectangleSequence( maPath, aIter.toString(), EAS_TextFrames );
1064             break;
1065             case EAS_glue_points :
1066             {
1067                 sal_Int32 i, nPairs = GetEnhancedParameterPairSequence( maPath, aIter.toString(), EAS_GluePoints );
1068                 GetImport().GetShapeImport()->moveGluePointMapping( mrxShape, nPairs );
1069                 for ( i = 0; i < nPairs; i++ )
1070                     GetImport().GetShapeImport()->addGluePointMapping( mrxShape, i + 4, i + 4 );
1071             }
1072             break;
1073             case EAS_glue_point_type :
1074                 GetEnum( maPath, aIter.toView(), EAS_GluePointType, *aXML_GluePointEnumMap );
1075             break;
1076             case EAS_glue_point_leaving_directions :
1077                 GetDoubleSequence( maPath, aIter.toString(), EAS_GluePointLeavingDirections );
1078             break;
1079             case EAS_text_path :
1080                 GetBool( maTextPath, aIter.toView(), EAS_TextPath );
1081             break;
1082             case EAS_text_path_mode :
1083             {
1084                 css::drawing::EnhancedCustomShapeTextPathMode eTextPathMode( css::drawing::EnhancedCustomShapeTextPathMode_NORMAL );
1085                 if( IsXMLToken( aIter, XML_PATH ) )
1086                     eTextPathMode = css::drawing::EnhancedCustomShapeTextPathMode_PATH;
1087                 else if ( IsXMLToken( aIter, XML_SHAPE ) )
1088                     eTextPathMode = css::drawing::EnhancedCustomShapeTextPathMode_SHAPE;
1089 
1090                 beans::PropertyValue aProp;
1091                 aProp.Name = EASGet( EAS_TextPathMode );
1092                 aProp.Value <<= eTextPathMode;
1093                 maTextPath.push_back( aProp );
1094             }
1095             break;
1096             case EAS_text_path_scale :
1097             {
1098                 bool bScaleX = IsXMLToken( aIter, XML_SHAPE );
1099                 beans::PropertyValue aProp;
1100                 aProp.Name = EASGet( EAS_ScaleX );
1101                 aProp.Value <<= bScaleX;
1102                 maTextPath.push_back( aProp );
1103             }
1104             break;
1105             case EAS_text_path_same_letter_heights :
1106                 GetBool( maTextPath, aIter.toView(), EAS_SameLetterHeights );
1107             break;
1108             case EAS_modifiers :
1109                 GetAdjustmentValues( mrCustomShapeGeometry, aIter.toString() );
1110             break;
1111             default:
1112                 break;
1113         }
1114     }
1115 }
1116 
SdXMLCustomShapePropertyMerge(std::vector<css::beans::PropertyValue> & rPropVec,const std::vector<beans::PropertyValues> & rElement,const OUString & rElementName)1117 static void SdXMLCustomShapePropertyMerge( std::vector< css::beans::PropertyValue >& rPropVec,
1118                                     const std::vector< beans::PropertyValues >& rElement,
1119                                         const OUString& rElementName )
1120 {
1121     if ( !rElement.empty() )
1122     {
1123         beans::PropertyValue aProp;
1124         aProp.Name = rElementName;
1125         aProp.Value <<= comphelper::containerToSequence(rElement);
1126         rPropVec.push_back( aProp );
1127     }
1128 }
1129 
SdXMLCustomShapePropertyMerge(std::vector<css::beans::PropertyValue> & rPropVec,const std::vector<OUString> & rElement,const OUString & rElementName)1130 static void SdXMLCustomShapePropertyMerge( std::vector< css::beans::PropertyValue >& rPropVec,
1131                                     const std::vector< OUString >& rElement,
1132                                         const OUString& rElementName )
1133 {
1134     if ( !rElement.empty() )
1135     {
1136         beans::PropertyValue aProp;
1137         aProp.Name = rElementName;
1138         aProp.Value <<= comphelper::containerToSequence(rElement);
1139         rPropVec.push_back( aProp );
1140     }
1141 }
1142 
SdXMLCustomShapePropertyMerge(std::vector<css::beans::PropertyValue> & rPropVec,const std::vector<css::beans::PropertyValue> & rElement,const OUString & rElementName)1143 static void SdXMLCustomShapePropertyMerge( std::vector< css::beans::PropertyValue >& rPropVec,
1144                                     const std::vector< css::beans::PropertyValue >& rElement,
1145                                         const OUString& rElementName )
1146 {
1147     if ( !rElement.empty() )
1148     {
1149         beans::PropertyValue aProp;
1150         aProp.Name = rElementName;
1151         aProp.Value <<= comphelper::containerToSequence(rElement);
1152         rPropVec.push_back( aProp );
1153     }
1154 }
1155 
1156 typedef std::unordered_map< OUString, sal_Int32 > EquationHashMap;
1157 
1158 /* if rPara.Type is from type EnhancedCustomShapeParameterType::EQUATION, the name of the equation
1159    will be converted from OUString to index */
CheckAndResolveEquationParameter(css::drawing::EnhancedCustomShapeParameter & rPara,EquationHashMap * pH)1160 static void CheckAndResolveEquationParameter( css::drawing::EnhancedCustomShapeParameter& rPara, EquationHashMap* pH )
1161 {
1162     if ( rPara.Type == css::drawing::EnhancedCustomShapeParameterType::EQUATION )
1163     {
1164         OUString aEquationName;
1165         if ( rPara.Value >>= aEquationName )
1166         {
1167             sal_Int32 nIndex = 0;
1168             EquationHashMap::iterator aHashIter( pH->find( aEquationName ) );
1169             if ( aHashIter != pH->end() )
1170                 nIndex = (*aHashIter).second;
1171             rPara.Value <<= nIndex;
1172         }
1173     }
1174 }
1175 
endFastElement(sal_Int32)1176 void XMLEnhancedCustomShapeContext::endFastElement(sal_Int32 )
1177 {
1178     // resolve properties that are indexing an Equation
1179     if ( !maEquations.empty() )
1180     {
1181         // creating hash map containing the name and index of each equation
1182         std::unique_ptr<EquationHashMap> pH = std::make_unique<EquationHashMap>();
1183         std::vector< OUString >::iterator aEquationNameIter = maEquationNames.begin();
1184         std::vector< OUString >::iterator aEquationNameEnd  = maEquationNames.end();
1185         while( aEquationNameIter != aEquationNameEnd )
1186         {
1187             (*pH)[ *aEquationNameIter ] = static_cast<sal_Int32>( aEquationNameIter - maEquationNames.begin() );
1188             ++aEquationNameIter;
1189         }
1190 
1191         // resolve equation
1192         for( auto& rEquation : maEquations )
1193         {
1194             sal_Int32 nIndexOf = 0;
1195             do
1196             {
1197                 nIndexOf = rEquation.indexOf( '?', nIndexOf );
1198                 if ( nIndexOf != -1 )
1199                 {
1200                     OUString aEquationName;
1201                     if ( GetEquationName( rEquation, nIndexOf + 1, aEquationName ) )
1202                     {
1203                         // copying first characters inclusive '?'
1204                         sal_Int32 nIndex = 0;
1205                         EquationHashMap::iterator aHashIter( pH->find( aEquationName ) );
1206                         if ( aHashIter != pH->end() )
1207                             nIndex = (*aHashIter).second;
1208                         OUString aNew = rEquation.subView( 0, nIndexOf + 1 ) +
1209                             OUString::number( nIndex ) +
1210                             rEquation.subView( nIndexOf + aEquationName.getLength() + 1 );
1211                         rEquation = aNew;
1212                     }
1213                     nIndexOf++;
1214                 }
1215             }
1216             while( nIndexOf != -1 );
1217         }
1218 
1219         // Path
1220         for ( const beans::PropertyValue& rPathItem : maPath )
1221         {
1222             switch( EASGet( rPathItem.Name ) )
1223             {
1224                 case EAS_Coordinates :
1225                 case EAS_GluePoints :
1226                 {
1227                     uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > const & rSeq =
1228                         *o3tl::doAccess<uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > >(
1229                             rPathItem.Value);
1230                     for ( const auto& rElem : rSeq )
1231                     {
1232                         CheckAndResolveEquationParameter( const_cast<css::drawing::EnhancedCustomShapeParameter &>(rElem.First), pH.get() );
1233                         CheckAndResolveEquationParameter( const_cast<css::drawing::EnhancedCustomShapeParameter &>(rElem.Second), pH.get() );
1234                     }
1235                 }
1236                 break;
1237                 case EAS_TextFrames :
1238                 {
1239                     uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > const & rSeq =
1240                         *o3tl::doAccess<uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > >(
1241                             rPathItem.Value);
1242                     for ( const auto& rElem : rSeq )
1243                     {
1244                         CheckAndResolveEquationParameter( const_cast<css::drawing::EnhancedCustomShapeParameter &>(rElem.TopLeft.First), pH.get() );
1245                         CheckAndResolveEquationParameter( const_cast<css::drawing::EnhancedCustomShapeParameter &>(rElem.TopLeft.Second), pH.get() );
1246                         CheckAndResolveEquationParameter( const_cast<css::drawing::EnhancedCustomShapeParameter &>(rElem.BottomRight.First), pH.get() );
1247                         CheckAndResolveEquationParameter( const_cast<css::drawing::EnhancedCustomShapeParameter &>(rElem.BottomRight.Second), pH.get() );
1248                     }
1249                 }
1250                 break;
1251                 default:
1252                     break;
1253             }
1254         }
1255         for ( css::beans::PropertyValues const & aHandle : maHandles )
1256         {
1257             for ( beans::PropertyValue const & propValue : aHandle )
1258             {
1259                 switch( EASGet( propValue.Name ) )
1260                 {
1261                     case EAS_RangeYMinimum :
1262                     case EAS_RangeYMaximum :
1263                     case EAS_RangeXMinimum :
1264                     case EAS_RangeXMaximum :
1265                     case EAS_RadiusRangeMinimum :
1266                     case EAS_RadiusRangeMaximum :
1267                     {
1268                         CheckAndResolveEquationParameter( const_cast<css::drawing::EnhancedCustomShapeParameter &>(*o3tl::doAccess<css::drawing::EnhancedCustomShapeParameter>(
1269                             propValue.Value)), pH.get() );
1270                     }
1271                     break;
1272 
1273                     case EAS_Position :
1274                     case EAS_Polar :
1275                     {
1276                         CheckAndResolveEquationParameter( const_cast<css::drawing::EnhancedCustomShapeParameter &>((*o3tl::doAccess<css::drawing::EnhancedCustomShapeParameterPair>(
1277                             propValue.Value)).First), pH.get() );
1278                         CheckAndResolveEquationParameter( const_cast<css::drawing::EnhancedCustomShapeParameter &>((*o3tl::doAccess<css::drawing::EnhancedCustomShapeParameterPair>(
1279                             propValue.Value)).Second), pH.get() );
1280                     }
1281                     break;
1282                     default:
1283                         break;
1284                 }
1285             }
1286         }
1287     }
1288 
1289     SdXMLCustomShapePropertyMerge( mrCustomShapeGeometry, maExtrusion, EASGet( EAS_Extrusion ) );
1290     SdXMLCustomShapePropertyMerge( mrCustomShapeGeometry, maPath,      EASGet( EAS_Path ) );
1291     SdXMLCustomShapePropertyMerge( mrCustomShapeGeometry, maTextPath,  EASGet( EAS_TextPath ) );
1292     SdXMLCustomShapePropertyMerge( mrCustomShapeGeometry, maEquations, EASGet( EAS_Equations ) );
1293     if  ( !maHandles.empty() )
1294         SdXMLCustomShapePropertyMerge( mrCustomShapeGeometry, maHandles, EASGet( EAS_Handles ) );
1295 }
1296 
createFastChildContext(sal_Int32 nElement,const css::uno::Reference<css::xml::sax::XFastAttributeList> & xAttrList)1297 css::uno::Reference< css::xml::sax::XFastContextHandler > XMLEnhancedCustomShapeContext::createFastChildContext(
1298     sal_Int32 nElement,
1299     const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
1300 {
1301     EnhancedCustomShapeTokenEnum aTokenEnum = EASGet( nElement );
1302     if ( aTokenEnum == EAS_equation )
1303     {
1304         OUString aFormula;
1305         OUString aFormulaName;
1306         for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
1307         {
1308             OUString sValue = aIter.toString();
1309             switch( EASGet( aIter.getToken() ) )
1310             {
1311                 case EAS_formula :
1312                     aFormula = sValue;
1313                 break;
1314                 case EAS_name :
1315                     aFormulaName = sValue;
1316                 break;
1317                 default:
1318                     break;
1319             }
1320         }
1321         if ( !aFormulaName.isEmpty() || !aFormula.isEmpty() )
1322         {
1323             maEquations.push_back( aFormula );
1324             maEquationNames.push_back( aFormulaName );
1325         }
1326     }
1327     else if ( aTokenEnum == EAS_handle )
1328     {
1329         std::vector< css::beans::PropertyValue > aHandle;
1330         for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
1331         {
1332             switch( EASGet( aIter.getToken() ) )
1333             {
1334                 case EAS_handle_mirror_vertical :
1335                     GetBool( aHandle, aIter.toView(), EAS_MirroredY );
1336                 break;
1337                 case EAS_handle_mirror_horizontal :
1338                     GetBool( aHandle, aIter.toView(), EAS_MirroredX );
1339                 break;
1340                 case EAS_handle_switched :
1341                     GetBool( aHandle, aIter.toView(), EAS_Switched );
1342                 break;
1343                 case EAS_handle_position :
1344                     GetEnhancedParameterPair( aHandle, aIter.toString(), EAS_Position );
1345                 break;
1346                 case EAS_handle_range_x_minimum :
1347                     GetEnhancedParameter( aHandle, aIter.toString(), EAS_RangeXMinimum );
1348                 break;
1349                 case EAS_handle_range_x_maximum :
1350                     GetEnhancedParameter( aHandle, aIter.toString(), EAS_RangeXMaximum );
1351                 break;
1352                 case EAS_handle_range_y_minimum :
1353                     GetEnhancedParameter( aHandle, aIter.toString(), EAS_RangeYMinimum );
1354                 break;
1355                 case EAS_handle_range_y_maximum :
1356                     GetEnhancedParameter( aHandle, aIter.toString(), EAS_RangeYMaximum );
1357                 break;
1358                 case EAS_handle_polar :
1359                     GetEnhancedParameterPair( aHandle, aIter.toString(), EAS_Polar );
1360                 break;
1361                 case EAS_handle_radius_range_minimum :
1362                     GetEnhancedParameter( aHandle, aIter.toString(), EAS_RadiusRangeMinimum );
1363                 break;
1364                 case EAS_handle_radius_range_maximum :
1365                     GetEnhancedParameter( aHandle, aIter.toString(), EAS_RadiusRangeMaximum );
1366                 break;
1367                 default:
1368                     break;
1369             }
1370         }
1371         maHandles.push_back( comphelper::containerToSequence(aHandle) );
1372     }
1373     return nullptr;
1374 }
1375 
1376 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1377