xref: /AOO41X/main/basegfx/source/polygon/b2dsvgpolypolygon.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1*cdf0e10cSrcweir /*************************************************************************
2*cdf0e10cSrcweir  *
3*cdf0e10cSrcweir  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4*cdf0e10cSrcweir  *
5*cdf0e10cSrcweir  * Copyright 2000, 2010 Oracle and/or its affiliates.
6*cdf0e10cSrcweir  *
7*cdf0e10cSrcweir  * OpenOffice.org - a multi-platform office productivity suite
8*cdf0e10cSrcweir  *
9*cdf0e10cSrcweir  * This file is part of OpenOffice.org.
10*cdf0e10cSrcweir  *
11*cdf0e10cSrcweir  * OpenOffice.org is free software: you can redistribute it and/or modify
12*cdf0e10cSrcweir  * it under the terms of the GNU Lesser General Public License version 3
13*cdf0e10cSrcweir  * only, as published by the Free Software Foundation.
14*cdf0e10cSrcweir  *
15*cdf0e10cSrcweir  * OpenOffice.org is distributed in the hope that it will be useful,
16*cdf0e10cSrcweir  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17*cdf0e10cSrcweir  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18*cdf0e10cSrcweir  * GNU Lesser General Public License version 3 for more details
19*cdf0e10cSrcweir  * (a copy is included in the LICENSE file that accompanied this code).
20*cdf0e10cSrcweir  *
21*cdf0e10cSrcweir  * You should have received a copy of the GNU Lesser General Public License
22*cdf0e10cSrcweir  * version 3 along with OpenOffice.org.  If not, see
23*cdf0e10cSrcweir  * <http://www.openoffice.org/license.html>
24*cdf0e10cSrcweir  * for a copy of the LGPLv3 License.
25*cdf0e10cSrcweir  *
26*cdf0e10cSrcweir  ************************************************************************/
27*cdf0e10cSrcweir 
28*cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
29*cdf0e10cSrcweir #include "precompiled_basegfx.hxx"
30*cdf0e10cSrcweir 
31*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx>
32*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygontools.hxx>
33*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx>
34*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygon.hxx>
35*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrix.hxx>
36*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrixtools.hxx>
37*cdf0e10cSrcweir #include <rtl/ustring.hxx>
38*cdf0e10cSrcweir #include <rtl/math.hxx>
39*cdf0e10cSrcweir 
40*cdf0e10cSrcweir namespace basegfx
41*cdf0e10cSrcweir {
42*cdf0e10cSrcweir 	namespace tools
43*cdf0e10cSrcweir 	{
44*cdf0e10cSrcweir         namespace
45*cdf0e10cSrcweir         {
46*cdf0e10cSrcweir             void lcl_skipSpaces(sal_Int32& 				io_rPos,
47*cdf0e10cSrcweir                                 const ::rtl::OUString& 	rStr,
48*cdf0e10cSrcweir                                 const sal_Int32 		nLen)
49*cdf0e10cSrcweir             {
50*cdf0e10cSrcweir                 while( io_rPos < nLen &&
51*cdf0e10cSrcweir                        sal_Unicode(' ') == rStr[io_rPos] )
52*cdf0e10cSrcweir                 {
53*cdf0e10cSrcweir                     ++io_rPos;
54*cdf0e10cSrcweir                 }
55*cdf0e10cSrcweir             }
56*cdf0e10cSrcweir 
57*cdf0e10cSrcweir             void lcl_skipSpacesAndCommas(sal_Int32& 			io_rPos,
58*cdf0e10cSrcweir                                          const ::rtl::OUString& rStr,
59*cdf0e10cSrcweir                                          const sal_Int32 		nLen)
60*cdf0e10cSrcweir             {
61*cdf0e10cSrcweir                 while(io_rPos < nLen
62*cdf0e10cSrcweir                       && (sal_Unicode(' ') == rStr[io_rPos] || sal_Unicode(',') == rStr[io_rPos]))
63*cdf0e10cSrcweir                 {
64*cdf0e10cSrcweir                     ++io_rPos;
65*cdf0e10cSrcweir                 }
66*cdf0e10cSrcweir             }
67*cdf0e10cSrcweir 
68*cdf0e10cSrcweir             inline bool lcl_isOnNumberChar(const sal_Unicode aChar, bool bSignAllowed = true)
69*cdf0e10cSrcweir             {
70*cdf0e10cSrcweir                 const bool bPredicate( (sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
71*cdf0e10cSrcweir                                        || (bSignAllowed && sal_Unicode('+') == aChar)
72*cdf0e10cSrcweir                                        || (bSignAllowed && sal_Unicode('-') == aChar) );
73*cdf0e10cSrcweir 
74*cdf0e10cSrcweir                 return bPredicate;
75*cdf0e10cSrcweir             }
76*cdf0e10cSrcweir 
77*cdf0e10cSrcweir             inline bool lcl_isOnNumberChar(const ::rtl::OUString& rStr, const sal_Int32 nPos, bool bSignAllowed = true)
78*cdf0e10cSrcweir             {
79*cdf0e10cSrcweir                 return lcl_isOnNumberChar(rStr[nPos],
80*cdf0e10cSrcweir                                           bSignAllowed);
81*cdf0e10cSrcweir             }
82*cdf0e10cSrcweir 
83*cdf0e10cSrcweir             bool lcl_getDoubleChar(double& 					o_fRetval,
84*cdf0e10cSrcweir                                    sal_Int32& 				io_rPos,
85*cdf0e10cSrcweir                                    const ::rtl::OUString& 	rStr,
86*cdf0e10cSrcweir                                    const sal_Int32 			/*nLen*/)
87*cdf0e10cSrcweir             {
88*cdf0e10cSrcweir                 sal_Unicode aChar( rStr[io_rPos] );
89*cdf0e10cSrcweir                 ::rtl::OUStringBuffer sNumberString;
90*cdf0e10cSrcweir 
91*cdf0e10cSrcweir                 if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar)
92*cdf0e10cSrcweir                 {
93*cdf0e10cSrcweir                     sNumberString.append(rStr[io_rPos]);
94*cdf0e10cSrcweir                     aChar = rStr[++io_rPos];
95*cdf0e10cSrcweir                 }
96*cdf0e10cSrcweir 
97*cdf0e10cSrcweir                 while((sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
98*cdf0e10cSrcweir                       || sal_Unicode('.') == aChar)
99*cdf0e10cSrcweir                 {
100*cdf0e10cSrcweir                     sNumberString.append(rStr[io_rPos]);
101*cdf0e10cSrcweir                     aChar = rStr[++io_rPos];
102*cdf0e10cSrcweir                 }
103*cdf0e10cSrcweir 
104*cdf0e10cSrcweir                 if(sal_Unicode('e') == aChar || sal_Unicode('E') == aChar)
105*cdf0e10cSrcweir                 {
106*cdf0e10cSrcweir                     sNumberString.append(rStr[io_rPos]);
107*cdf0e10cSrcweir                     aChar = rStr[++io_rPos];
108*cdf0e10cSrcweir 
109*cdf0e10cSrcweir                     if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar)
110*cdf0e10cSrcweir                     {
111*cdf0e10cSrcweir                         sNumberString.append(rStr[io_rPos]);
112*cdf0e10cSrcweir                         aChar = rStr[++io_rPos];
113*cdf0e10cSrcweir                     }
114*cdf0e10cSrcweir 
115*cdf0e10cSrcweir                     while(sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
116*cdf0e10cSrcweir                     {
117*cdf0e10cSrcweir                         sNumberString.append(rStr[io_rPos]);
118*cdf0e10cSrcweir                         aChar = rStr[++io_rPos];
119*cdf0e10cSrcweir                     }
120*cdf0e10cSrcweir                 }
121*cdf0e10cSrcweir 
122*cdf0e10cSrcweir                 if(sNumberString.getLength())
123*cdf0e10cSrcweir                 {
124*cdf0e10cSrcweir                     rtl_math_ConversionStatus eStatus;
125*cdf0e10cSrcweir                     o_fRetval = ::rtl::math::stringToDouble( sNumberString.makeStringAndClear(),
126*cdf0e10cSrcweir                                                              (sal_Unicode)('.'),
127*cdf0e10cSrcweir                                                              (sal_Unicode)(','),
128*cdf0e10cSrcweir                                                              &eStatus,
129*cdf0e10cSrcweir                                                              NULL );
130*cdf0e10cSrcweir                     return ( eStatus == rtl_math_ConversionStatus_Ok );
131*cdf0e10cSrcweir                 }
132*cdf0e10cSrcweir 
133*cdf0e10cSrcweir                 return false;
134*cdf0e10cSrcweir             }
135*cdf0e10cSrcweir 
136*cdf0e10cSrcweir             bool lcl_importDoubleAndSpaces( double& 				o_fRetval,
137*cdf0e10cSrcweir                                             sal_Int32& 				io_rPos,
138*cdf0e10cSrcweir                                             const ::rtl::OUString& 	rStr,
139*cdf0e10cSrcweir                                             const sal_Int32 		nLen )
140*cdf0e10cSrcweir             {
141*cdf0e10cSrcweir                 if( !lcl_getDoubleChar(o_fRetval, io_rPos, rStr, nLen) )
142*cdf0e10cSrcweir                     return false;
143*cdf0e10cSrcweir 
144*cdf0e10cSrcweir                 lcl_skipSpacesAndCommas(io_rPos, rStr, nLen);
145*cdf0e10cSrcweir 
146*cdf0e10cSrcweir                 return true;
147*cdf0e10cSrcweir             }
148*cdf0e10cSrcweir 
149*cdf0e10cSrcweir             bool lcl_importNumberAndSpaces(sal_Int32&                o_nRetval,
150*cdf0e10cSrcweir                                            sal_Int32& 				io_rPos,
151*cdf0e10cSrcweir                                            const ::rtl::OUString& 	rStr,
152*cdf0e10cSrcweir                                            const sal_Int32 		nLen)
153*cdf0e10cSrcweir             {
154*cdf0e10cSrcweir                 sal_Unicode aChar( rStr[io_rPos] );
155*cdf0e10cSrcweir                 ::rtl::OUStringBuffer sNumberString;
156*cdf0e10cSrcweir 
157*cdf0e10cSrcweir                 if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar)
158*cdf0e10cSrcweir                 {
159*cdf0e10cSrcweir                     sNumberString.append(rStr[io_rPos]);
160*cdf0e10cSrcweir                     aChar = rStr[++io_rPos];
161*cdf0e10cSrcweir                 }
162*cdf0e10cSrcweir 
163*cdf0e10cSrcweir                 while(sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
164*cdf0e10cSrcweir                 {
165*cdf0e10cSrcweir                     sNumberString.append(rStr[io_rPos]);
166*cdf0e10cSrcweir                     aChar = rStr[++io_rPos];
167*cdf0e10cSrcweir                 }
168*cdf0e10cSrcweir 
169*cdf0e10cSrcweir                 if(sNumberString.getLength())
170*cdf0e10cSrcweir                 {
171*cdf0e10cSrcweir                     o_nRetval = sNumberString.makeStringAndClear().toInt32();
172*cdf0e10cSrcweir                     lcl_skipSpacesAndCommas(io_rPos, rStr, nLen);
173*cdf0e10cSrcweir 
174*cdf0e10cSrcweir                     return true;
175*cdf0e10cSrcweir                 }
176*cdf0e10cSrcweir 
177*cdf0e10cSrcweir                 return false;
178*cdf0e10cSrcweir             }
179*cdf0e10cSrcweir 
180*cdf0e10cSrcweir             void lcl_skipNumber(sal_Int32& 				io_rPos,
181*cdf0e10cSrcweir                                 const ::rtl::OUString& 	rStr,
182*cdf0e10cSrcweir                                 const sal_Int32 		nLen)
183*cdf0e10cSrcweir             {
184*cdf0e10cSrcweir                 bool bSignAllowed(true);
185*cdf0e10cSrcweir 
186*cdf0e10cSrcweir                 while(io_rPos < nLen && lcl_isOnNumberChar(rStr, io_rPos, bSignAllowed))
187*cdf0e10cSrcweir                 {
188*cdf0e10cSrcweir                     bSignAllowed = false;
189*cdf0e10cSrcweir                     ++io_rPos;
190*cdf0e10cSrcweir                 }
191*cdf0e10cSrcweir             }
192*cdf0e10cSrcweir 
193*cdf0e10cSrcweir             void lcl_skipDouble(sal_Int32& 				io_rPos,
194*cdf0e10cSrcweir                                 const ::rtl::OUString& 	rStr,
195*cdf0e10cSrcweir                                 const sal_Int32 		/*nLen*/)
196*cdf0e10cSrcweir             {
197*cdf0e10cSrcweir                 sal_Unicode aChar( rStr[io_rPos] );
198*cdf0e10cSrcweir 
199*cdf0e10cSrcweir                 if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar)
200*cdf0e10cSrcweir                     aChar = rStr[++io_rPos];
201*cdf0e10cSrcweir 
202*cdf0e10cSrcweir                 while((sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
203*cdf0e10cSrcweir                       || sal_Unicode('.') == aChar)
204*cdf0e10cSrcweir                 {
205*cdf0e10cSrcweir                     aChar = rStr[++io_rPos];
206*cdf0e10cSrcweir                 }
207*cdf0e10cSrcweir 
208*cdf0e10cSrcweir                 if(sal_Unicode('e') == aChar || sal_Unicode('E') == aChar)
209*cdf0e10cSrcweir                 {
210*cdf0e10cSrcweir                     aChar = rStr[++io_rPos];
211*cdf0e10cSrcweir 
212*cdf0e10cSrcweir                     if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar)
213*cdf0e10cSrcweir                         aChar = rStr[++io_rPos];
214*cdf0e10cSrcweir 
215*cdf0e10cSrcweir                     while(sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
216*cdf0e10cSrcweir                     {
217*cdf0e10cSrcweir                         aChar = rStr[++io_rPos];
218*cdf0e10cSrcweir                     }
219*cdf0e10cSrcweir                 }
220*cdf0e10cSrcweir             }
221*cdf0e10cSrcweir             void lcl_skipNumberAndSpacesAndCommas(sal_Int32& 				io_rPos,
222*cdf0e10cSrcweir                                                   const ::rtl::OUString& 	rStr,
223*cdf0e10cSrcweir                                                   const sal_Int32 			nLen)
224*cdf0e10cSrcweir             {
225*cdf0e10cSrcweir                 lcl_skipNumber(io_rPos, rStr, nLen);
226*cdf0e10cSrcweir                 lcl_skipSpacesAndCommas(io_rPos, rStr, nLen);
227*cdf0e10cSrcweir             }
228*cdf0e10cSrcweir 
229*cdf0e10cSrcweir 			// #100617# Allow to skip doubles, too.
230*cdf0e10cSrcweir             void lcl_skipDoubleAndSpacesAndCommas(sal_Int32& 				io_rPos,
231*cdf0e10cSrcweir                                                   const ::rtl::OUString& 	rStr,
232*cdf0e10cSrcweir                                                   const sal_Int32 			nLen)
233*cdf0e10cSrcweir             {
234*cdf0e10cSrcweir                 lcl_skipDouble(io_rPos, rStr, nLen);
235*cdf0e10cSrcweir                 lcl_skipSpacesAndCommas(io_rPos, rStr, nLen);
236*cdf0e10cSrcweir             }
237*cdf0e10cSrcweir 
238*cdf0e10cSrcweir             void lcl_putNumberChar( ::rtl::OUStringBuffer& rStr,
239*cdf0e10cSrcweir                                     double 		 	       fValue )
240*cdf0e10cSrcweir             {
241*cdf0e10cSrcweir                 rStr.append( fValue );
242*cdf0e10cSrcweir             }
243*cdf0e10cSrcweir 
244*cdf0e10cSrcweir             void lcl_putNumberCharWithSpace( ::rtl::OUStringBuffer& rStr,
245*cdf0e10cSrcweir                                              double 		        fValue,
246*cdf0e10cSrcweir                                              double 		        fOldValue,
247*cdf0e10cSrcweir                                              bool 			        bUseRelativeCoordinates )
248*cdf0e10cSrcweir             {
249*cdf0e10cSrcweir                 if( bUseRelativeCoordinates )
250*cdf0e10cSrcweir                     fValue -= fOldValue;
251*cdf0e10cSrcweir 
252*cdf0e10cSrcweir                 const sal_Int32 aLen( rStr.getLength() );
253*cdf0e10cSrcweir                 if(aLen)
254*cdf0e10cSrcweir                 {
255*cdf0e10cSrcweir                     if( lcl_isOnNumberChar(rStr.charAt(aLen - 1), false) &&
256*cdf0e10cSrcweir                         fValue >= 0.0 )
257*cdf0e10cSrcweir                     {
258*cdf0e10cSrcweir                         rStr.append( sal_Unicode(' ') );
259*cdf0e10cSrcweir                     }
260*cdf0e10cSrcweir                 }
261*cdf0e10cSrcweir 
262*cdf0e10cSrcweir                 lcl_putNumberChar(rStr, fValue);
263*cdf0e10cSrcweir             }
264*cdf0e10cSrcweir 
265*cdf0e10cSrcweir             inline sal_Unicode lcl_getCommand( sal_Char cUpperCaseCommand,
266*cdf0e10cSrcweir                                                sal_Char cLowerCaseCommand,
267*cdf0e10cSrcweir                                                bool 	bUseRelativeCoordinates )
268*cdf0e10cSrcweir             {
269*cdf0e10cSrcweir                 return bUseRelativeCoordinates ? cLowerCaseCommand : cUpperCaseCommand;
270*cdf0e10cSrcweir             }
271*cdf0e10cSrcweir         }
272*cdf0e10cSrcweir 
273*cdf0e10cSrcweir         bool importFromSvgD(B2DPolyPolygon& o_rPolyPolygon, const ::rtl::OUString& 	rSvgDStatement)
274*cdf0e10cSrcweir         {
275*cdf0e10cSrcweir             o_rPolyPolygon.clear();
276*cdf0e10cSrcweir             const sal_Int32 nLen(rSvgDStatement.getLength());
277*cdf0e10cSrcweir             sal_Int32 nPos(0);
278*cdf0e10cSrcweir             bool bIsClosed(false);
279*cdf0e10cSrcweir             double nLastX( 0.0 );
280*cdf0e10cSrcweir             double nLastY( 0.0 );
281*cdf0e10cSrcweir 			B2DPolygon aCurrPoly;
282*cdf0e10cSrcweir 
283*cdf0e10cSrcweir 			// skip initial whitespace
284*cdf0e10cSrcweir             lcl_skipSpaces(nPos, rSvgDStatement, nLen);
285*cdf0e10cSrcweir 
286*cdf0e10cSrcweir             while(nPos < nLen)
287*cdf0e10cSrcweir             {
288*cdf0e10cSrcweir                 bool bRelative(false);
289*cdf0e10cSrcweir                 bool bMoveTo(false);
290*cdf0e10cSrcweir                 const sal_Unicode aCurrChar(rSvgDStatement[nPos]);
291*cdf0e10cSrcweir 
292*cdf0e10cSrcweir                 switch(aCurrChar)
293*cdf0e10cSrcweir                 {
294*cdf0e10cSrcweir                     case 'z' :
295*cdf0e10cSrcweir                     case 'Z' :
296*cdf0e10cSrcweir                     {
297*cdf0e10cSrcweir                         nPos++;
298*cdf0e10cSrcweir                         lcl_skipSpaces(nPos, rSvgDStatement, nLen);
299*cdf0e10cSrcweir 
300*cdf0e10cSrcweir                         // remember closed state of current polygon
301*cdf0e10cSrcweir                         bIsClosed = true;
302*cdf0e10cSrcweir                         break;
303*cdf0e10cSrcweir                     }
304*cdf0e10cSrcweir 
305*cdf0e10cSrcweir                     case 'm' :
306*cdf0e10cSrcweir                     case 'M' :
307*cdf0e10cSrcweir                     {
308*cdf0e10cSrcweir                         bMoveTo = true;
309*cdf0e10cSrcweir                         // FALLTHROUGH intended
310*cdf0e10cSrcweir                     }
311*cdf0e10cSrcweir                     case 'l' :
312*cdf0e10cSrcweir                     case 'L' :
313*cdf0e10cSrcweir                     {
314*cdf0e10cSrcweir                         if('m' == aCurrChar || 'l' == aCurrChar)
315*cdf0e10cSrcweir 						{
316*cdf0e10cSrcweir                             bRelative = true;
317*cdf0e10cSrcweir 						}
318*cdf0e10cSrcweir 
319*cdf0e10cSrcweir                         if(bMoveTo)
320*cdf0e10cSrcweir                         {
321*cdf0e10cSrcweir 							// new polygon start, finish old one
322*cdf0e10cSrcweir                             if(aCurrPoly.count())
323*cdf0e10cSrcweir                             {
324*cdf0e10cSrcweir 								// add current polygon
325*cdf0e10cSrcweir 								if(bIsClosed)
326*cdf0e10cSrcweir 								{
327*cdf0e10cSrcweir 									closeWithGeometryChange(aCurrPoly);
328*cdf0e10cSrcweir 								}
329*cdf0e10cSrcweir 
330*cdf0e10cSrcweir 								o_rPolyPolygon.append(aCurrPoly);
331*cdf0e10cSrcweir 
332*cdf0e10cSrcweir 								// reset import values
333*cdf0e10cSrcweir 								bIsClosed = false;
334*cdf0e10cSrcweir                                 aCurrPoly.clear();
335*cdf0e10cSrcweir                             }
336*cdf0e10cSrcweir                         }
337*cdf0e10cSrcweir 
338*cdf0e10cSrcweir                         nPos++;
339*cdf0e10cSrcweir                         lcl_skipSpaces(nPos, rSvgDStatement, nLen);
340*cdf0e10cSrcweir 
341*cdf0e10cSrcweir                         while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
342*cdf0e10cSrcweir                         {
343*cdf0e10cSrcweir                             double nX, nY;
344*cdf0e10cSrcweir 
345*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
346*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
347*cdf0e10cSrcweir 
348*cdf0e10cSrcweir                             if(bRelative)
349*cdf0e10cSrcweir                             {
350*cdf0e10cSrcweir                                 nX += nLastX;
351*cdf0e10cSrcweir                                 nY += nLastY;
352*cdf0e10cSrcweir                             }
353*cdf0e10cSrcweir 
354*cdf0e10cSrcweir                             // set last position
355*cdf0e10cSrcweir                             nLastX = nX;
356*cdf0e10cSrcweir                             nLastY = nY;
357*cdf0e10cSrcweir 
358*cdf0e10cSrcweir                             // add point
359*cdf0e10cSrcweir                             aCurrPoly.append(B2DPoint(nX, nY));
360*cdf0e10cSrcweir                         }
361*cdf0e10cSrcweir                         break;
362*cdf0e10cSrcweir                     }
363*cdf0e10cSrcweir 
364*cdf0e10cSrcweir                     case 'h' :
365*cdf0e10cSrcweir                     {
366*cdf0e10cSrcweir                         bRelative = true;
367*cdf0e10cSrcweir                         // FALLTHROUGH intended
368*cdf0e10cSrcweir                     }
369*cdf0e10cSrcweir                     case 'H' :
370*cdf0e10cSrcweir                     {
371*cdf0e10cSrcweir                         nPos++;
372*cdf0e10cSrcweir                         lcl_skipSpaces(nPos, rSvgDStatement, nLen);
373*cdf0e10cSrcweir 
374*cdf0e10cSrcweir                         while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
375*cdf0e10cSrcweir                         {
376*cdf0e10cSrcweir                             double nX, nY(nLastY);
377*cdf0e10cSrcweir 
378*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
379*cdf0e10cSrcweir 
380*cdf0e10cSrcweir                             if(bRelative)
381*cdf0e10cSrcweir 							{
382*cdf0e10cSrcweir                                 nX += nLastX;
383*cdf0e10cSrcweir 							}
384*cdf0e10cSrcweir 
385*cdf0e10cSrcweir                             // set last position
386*cdf0e10cSrcweir                             nLastX = nX;
387*cdf0e10cSrcweir 
388*cdf0e10cSrcweir                             // add point
389*cdf0e10cSrcweir                             aCurrPoly.append(B2DPoint(nX, nY));
390*cdf0e10cSrcweir                         }
391*cdf0e10cSrcweir                         break;
392*cdf0e10cSrcweir                     }
393*cdf0e10cSrcweir 
394*cdf0e10cSrcweir                     case 'v' :
395*cdf0e10cSrcweir                     {
396*cdf0e10cSrcweir                         bRelative = true;
397*cdf0e10cSrcweir                         // FALLTHROUGH intended
398*cdf0e10cSrcweir                     }
399*cdf0e10cSrcweir                     case 'V' :
400*cdf0e10cSrcweir                     {
401*cdf0e10cSrcweir                         nPos++;
402*cdf0e10cSrcweir                         lcl_skipSpaces(nPos, rSvgDStatement, nLen);
403*cdf0e10cSrcweir 
404*cdf0e10cSrcweir                         while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
405*cdf0e10cSrcweir                         {
406*cdf0e10cSrcweir                             double nX(nLastX), nY;
407*cdf0e10cSrcweir 
408*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
409*cdf0e10cSrcweir 
410*cdf0e10cSrcweir                             if(bRelative)
411*cdf0e10cSrcweir 							{
412*cdf0e10cSrcweir                                 nY += nLastY;
413*cdf0e10cSrcweir 							}
414*cdf0e10cSrcweir 
415*cdf0e10cSrcweir                             // set last position
416*cdf0e10cSrcweir                             nLastY = nY;
417*cdf0e10cSrcweir 
418*cdf0e10cSrcweir                             // add point
419*cdf0e10cSrcweir                             aCurrPoly.append(B2DPoint(nX, nY));
420*cdf0e10cSrcweir                         }
421*cdf0e10cSrcweir                         break;
422*cdf0e10cSrcweir                     }
423*cdf0e10cSrcweir 
424*cdf0e10cSrcweir                     case 's' :
425*cdf0e10cSrcweir                     {
426*cdf0e10cSrcweir                         bRelative = true;
427*cdf0e10cSrcweir                         // FALLTHROUGH intended
428*cdf0e10cSrcweir                     }
429*cdf0e10cSrcweir                     case 'S' :
430*cdf0e10cSrcweir                     {
431*cdf0e10cSrcweir                         nPos++;
432*cdf0e10cSrcweir                         lcl_skipSpaces(nPos, rSvgDStatement, nLen);
433*cdf0e10cSrcweir 
434*cdf0e10cSrcweir                         while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
435*cdf0e10cSrcweir                         {
436*cdf0e10cSrcweir                             double nX, nY;
437*cdf0e10cSrcweir                             double nX2, nY2;
438*cdf0e10cSrcweir 
439*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nX2, nPos, rSvgDStatement, nLen)) return false;
440*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nY2, nPos, rSvgDStatement, nLen)) return false;
441*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
442*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
443*cdf0e10cSrcweir 
444*cdf0e10cSrcweir                             if(bRelative)
445*cdf0e10cSrcweir                             {
446*cdf0e10cSrcweir                                 nX2 += nLastX;
447*cdf0e10cSrcweir                                 nY2 += nLastY;
448*cdf0e10cSrcweir                                 nX += nLastX;
449*cdf0e10cSrcweir                                 nY += nLastY;
450*cdf0e10cSrcweir                             }
451*cdf0e10cSrcweir 
452*cdf0e10cSrcweir 							// ensure existance of start point
453*cdf0e10cSrcweir 							if(!aCurrPoly.count())
454*cdf0e10cSrcweir 							{
455*cdf0e10cSrcweir                                 aCurrPoly.append(B2DPoint(nLastX, nLastY));
456*cdf0e10cSrcweir 							}
457*cdf0e10cSrcweir 
458*cdf0e10cSrcweir 							// get first control point. It's the reflection of the PrevControlPoint
459*cdf0e10cSrcweir 							// of the last point. If not existent, use current point (see SVG)
460*cdf0e10cSrcweir 							B2DPoint aPrevControl(B2DPoint(nLastX, nLastY));
461*cdf0e10cSrcweir 							const sal_uInt32 nIndex(aCurrPoly.count() - 1);
462*cdf0e10cSrcweir 
463*cdf0e10cSrcweir 							if(aCurrPoly.areControlPointsUsed() && aCurrPoly.isPrevControlPointUsed(nIndex))
464*cdf0e10cSrcweir 							{
465*cdf0e10cSrcweir 								const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(nIndex));
466*cdf0e10cSrcweir 								const B2DPoint aPrevControlPoint(aCurrPoly.getPrevControlPoint(nIndex));
467*cdf0e10cSrcweir 
468*cdf0e10cSrcweir 								// use mirrored previous control point
469*cdf0e10cSrcweir 								aPrevControl.setX((2.0 * aPrevPoint.getX()) - aPrevControlPoint.getX());
470*cdf0e10cSrcweir 								aPrevControl.setY((2.0 * aPrevPoint.getY()) - aPrevControlPoint.getY());
471*cdf0e10cSrcweir 							}
472*cdf0e10cSrcweir 
473*cdf0e10cSrcweir 							// append curved edge
474*cdf0e10cSrcweir 							aCurrPoly.appendBezierSegment(aPrevControl, B2DPoint(nX2, nY2), B2DPoint(nX, nY));
475*cdf0e10cSrcweir 
476*cdf0e10cSrcweir                             // set last position
477*cdf0e10cSrcweir                             nLastX = nX;
478*cdf0e10cSrcweir                             nLastY = nY;
479*cdf0e10cSrcweir                         }
480*cdf0e10cSrcweir                         break;
481*cdf0e10cSrcweir                     }
482*cdf0e10cSrcweir 
483*cdf0e10cSrcweir                     case 'c' :
484*cdf0e10cSrcweir                     {
485*cdf0e10cSrcweir                         bRelative = true;
486*cdf0e10cSrcweir                         // FALLTHROUGH intended
487*cdf0e10cSrcweir                     }
488*cdf0e10cSrcweir                     case 'C' :
489*cdf0e10cSrcweir                     {
490*cdf0e10cSrcweir                         nPos++;
491*cdf0e10cSrcweir                         lcl_skipSpaces(nPos, rSvgDStatement, nLen);
492*cdf0e10cSrcweir 
493*cdf0e10cSrcweir                         while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
494*cdf0e10cSrcweir                         {
495*cdf0e10cSrcweir                             double nX, nY;
496*cdf0e10cSrcweir                             double nX1, nY1;
497*cdf0e10cSrcweir                             double nX2, nY2;
498*cdf0e10cSrcweir 
499*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nX1, nPos, rSvgDStatement, nLen)) return false;
500*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nY1, nPos, rSvgDStatement, nLen)) return false;
501*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nX2, nPos, rSvgDStatement, nLen)) return false;
502*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nY2, nPos, rSvgDStatement, nLen)) return false;
503*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
504*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
505*cdf0e10cSrcweir 
506*cdf0e10cSrcweir                             if(bRelative)
507*cdf0e10cSrcweir                             {
508*cdf0e10cSrcweir                                 nX1 += nLastX;
509*cdf0e10cSrcweir                                 nY1 += nLastY;
510*cdf0e10cSrcweir                                 nX2 += nLastX;
511*cdf0e10cSrcweir                                 nY2 += nLastY;
512*cdf0e10cSrcweir                                 nX += nLastX;
513*cdf0e10cSrcweir                                 nY += nLastY;
514*cdf0e10cSrcweir                             }
515*cdf0e10cSrcweir 
516*cdf0e10cSrcweir 							// ensure existance of start point
517*cdf0e10cSrcweir 							if(!aCurrPoly.count())
518*cdf0e10cSrcweir 							{
519*cdf0e10cSrcweir                                 aCurrPoly.append(B2DPoint(nLastX, nLastY));
520*cdf0e10cSrcweir 							}
521*cdf0e10cSrcweir 
522*cdf0e10cSrcweir 							// append curved edge
523*cdf0e10cSrcweir 							aCurrPoly.appendBezierSegment(B2DPoint(nX1, nY1), B2DPoint(nX2, nY2), B2DPoint(nX, nY));
524*cdf0e10cSrcweir 
525*cdf0e10cSrcweir                             // set last position
526*cdf0e10cSrcweir                             nLastX = nX;
527*cdf0e10cSrcweir                             nLastY = nY;
528*cdf0e10cSrcweir                         }
529*cdf0e10cSrcweir                         break;
530*cdf0e10cSrcweir                     }
531*cdf0e10cSrcweir 
532*cdf0e10cSrcweir                     // #100617# quadratic beziers are imported as cubic ones
533*cdf0e10cSrcweir                     case 'q' :
534*cdf0e10cSrcweir                     {
535*cdf0e10cSrcweir                         bRelative = true;
536*cdf0e10cSrcweir                         // FALLTHROUGH intended
537*cdf0e10cSrcweir                     }
538*cdf0e10cSrcweir                     case 'Q' :
539*cdf0e10cSrcweir                     {
540*cdf0e10cSrcweir                         nPos++;
541*cdf0e10cSrcweir                         lcl_skipSpaces(nPos, rSvgDStatement, nLen);
542*cdf0e10cSrcweir 
543*cdf0e10cSrcweir                         while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
544*cdf0e10cSrcweir                         {
545*cdf0e10cSrcweir                             double nX, nY;
546*cdf0e10cSrcweir                             double nX1, nY1;
547*cdf0e10cSrcweir 
548*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nX1, nPos, rSvgDStatement, nLen)) return false;
549*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nY1, nPos, rSvgDStatement, nLen)) return false;
550*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
551*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
552*cdf0e10cSrcweir 
553*cdf0e10cSrcweir                             if(bRelative)
554*cdf0e10cSrcweir                             {
555*cdf0e10cSrcweir                                 nX1 += nLastX;
556*cdf0e10cSrcweir                                 nY1 += nLastY;
557*cdf0e10cSrcweir                                 nX += nLastX;
558*cdf0e10cSrcweir                                 nY += nLastY;
559*cdf0e10cSrcweir                             }
560*cdf0e10cSrcweir 
561*cdf0e10cSrcweir                             // calculate the cubic bezier coefficients from the quadratic ones
562*cdf0e10cSrcweir                             const double nX1Prime((nX1 * 2.0 + nLastX) / 3.0);
563*cdf0e10cSrcweir                             const double nY1Prime((nY1 * 2.0 + nLastY) / 3.0);
564*cdf0e10cSrcweir                             const double nX2Prime((nX1 * 2.0 + nX) / 3.0);
565*cdf0e10cSrcweir                             const double nY2Prime((nY1 * 2.0 + nY) / 3.0);
566*cdf0e10cSrcweir 
567*cdf0e10cSrcweir 							// ensure existance of start point
568*cdf0e10cSrcweir 							if(!aCurrPoly.count())
569*cdf0e10cSrcweir 							{
570*cdf0e10cSrcweir                                 aCurrPoly.append(B2DPoint(nLastX, nLastY));
571*cdf0e10cSrcweir 							}
572*cdf0e10cSrcweir 
573*cdf0e10cSrcweir 							// append curved edge
574*cdf0e10cSrcweir 							aCurrPoly.appendBezierSegment(B2DPoint(nX1Prime, nY1Prime), B2DPoint(nX2Prime, nY2Prime), B2DPoint(nX, nY));
575*cdf0e10cSrcweir 
576*cdf0e10cSrcweir                             // set last position
577*cdf0e10cSrcweir                             nLastX = nX;
578*cdf0e10cSrcweir                             nLastY = nY;
579*cdf0e10cSrcweir                         }
580*cdf0e10cSrcweir                         break;
581*cdf0e10cSrcweir                     }
582*cdf0e10cSrcweir 
583*cdf0e10cSrcweir                     // #100617# relative quadratic beziers are imported as cubic
584*cdf0e10cSrcweir                     case 't' :
585*cdf0e10cSrcweir                     {
586*cdf0e10cSrcweir                         bRelative = true;
587*cdf0e10cSrcweir                         // FALLTHROUGH intended
588*cdf0e10cSrcweir                     }
589*cdf0e10cSrcweir                     case 'T' :
590*cdf0e10cSrcweir                     {
591*cdf0e10cSrcweir                         nPos++;
592*cdf0e10cSrcweir                         lcl_skipSpaces(nPos, rSvgDStatement, nLen);
593*cdf0e10cSrcweir 
594*cdf0e10cSrcweir                         while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
595*cdf0e10cSrcweir                         {
596*cdf0e10cSrcweir                             double nX, nY;
597*cdf0e10cSrcweir 
598*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
599*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
600*cdf0e10cSrcweir 
601*cdf0e10cSrcweir                             if(bRelative)
602*cdf0e10cSrcweir                             {
603*cdf0e10cSrcweir                                 nX += nLastX;
604*cdf0e10cSrcweir                                 nY += nLastY;
605*cdf0e10cSrcweir                             }
606*cdf0e10cSrcweir 
607*cdf0e10cSrcweir 							// ensure existance of start point
608*cdf0e10cSrcweir 							if(!aCurrPoly.count())
609*cdf0e10cSrcweir 							{
610*cdf0e10cSrcweir                                 aCurrPoly.append(B2DPoint(nLastX, nLastY));
611*cdf0e10cSrcweir 							}
612*cdf0e10cSrcweir 
613*cdf0e10cSrcweir 							// get first control point. It's the reflection of the PrevControlPoint
614*cdf0e10cSrcweir 							// of the last point. If not existent, use current point (see SVG)
615*cdf0e10cSrcweir 							B2DPoint aPrevControl(B2DPoint(nLastX, nLastY));
616*cdf0e10cSrcweir 							const sal_uInt32 nIndex(aCurrPoly.count() - 1);
617*cdf0e10cSrcweir 							const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(nIndex));
618*cdf0e10cSrcweir 
619*cdf0e10cSrcweir 							if(aCurrPoly.areControlPointsUsed() && aCurrPoly.isPrevControlPointUsed(nIndex))
620*cdf0e10cSrcweir 							{
621*cdf0e10cSrcweir 								const B2DPoint aPrevControlPoint(aCurrPoly.getPrevControlPoint(nIndex));
622*cdf0e10cSrcweir 
623*cdf0e10cSrcweir 								// use mirrored previous control point
624*cdf0e10cSrcweir 								aPrevControl.setX((2.0 * aPrevPoint.getX()) - aPrevControlPoint.getX());
625*cdf0e10cSrcweir 								aPrevControl.setY((2.0 * aPrevPoint.getY()) - aPrevControlPoint.getY());
626*cdf0e10cSrcweir 							}
627*cdf0e10cSrcweir 
628*cdf0e10cSrcweir 							if(!aPrevControl.equal(aPrevPoint))
629*cdf0e10cSrcweir 							{
630*cdf0e10cSrcweir 								// there is a prev control point, and we have the already mirrored one
631*cdf0e10cSrcweir 								// in aPrevControl. We also need the quadratic control point for this
632*cdf0e10cSrcweir 								// new quadratic segment to calculate the 2nd cubic control point
633*cdf0e10cSrcweir 								const B2DPoint aQuadControlPoint(
634*cdf0e10cSrcweir 									((3.0 * aPrevControl.getX()) - aPrevPoint.getX()) / 2.0,
635*cdf0e10cSrcweir 									((3.0 * aPrevControl.getY()) - aPrevPoint.getY()) / 2.0);
636*cdf0e10cSrcweir 
637*cdf0e10cSrcweir 								// calculate the cubic bezier coefficients from the quadratic ones.
638*cdf0e10cSrcweir 								const double nX2Prime((aQuadControlPoint.getX() * 2.0 + nX) / 3.0);
639*cdf0e10cSrcweir 								const double nY2Prime((aQuadControlPoint.getY() * 2.0 + nY) / 3.0);
640*cdf0e10cSrcweir 
641*cdf0e10cSrcweir 								// append curved edge, use mirrored cubic control point directly
642*cdf0e10cSrcweir 								aCurrPoly.appendBezierSegment(aPrevControl, B2DPoint(nX2Prime, nY2Prime), B2DPoint(nX, nY));
643*cdf0e10cSrcweir 							}
644*cdf0e10cSrcweir 							else
645*cdf0e10cSrcweir 							{
646*cdf0e10cSrcweir 								// when no previous control, SVG says to use current point -> straight line.
647*cdf0e10cSrcweir 								// Just add end point
648*cdf0e10cSrcweir 								aCurrPoly.append(B2DPoint(nX, nY));
649*cdf0e10cSrcweir 							}
650*cdf0e10cSrcweir 
651*cdf0e10cSrcweir                             // set last position
652*cdf0e10cSrcweir                             nLastX = nX;
653*cdf0e10cSrcweir                             nLastY = nY;
654*cdf0e10cSrcweir                         }
655*cdf0e10cSrcweir                         break;
656*cdf0e10cSrcweir                     }
657*cdf0e10cSrcweir 
658*cdf0e10cSrcweir                     case 'a' :
659*cdf0e10cSrcweir                     {
660*cdf0e10cSrcweir                         bRelative = true;
661*cdf0e10cSrcweir                         // FALLTHROUGH intended
662*cdf0e10cSrcweir                     }
663*cdf0e10cSrcweir                     case 'A' :
664*cdf0e10cSrcweir                     {
665*cdf0e10cSrcweir                         nPos++;
666*cdf0e10cSrcweir                         lcl_skipSpaces(nPos, rSvgDStatement, nLen);
667*cdf0e10cSrcweir 
668*cdf0e10cSrcweir                         while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
669*cdf0e10cSrcweir                         {
670*cdf0e10cSrcweir                             double nX, nY;
671*cdf0e10cSrcweir                             double fRX, fRY, fPhi;
672*cdf0e10cSrcweir                             sal_Int32 bLargeArcFlag, bSweepFlag;
673*cdf0e10cSrcweir 
674*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(fRX, nPos, rSvgDStatement, nLen)) return false;
675*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(fRY, nPos, rSvgDStatement, nLen)) return false;
676*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(fPhi, nPos, rSvgDStatement, nLen)) return false;
677*cdf0e10cSrcweir                             if(!lcl_importNumberAndSpaces(bLargeArcFlag, nPos, rSvgDStatement, nLen)) return false;
678*cdf0e10cSrcweir                             if(!lcl_importNumberAndSpaces(bSweepFlag, nPos, rSvgDStatement, nLen)) return false;
679*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
680*cdf0e10cSrcweir                             if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
681*cdf0e10cSrcweir 
682*cdf0e10cSrcweir                             if(bRelative)
683*cdf0e10cSrcweir                             {
684*cdf0e10cSrcweir                                 nX += nLastX;
685*cdf0e10cSrcweir                                 nY += nLastY;
686*cdf0e10cSrcweir                             }
687*cdf0e10cSrcweir 
688*cdf0e10cSrcweir 							const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(aCurrPoly.count() - 1));
689*cdf0e10cSrcweir 
690*cdf0e10cSrcweir                             if( nX == nLastX && nY == nLastY )
691*cdf0e10cSrcweir                                 continue; // start==end -> skip according to SVG spec
692*cdf0e10cSrcweir 
693*cdf0e10cSrcweir                             if( fRX == 0.0 || fRY == 0.0 )
694*cdf0e10cSrcweir                             {
695*cdf0e10cSrcweir                                 // straight line segment according to SVG spec
696*cdf0e10cSrcweir                                 aCurrPoly.append(B2DPoint(nX, nY));
697*cdf0e10cSrcweir                             }
698*cdf0e10cSrcweir                             else
699*cdf0e10cSrcweir                             {
700*cdf0e10cSrcweir                                 // normalize according to SVG spec
701*cdf0e10cSrcweir                                 fRX=fabs(fRX); fRY=fabs(fRY);
702*cdf0e10cSrcweir 
703*cdf0e10cSrcweir                                 // from the SVG spec, appendix F.6.4
704*cdf0e10cSrcweir 
705*cdf0e10cSrcweir                                 // |x1'|   |cos phi   sin phi|  |(x1 - x2)/2|
706*cdf0e10cSrcweir                                 // |y1'| = |-sin phi  cos phi|  |(y1 - y2)/2|
707*cdf0e10cSrcweir                                 const B2DPoint p1(nLastX, nLastY);
708*cdf0e10cSrcweir                                 const B2DPoint p2(nX, nY);
709*cdf0e10cSrcweir                                 B2DHomMatrix aTransform(basegfx::tools::createRotateB2DHomMatrix(-fPhi*M_PI/180));
710*cdf0e10cSrcweir 
711*cdf0e10cSrcweir                                 const B2DPoint p1_prime( aTransform * B2DPoint(((p1-p2)/2.0)) );
712*cdf0e10cSrcweir 
713*cdf0e10cSrcweir                                 //           ______________________________________       rx y1'
714*cdf0e10cSrcweir                                 // |cx'|  + /  rx^2 ry^2 - rx^2 y1'^2 - ry^2 x1^2           ry
715*cdf0e10cSrcweir                                 // |cy'| =-/       rx^2y1'^2 + ry^2 x1'^2               - ry x1'
716*cdf0e10cSrcweir                                 //                                                          rx
717*cdf0e10cSrcweir                                 // chose + if f_A != f_S
718*cdf0e10cSrcweir                                 // chose - if f_A  = f_S
719*cdf0e10cSrcweir                                 B2DPoint aCenter_prime;
720*cdf0e10cSrcweir                                 const double fRadicant(
721*cdf0e10cSrcweir                                     (fRX*fRX*fRY*fRY - fRX*fRX*p1_prime.getY()*p1_prime.getY() - fRY*fRY*p1_prime.getX()*p1_prime.getX())/
722*cdf0e10cSrcweir                                     (fRX*fRX*p1_prime.getY()*p1_prime.getY() + fRY*fRY*p1_prime.getX()*p1_prime.getX()));
723*cdf0e10cSrcweir                                 if( fRadicant < 0.0 )
724*cdf0e10cSrcweir                                 {
725*cdf0e10cSrcweir                                     // no solution - according to SVG
726*cdf0e10cSrcweir                                     // spec, scale up ellipse
727*cdf0e10cSrcweir                                     // uniformly such that it passes
728*cdf0e10cSrcweir                                     // through end points (denominator
729*cdf0e10cSrcweir                                     // of radicant solved for fRY,
730*cdf0e10cSrcweir                                     // with s=fRX/fRY)
731*cdf0e10cSrcweir                                     const double fRatio(fRX/fRY);
732*cdf0e10cSrcweir                                     const double fRadicant2(
733*cdf0e10cSrcweir                                         p1_prime.getY()*p1_prime.getY() +
734*cdf0e10cSrcweir                                         p1_prime.getX()*p1_prime.getX()/(fRatio*fRatio));
735*cdf0e10cSrcweir                                     if( fRadicant2 < 0.0 )
736*cdf0e10cSrcweir                                     {
737*cdf0e10cSrcweir                                         // only trivial solution, one
738*cdf0e10cSrcweir                                         // of the axes 0 -> straight
739*cdf0e10cSrcweir                                         // line segment according to
740*cdf0e10cSrcweir                                         // SVG spec
741*cdf0e10cSrcweir                                         aCurrPoly.append(B2DPoint(nX, nY));
742*cdf0e10cSrcweir                                         continue;
743*cdf0e10cSrcweir                                     }
744*cdf0e10cSrcweir 
745*cdf0e10cSrcweir                                     fRY=sqrt(fRadicant2);
746*cdf0e10cSrcweir                                     fRX=fRatio*fRY;
747*cdf0e10cSrcweir 
748*cdf0e10cSrcweir                                     // keep center_prime forced to (0,0)
749*cdf0e10cSrcweir                                 }
750*cdf0e10cSrcweir                                 else
751*cdf0e10cSrcweir                                 {
752*cdf0e10cSrcweir                                     const double fFactor(
753*cdf0e10cSrcweir                                         (bLargeArcFlag==bSweepFlag ? -1.0 : 1.0) *
754*cdf0e10cSrcweir                                         sqrt(fRadicant));
755*cdf0e10cSrcweir 
756*cdf0e10cSrcweir                                     // actually calculate center_prime
757*cdf0e10cSrcweir                                     aCenter_prime = B2DPoint(
758*cdf0e10cSrcweir                                         fFactor*fRX*p1_prime.getY()/fRY,
759*cdf0e10cSrcweir                                         -fFactor*fRY*p1_prime.getX()/fRX);
760*cdf0e10cSrcweir                                 }
761*cdf0e10cSrcweir 
762*cdf0e10cSrcweir                                 //              +           u - v
763*cdf0e10cSrcweir                                 // angle(u,v) =  arccos( ------------ )     (take the sign of (ux vy - uy vx))
764*cdf0e10cSrcweir                                 //              -        ||u|| ||v||
765*cdf0e10cSrcweir 
766*cdf0e10cSrcweir                                 //                  1    | (x1' - cx')/rx |
767*cdf0e10cSrcweir                                 // theta1 = angle((   ), |                | )
768*cdf0e10cSrcweir                                 //                  0    | (y1' - cy')/ry |
769*cdf0e10cSrcweir                                 const B2DPoint aRadii(fRX,fRY);
770*cdf0e10cSrcweir                                 double fTheta1(
771*cdf0e10cSrcweir                                     B2DVector(1.0,0.0).angle(
772*cdf0e10cSrcweir                                         (p1_prime-aCenter_prime)/aRadii));
773*cdf0e10cSrcweir 
774*cdf0e10cSrcweir                                 //                 |1|    |  (-x1' - cx')/rx |
775*cdf0e10cSrcweir                                 // theta2 = angle( | | ,  |                  | )
776*cdf0e10cSrcweir                                 //                 |0|    |  (-y1' - cy')/ry |
777*cdf0e10cSrcweir                                 double fTheta2(
778*cdf0e10cSrcweir                                     B2DVector(1.0,0.0).angle(
779*cdf0e10cSrcweir                                         (-p1_prime-aCenter_prime)/aRadii));
780*cdf0e10cSrcweir 
781*cdf0e10cSrcweir                                 // map both angles to [0,2pi)
782*cdf0e10cSrcweir                                 fTheta1 = fmod(2*M_PI+fTheta1,2*M_PI);
783*cdf0e10cSrcweir                                 fTheta2 = fmod(2*M_PI+fTheta2,2*M_PI);
784*cdf0e10cSrcweir 
785*cdf0e10cSrcweir                                 // make sure the large arc is taken
786*cdf0e10cSrcweir                                 // (since
787*cdf0e10cSrcweir                                 // createPolygonFromEllipseSegment()
788*cdf0e10cSrcweir                                 // normalizes to e.g. cw arc)
789*cdf0e10cSrcweir                                 const bool bFlipSegment( (bLargeArcFlag!=0) ==
790*cdf0e10cSrcweir                                     (fmod(fTheta2+2*M_PI-fTheta1,
791*cdf0e10cSrcweir                                           2*M_PI)<M_PI) );
792*cdf0e10cSrcweir                                 if( bFlipSegment )
793*cdf0e10cSrcweir                                     std::swap(fTheta1,fTheta2);
794*cdf0e10cSrcweir 
795*cdf0e10cSrcweir                                 // finally, create bezier polygon from this
796*cdf0e10cSrcweir                                 B2DPolygon aSegment(
797*cdf0e10cSrcweir                                     tools::createPolygonFromUnitEllipseSegment(
798*cdf0e10cSrcweir                                         fTheta1, fTheta2 ));
799*cdf0e10cSrcweir 
800*cdf0e10cSrcweir                                 // transform ellipse by rotation & move to final center
801*cdf0e10cSrcweir                                 aTransform = basegfx::tools::createScaleB2DHomMatrix(fRX, fRY);
802*cdf0e10cSrcweir                                 aTransform.translate(aCenter_prime.getX(),
803*cdf0e10cSrcweir                                                      aCenter_prime.getY());
804*cdf0e10cSrcweir                                 aTransform.rotate(fPhi*M_PI/180);
805*cdf0e10cSrcweir                                 const B2DPoint aOffset((p1+p2)/2.0);
806*cdf0e10cSrcweir                                 aTransform.translate(aOffset.getX(),
807*cdf0e10cSrcweir                                                      aOffset.getY());
808*cdf0e10cSrcweir                                 aSegment.transform(aTransform);
809*cdf0e10cSrcweir 
810*cdf0e10cSrcweir                                 // createPolygonFromEllipseSegment()
811*cdf0e10cSrcweir                                 // always creates arcs that are
812*cdf0e10cSrcweir                                 // positively oriented - flip polygon
813*cdf0e10cSrcweir                                 // if we swapped angles above
814*cdf0e10cSrcweir                                 if( bFlipSegment )
815*cdf0e10cSrcweir                                     aSegment.flip();
816*cdf0e10cSrcweir                                 aCurrPoly.append(aSegment);
817*cdf0e10cSrcweir                             }
818*cdf0e10cSrcweir 
819*cdf0e10cSrcweir                             // set last position
820*cdf0e10cSrcweir                             nLastX = nX;
821*cdf0e10cSrcweir                             nLastY = nY;
822*cdf0e10cSrcweir                         }
823*cdf0e10cSrcweir                         break;
824*cdf0e10cSrcweir                     }
825*cdf0e10cSrcweir 
826*cdf0e10cSrcweir                     default:
827*cdf0e10cSrcweir                     {
828*cdf0e10cSrcweir                         OSL_ENSURE(false, "importFromSvgD(): skipping tags in svg:d element (unknown)!");
829*cdf0e10cSrcweir                         OSL_TRACE("importFromSvgD(): skipping tags in svg:d element (unknown: \"%c\")!", aCurrChar);
830*cdf0e10cSrcweir                         ++nPos;
831*cdf0e10cSrcweir                         break;
832*cdf0e10cSrcweir                     }
833*cdf0e10cSrcweir                 }
834*cdf0e10cSrcweir             }
835*cdf0e10cSrcweir 
836*cdf0e10cSrcweir             if(aCurrPoly.count())
837*cdf0e10cSrcweir             {
838*cdf0e10cSrcweir                 // end-process last poly
839*cdf0e10cSrcweir 				if(bIsClosed)
840*cdf0e10cSrcweir 				{
841*cdf0e10cSrcweir 					closeWithGeometryChange(aCurrPoly);
842*cdf0e10cSrcweir 				}
843*cdf0e10cSrcweir 
844*cdf0e10cSrcweir 				o_rPolyPolygon.append(aCurrPoly);
845*cdf0e10cSrcweir             }
846*cdf0e10cSrcweir 
847*cdf0e10cSrcweir             return true;
848*cdf0e10cSrcweir         }
849*cdf0e10cSrcweir 
850*cdf0e10cSrcweir         bool importFromSvgPoints( B2DPolygon&            o_rPoly,
851*cdf0e10cSrcweir                                   const ::rtl::OUString& rSvgPointsAttribute )
852*cdf0e10cSrcweir         {
853*cdf0e10cSrcweir             o_rPoly.clear();
854*cdf0e10cSrcweir             const sal_Int32 nLen(rSvgPointsAttribute.getLength());
855*cdf0e10cSrcweir             sal_Int32 nPos(0);
856*cdf0e10cSrcweir             double nX, nY;
857*cdf0e10cSrcweir 
858*cdf0e10cSrcweir             // skip initial whitespace
859*cdf0e10cSrcweir             lcl_skipSpaces(nPos, rSvgPointsAttribute, nLen);
860*cdf0e10cSrcweir 
861*cdf0e10cSrcweir             while(nPos < nLen)
862*cdf0e10cSrcweir             {
863*cdf0e10cSrcweir                 if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgPointsAttribute, nLen)) return false;
864*cdf0e10cSrcweir                 if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgPointsAttribute, nLen)) return false;
865*cdf0e10cSrcweir 
866*cdf0e10cSrcweir                 // add point
867*cdf0e10cSrcweir                 o_rPoly.append(B2DPoint(nX, nY));
868*cdf0e10cSrcweir 
869*cdf0e10cSrcweir                 // skip to next number, or finish
870*cdf0e10cSrcweir                 lcl_skipSpaces(nPos, rSvgPointsAttribute, nLen);
871*cdf0e10cSrcweir             }
872*cdf0e10cSrcweir 
873*cdf0e10cSrcweir             return true;
874*cdf0e10cSrcweir         }
875*cdf0e10cSrcweir 
876*cdf0e10cSrcweir         ::rtl::OUString exportToSvgD(
877*cdf0e10cSrcweir 			const B2DPolyPolygon& rPolyPolygon,
878*cdf0e10cSrcweir 			bool bUseRelativeCoordinates,
879*cdf0e10cSrcweir 			bool bDetectQuadraticBeziers)
880*cdf0e10cSrcweir         {
881*cdf0e10cSrcweir             const sal_uInt32 nCount(rPolyPolygon.count());
882*cdf0e10cSrcweir             ::rtl::OUStringBuffer aResult;
883*cdf0e10cSrcweir             B2DPoint aCurrentSVGPosition(0.0, 0.0); // SVG assumes (0,0) as the initial current point
884*cdf0e10cSrcweir 
885*cdf0e10cSrcweir             for(sal_uInt32 i(0); i < nCount; i++)
886*cdf0e10cSrcweir             {
887*cdf0e10cSrcweir                 const B2DPolygon aPolygon(rPolyPolygon.getB2DPolygon(i));
888*cdf0e10cSrcweir                 const sal_uInt32 nPointCount(aPolygon.count());
889*cdf0e10cSrcweir 
890*cdf0e10cSrcweir 				if(nPointCount)
891*cdf0e10cSrcweir 				{
892*cdf0e10cSrcweir 					const bool bPolyUsesControlPoints(aPolygon.areControlPointsUsed());
893*cdf0e10cSrcweir 					const sal_uInt32 nEdgeCount(aPolygon.isClosed() ? nPointCount : nPointCount - 1);
894*cdf0e10cSrcweir 					sal_Unicode aLastSVGCommand(' '); // last SVG command char
895*cdf0e10cSrcweir 					B2DPoint aLeft, aRight; // for quadratic bezier test
896*cdf0e10cSrcweir 
897*cdf0e10cSrcweir 					// handle polygon start point
898*cdf0e10cSrcweir 					B2DPoint aEdgeStart(aPolygon.getB2DPoint(0));
899*cdf0e10cSrcweir                     aResult.append(lcl_getCommand('M', 'm', bUseRelativeCoordinates));
900*cdf0e10cSrcweir 					lcl_putNumberCharWithSpace(aResult, aEdgeStart.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
901*cdf0e10cSrcweir 					lcl_putNumberCharWithSpace(aResult, aEdgeStart.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
902*cdf0e10cSrcweir 					aLastSVGCommand =  lcl_getCommand('L', 'l', bUseRelativeCoordinates);
903*cdf0e10cSrcweir 					aCurrentSVGPosition = aEdgeStart;
904*cdf0e10cSrcweir 
905*cdf0e10cSrcweir 					for(sal_uInt32 nIndex(0); nIndex < nEdgeCount; nIndex++)
906*cdf0e10cSrcweir 					{
907*cdf0e10cSrcweir 						// prepare access to next point
908*cdf0e10cSrcweir 						const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
909*cdf0e10cSrcweir 						const B2DPoint aEdgeEnd(aPolygon.getB2DPoint(nNextIndex));
910*cdf0e10cSrcweir 
911*cdf0e10cSrcweir 						// handle edge from (aEdgeStart, aEdgeEnd) using indices (nIndex, nNextIndex)
912*cdf0e10cSrcweir 						const bool bEdgeIsBezier(bPolyUsesControlPoints
913*cdf0e10cSrcweir 							&& (aPolygon.isNextControlPointUsed(nIndex) || aPolygon.isPrevControlPointUsed(nNextIndex)));
914*cdf0e10cSrcweir 
915*cdf0e10cSrcweir 						if(bEdgeIsBezier)
916*cdf0e10cSrcweir 						{
917*cdf0e10cSrcweir 							// handle bezier edge
918*cdf0e10cSrcweir 							const B2DPoint aControlEdgeStart(aPolygon.getNextControlPoint(nIndex));
919*cdf0e10cSrcweir 							const B2DPoint aControlEdgeEnd(aPolygon.getPrevControlPoint(nNextIndex));
920*cdf0e10cSrcweir 							bool bIsQuadraticBezier(false);
921*cdf0e10cSrcweir 
922*cdf0e10cSrcweir 							// check continuity at current edge's start point. For SVG, do NOT use an
923*cdf0e10cSrcweir 							// existing continuity since no 'S' or 's' statement should be written. At
924*cdf0e10cSrcweir 							// import, that 'previous' control vector is not available. SVG documentation
925*cdf0e10cSrcweir 							// says for interpretation:
926*cdf0e10cSrcweir 							//
927*cdf0e10cSrcweir 							// "(If there is no previous command or if the previous command was
928*cdf0e10cSrcweir 							// not an C, c, S or s, assume the first control point is coincident
929*cdf0e10cSrcweir 							// with the current point.)"
930*cdf0e10cSrcweir 							//
931*cdf0e10cSrcweir 							// That's what is done from our import, so avoid exporting it as first statement
932*cdf0e10cSrcweir 							// is necessary.
933*cdf0e10cSrcweir 							const bool bSymmetricAtEdgeStart(
934*cdf0e10cSrcweir 								0 != nIndex
935*cdf0e10cSrcweir 								&& CONTINUITY_C2 == aPolygon.getContinuityInPoint(nIndex));
936*cdf0e10cSrcweir 
937*cdf0e10cSrcweir 							if(bDetectQuadraticBeziers)
938*cdf0e10cSrcweir 							{
939*cdf0e10cSrcweir 								// check for quadratic beziers - that's
940*cdf0e10cSrcweir 								// the case if both control points are in
941*cdf0e10cSrcweir 								// the same place when they are prolonged
942*cdf0e10cSrcweir 								// to the common quadratic control point
943*cdf0e10cSrcweir 								//
944*cdf0e10cSrcweir 								// Left: P = (3P1 - P0) / 2
945*cdf0e10cSrcweir 								// Right: P = (3P2 - P3) / 2
946*cdf0e10cSrcweir 								aLeft = B2DPoint((3.0 * aControlEdgeStart - aEdgeStart) / 2.0);
947*cdf0e10cSrcweir 								aRight= B2DPoint((3.0 * aControlEdgeEnd - aEdgeEnd) / 2.0);
948*cdf0e10cSrcweir 								bIsQuadraticBezier = aLeft.equal(aRight);
949*cdf0e10cSrcweir 							}
950*cdf0e10cSrcweir 
951*cdf0e10cSrcweir 							if(bIsQuadraticBezier)
952*cdf0e10cSrcweir 							{
953*cdf0e10cSrcweir 								// approximately equal, export as quadratic bezier
954*cdf0e10cSrcweir 								if(bSymmetricAtEdgeStart)
955*cdf0e10cSrcweir 								{
956*cdf0e10cSrcweir 									const sal_Unicode aCommand(lcl_getCommand('T', 't', bUseRelativeCoordinates));
957*cdf0e10cSrcweir 
958*cdf0e10cSrcweir 									if(aLastSVGCommand != aCommand)
959*cdf0e10cSrcweir 									{
960*cdf0e10cSrcweir                                         aResult.append(aCommand);
961*cdf0e10cSrcweir 										aLastSVGCommand = aCommand;
962*cdf0e10cSrcweir 									}
963*cdf0e10cSrcweir 
964*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
965*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
966*cdf0e10cSrcweir 									aLastSVGCommand = aCommand;
967*cdf0e10cSrcweir 									aCurrentSVGPosition = aEdgeEnd;
968*cdf0e10cSrcweir 								}
969*cdf0e10cSrcweir 								else
970*cdf0e10cSrcweir 								{
971*cdf0e10cSrcweir 									const sal_Unicode aCommand(lcl_getCommand('Q', 'q', bUseRelativeCoordinates));
972*cdf0e10cSrcweir 
973*cdf0e10cSrcweir 									if(aLastSVGCommand != aCommand)
974*cdf0e10cSrcweir 									{
975*cdf0e10cSrcweir                                         aResult.append(aCommand);
976*cdf0e10cSrcweir 										aLastSVGCommand = aCommand;
977*cdf0e10cSrcweir 									}
978*cdf0e10cSrcweir 
979*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aLeft.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
980*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aLeft.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
981*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
982*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
983*cdf0e10cSrcweir 									aLastSVGCommand = aCommand;
984*cdf0e10cSrcweir 									aCurrentSVGPosition = aEdgeEnd;
985*cdf0e10cSrcweir 								}
986*cdf0e10cSrcweir 							}
987*cdf0e10cSrcweir 							else
988*cdf0e10cSrcweir 							{
989*cdf0e10cSrcweir 								// export as cubic bezier
990*cdf0e10cSrcweir 								if(bSymmetricAtEdgeStart)
991*cdf0e10cSrcweir 								{
992*cdf0e10cSrcweir 									const sal_Unicode aCommand(lcl_getCommand('S', 's', bUseRelativeCoordinates));
993*cdf0e10cSrcweir 
994*cdf0e10cSrcweir 									if(aLastSVGCommand != aCommand)
995*cdf0e10cSrcweir 									{
996*cdf0e10cSrcweir                                         aResult.append(aCommand);
997*cdf0e10cSrcweir 										aLastSVGCommand = aCommand;
998*cdf0e10cSrcweir 									}
999*cdf0e10cSrcweir 
1000*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
1001*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
1002*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
1003*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
1004*cdf0e10cSrcweir 									aLastSVGCommand = aCommand;
1005*cdf0e10cSrcweir 									aCurrentSVGPosition = aEdgeEnd;
1006*cdf0e10cSrcweir 								}
1007*cdf0e10cSrcweir 								else
1008*cdf0e10cSrcweir 								{
1009*cdf0e10cSrcweir 									const sal_Unicode aCommand(lcl_getCommand('C', 'c', bUseRelativeCoordinates));
1010*cdf0e10cSrcweir 
1011*cdf0e10cSrcweir 									if(aLastSVGCommand != aCommand)
1012*cdf0e10cSrcweir 									{
1013*cdf0e10cSrcweir                                         aResult.append(aCommand);
1014*cdf0e10cSrcweir 										aLastSVGCommand = aCommand;
1015*cdf0e10cSrcweir 									}
1016*cdf0e10cSrcweir 
1017*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aControlEdgeStart.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
1018*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aControlEdgeStart.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
1019*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
1020*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
1021*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
1022*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
1023*cdf0e10cSrcweir 									aLastSVGCommand = aCommand;
1024*cdf0e10cSrcweir 									aCurrentSVGPosition = aEdgeEnd;
1025*cdf0e10cSrcweir 								}
1026*cdf0e10cSrcweir 							}
1027*cdf0e10cSrcweir 						}
1028*cdf0e10cSrcweir 						else
1029*cdf0e10cSrcweir 						{
1030*cdf0e10cSrcweir 							// straight edge
1031*cdf0e10cSrcweir 							if(0 == nNextIndex)
1032*cdf0e10cSrcweir 							{
1033*cdf0e10cSrcweir 								// it's a closed polygon's last edge and it's not a bezier edge, so there is
1034*cdf0e10cSrcweir 								// no need to write it
1035*cdf0e10cSrcweir 							}
1036*cdf0e10cSrcweir 							else
1037*cdf0e10cSrcweir 							{
1038*cdf0e10cSrcweir 								const bool bXEqual(aEdgeStart.getX() == aEdgeEnd.getX());
1039*cdf0e10cSrcweir 								const bool bYEqual(aEdgeStart.getY() == aEdgeEnd.getY());
1040*cdf0e10cSrcweir 
1041*cdf0e10cSrcweir 								if(bXEqual && bYEqual)
1042*cdf0e10cSrcweir 								{
1043*cdf0e10cSrcweir 									// point is a double point; do not export at all
1044*cdf0e10cSrcweir 								}
1045*cdf0e10cSrcweir 								else if(bXEqual)
1046*cdf0e10cSrcweir 								{
1047*cdf0e10cSrcweir 									// export as vertical line
1048*cdf0e10cSrcweir 									const sal_Unicode aCommand(lcl_getCommand('V', 'v', bUseRelativeCoordinates));
1049*cdf0e10cSrcweir 
1050*cdf0e10cSrcweir 									if(aLastSVGCommand != aCommand)
1051*cdf0e10cSrcweir 									{
1052*cdf0e10cSrcweir                                         aResult.append(aCommand);
1053*cdf0e10cSrcweir 										aLastSVGCommand = aCommand;
1054*cdf0e10cSrcweir 									}
1055*cdf0e10cSrcweir 
1056*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
1057*cdf0e10cSrcweir 									aCurrentSVGPosition = aEdgeEnd;
1058*cdf0e10cSrcweir 								}
1059*cdf0e10cSrcweir 								else if(bYEqual)
1060*cdf0e10cSrcweir 								{
1061*cdf0e10cSrcweir 									// export as horizontal line
1062*cdf0e10cSrcweir 									const sal_Unicode aCommand(lcl_getCommand('H', 'h', bUseRelativeCoordinates));
1063*cdf0e10cSrcweir 
1064*cdf0e10cSrcweir 									if(aLastSVGCommand != aCommand)
1065*cdf0e10cSrcweir 									{
1066*cdf0e10cSrcweir                                         aResult.append(aCommand);
1067*cdf0e10cSrcweir 										aLastSVGCommand = aCommand;
1068*cdf0e10cSrcweir 									}
1069*cdf0e10cSrcweir 
1070*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
1071*cdf0e10cSrcweir 									aCurrentSVGPosition = aEdgeEnd;
1072*cdf0e10cSrcweir 								}
1073*cdf0e10cSrcweir 								else
1074*cdf0e10cSrcweir 								{
1075*cdf0e10cSrcweir 									// export as line
1076*cdf0e10cSrcweir 									const sal_Unicode aCommand(lcl_getCommand('L', 'l', bUseRelativeCoordinates));
1077*cdf0e10cSrcweir 
1078*cdf0e10cSrcweir 									if(aLastSVGCommand != aCommand)
1079*cdf0e10cSrcweir 									{
1080*cdf0e10cSrcweir                                         aResult.append(aCommand);
1081*cdf0e10cSrcweir 										aLastSVGCommand = aCommand;
1082*cdf0e10cSrcweir 									}
1083*cdf0e10cSrcweir 
1084*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
1085*cdf0e10cSrcweir 									lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
1086*cdf0e10cSrcweir 									aCurrentSVGPosition = aEdgeEnd;
1087*cdf0e10cSrcweir 								}
1088*cdf0e10cSrcweir 							}
1089*cdf0e10cSrcweir 						}
1090*cdf0e10cSrcweir 
1091*cdf0e10cSrcweir 						// prepare edge start for next loop step
1092*cdf0e10cSrcweir 						aEdgeStart = aEdgeEnd;
1093*cdf0e10cSrcweir 					}
1094*cdf0e10cSrcweir 
1095*cdf0e10cSrcweir 					// close path if closed poly (Z and z are equivalent here, but looks nicer when case is matched)
1096*cdf0e10cSrcweir 					if(aPolygon.isClosed())
1097*cdf0e10cSrcweir 					{
1098*cdf0e10cSrcweir                         aResult.append(lcl_getCommand('Z', 'z', bUseRelativeCoordinates));
1099*cdf0e10cSrcweir 					}
1100*cdf0e10cSrcweir 				}
1101*cdf0e10cSrcweir             }
1102*cdf0e10cSrcweir 
1103*cdf0e10cSrcweir             return aResult.makeStringAndClear();
1104*cdf0e10cSrcweir         }
1105*cdf0e10cSrcweir     }
1106*cdf0e10cSrcweir }
1107*cdf0e10cSrcweir 
1108*cdf0e10cSrcweir // eof
1109