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