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>
351f882ec4SArmin Le Grand #include <stringconversiontools.hxx>
36cdf0e10cSrcweir 
37cdf0e10cSrcweir namespace basegfx
38cdf0e10cSrcweir {
391f882ec4SArmin Le Grand     namespace tools
401f882ec4SArmin Le Grand     {
operator <(const PointIndex & rComp) const411f882ec4SArmin Le Grand         bool PointIndex::operator<(const PointIndex& rComp) const
42cdf0e10cSrcweir         {
431f882ec4SArmin Le Grand             if(rComp.getPolygonIndex() == getPolygonIndex())
44cdf0e10cSrcweir             {
451f882ec4SArmin Le Grand                 return rComp.getPointIndex() < getPointIndex();
46cdf0e10cSrcweir             }
47cdf0e10cSrcweir 
481f882ec4SArmin Le Grand             return rComp.getPolygonIndex() < getPolygonIndex();
49cdf0e10cSrcweir         }
50cdf0e10cSrcweir 
importFromSvgD(B2DPolyPolygon & o_rPolyPolygon,const::rtl::OUString & rSvgDStatement,bool bHandleRelativeNextPointCompatible,PointIndexSet * pHelpPointIndexSet)511f882ec4SArmin Le Grand         bool importFromSvgD(
521f882ec4SArmin Le Grand             B2DPolyPolygon& o_rPolyPolygon,
531f882ec4SArmin Le Grand             const ::rtl::OUString& rSvgDStatement,
541f882ec4SArmin Le Grand             bool bHandleRelativeNextPointCompatible,
551f882ec4SArmin Le Grand             PointIndexSet* pHelpPointIndexSet)
56cdf0e10cSrcweir         {
57cdf0e10cSrcweir             o_rPolyPolygon.clear();
58cdf0e10cSrcweir             const sal_Int32 nLen(rSvgDStatement.getLength());
59cdf0e10cSrcweir             sal_Int32 nPos(0);
60cdf0e10cSrcweir             double nLastX( 0.0 );
61cdf0e10cSrcweir             double nLastY( 0.0 );
621f882ec4SArmin Le Grand             B2DPolygon aCurrPoly;
63cdf0e10cSrcweir 
641f882ec4SArmin Le Grand             // skip initial whitespace
651f882ec4SArmin Le Grand             ::basegfx::internal::lcl_skipSpaces(nPos, rSvgDStatement, nLen);
66cdf0e10cSrcweir 
67cdf0e10cSrcweir             while(nPos < nLen)
68cdf0e10cSrcweir             {
69cdf0e10cSrcweir                 bool bRelative(false);
70cdf0e10cSrcweir                 const sal_Unicode aCurrChar(rSvgDStatement[nPos]);
71cdf0e10cSrcweir 
721f882ec4SArmin Le Grand                 if(o_rPolyPolygon.count() && !aCurrPoly.count() && !('m' == aCurrChar || 'M' == aCurrChar))
731f882ec4SArmin Le Grand                 {
741f882ec4SArmin Le Grand                     // we have a new sub-polygon starting, but without a 'moveto' command.
751f882ec4SArmin Le Grand                     // this requires to add the current point as start point to the polygon
761f882ec4SArmin Le Grand                     // (see SVG1.1 8.3.3 The "closepath" command)
771f882ec4SArmin Le Grand                     aCurrPoly.append(B2DPoint(nLastX, nLastY));
781f882ec4SArmin Le Grand                 }
791f882ec4SArmin Le Grand 
80cdf0e10cSrcweir                 switch(aCurrChar)
81cdf0e10cSrcweir                 {
82cdf0e10cSrcweir                     case 'z' :
83cdf0e10cSrcweir                     case 'Z' :
84cdf0e10cSrcweir                     {
851f882ec4SArmin Le Grand                         // consume CurrChar and whitespace
86cdf0e10cSrcweir                         nPos++;
871f882ec4SArmin Le Grand                         ::basegfx::internal::lcl_skipSpaces(nPos, rSvgDStatement, nLen);
881f882ec4SArmin Le Grand 
891f882ec4SArmin Le Grand                         // create closed polygon and reset import values
901f882ec4SArmin Le Grand                         if(aCurrPoly.count())
911f882ec4SArmin Le Grand                         {
921f882ec4SArmin Le Grand                             if(!bHandleRelativeNextPointCompatible)
931f882ec4SArmin Le Grand                             {
941f882ec4SArmin Le Grand                                 // SVG defines that "the next subpath starts at the
951f882ec4SArmin Le Grand                                 // same initial point as the current subpath", so set the
961f882ec4SArmin Le Grand                                 // current point if we do not need to be compatible
971f882ec4SArmin Le Grand                                 nLastX = aCurrPoly.getB2DPoint(0).getX();
981f882ec4SArmin Le Grand                                 nLastY = aCurrPoly.getB2DPoint(0).getY();
991f882ec4SArmin Le Grand                             }
1001f882ec4SArmin Le Grand 
1011f882ec4SArmin Le Grand                             aCurrPoly.setClosed(true);
1021f882ec4SArmin Le Grand                             o_rPolyPolygon.append(aCurrPoly);
1031f882ec4SArmin Le Grand                             aCurrPoly.clear();
1041f882ec4SArmin Le Grand                         }
105cdf0e10cSrcweir 
106cdf0e10cSrcweir                         break;
107cdf0e10cSrcweir                     }
1081f882ec4SArmin Le Grand 
109cdf0e10cSrcweir                     case 'm' :
110cdf0e10cSrcweir                     case 'M' :
111cdf0e10cSrcweir                     {
1121f882ec4SArmin Le Grand                         // create non-closed polygon and reset import values
1131f882ec4SArmin Le Grand                         if(aCurrPoly.count())
1141f882ec4SArmin Le Grand                         {
1151f882ec4SArmin Le Grand                             o_rPolyPolygon.append(aCurrPoly);
1161f882ec4SArmin Le Grand                             aCurrPoly.clear();
1171f882ec4SArmin Le Grand                         }
1181f882ec4SArmin Le Grand 
1191f882ec4SArmin Le Grand                         // FALLTHROUGH intended to add coordinate data as 1st point of new polygon
120cdf0e10cSrcweir                     }
121cdf0e10cSrcweir                     case 'l' :
122cdf0e10cSrcweir                     case 'L' :
123cdf0e10cSrcweir                     {
124cdf0e10cSrcweir                         if('m' == aCurrChar || 'l' == aCurrChar)
125cdf0e10cSrcweir                         {
1261f882ec4SArmin Le Grand                             bRelative = true;
127cdf0e10cSrcweir                         }
128cdf0e10cSrcweir 
1291f882ec4SArmin Le Grand                         // consume CurrChar and whitespace
130cdf0e10cSrcweir                         nPos++;
1311f882ec4SArmin Le Grand                         ::basegfx::internal::lcl_skipSpaces(nPos, rSvgDStatement, nLen);
132cdf0e10cSrcweir 
1331f882ec4SArmin Le Grand                         while(nPos < nLen && ::basegfx::internal::lcl_isOnNumberChar(rSvgDStatement, nPos))
134cdf0e10cSrcweir                         {
135cdf0e10cSrcweir                             double nX, nY;
136cdf0e10cSrcweir 
1371f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
1381f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
139cdf0e10cSrcweir 
140cdf0e10cSrcweir                             if(bRelative)
141cdf0e10cSrcweir                             {
142cdf0e10cSrcweir                                 nX += nLastX;
143cdf0e10cSrcweir                                 nY += nLastY;
144cdf0e10cSrcweir                             }
145cdf0e10cSrcweir 
146cdf0e10cSrcweir                             // set last position
147cdf0e10cSrcweir                             nLastX = nX;
148cdf0e10cSrcweir                             nLastY = nY;
1491f882ec4SArmin Le Grand 
150cdf0e10cSrcweir                             // add point
151cdf0e10cSrcweir                             aCurrPoly.append(B2DPoint(nX, nY));
152cdf0e10cSrcweir                         }
153cdf0e10cSrcweir                         break;
154cdf0e10cSrcweir                     }
155cdf0e10cSrcweir 
156cdf0e10cSrcweir                     case 'h' :
157cdf0e10cSrcweir                     {
158cdf0e10cSrcweir                         bRelative = true;
159cdf0e10cSrcweir                         // FALLTHROUGH intended
160cdf0e10cSrcweir                     }
161cdf0e10cSrcweir                     case 'H' :
162cdf0e10cSrcweir                     {
163cdf0e10cSrcweir                         nPos++;
1641f882ec4SArmin Le Grand                         ::basegfx::internal::lcl_skipSpaces(nPos, rSvgDStatement, nLen);
165cdf0e10cSrcweir 
1661f882ec4SArmin Le Grand                         while(nPos < nLen && ::basegfx::internal::lcl_isOnNumberChar(rSvgDStatement, nPos))
167cdf0e10cSrcweir                         {
168cdf0e10cSrcweir                             double nX, nY(nLastY);
169cdf0e10cSrcweir 
1701f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
171cdf0e10cSrcweir 
172cdf0e10cSrcweir                             if(bRelative)
1731f882ec4SArmin Le Grand                             {
174cdf0e10cSrcweir                                 nX += nLastX;
1751f882ec4SArmin Le Grand                             }
176cdf0e10cSrcweir 
177cdf0e10cSrcweir                             // set last position
178cdf0e10cSrcweir                             nLastX = nX;
1791f882ec4SArmin Le Grand 
180cdf0e10cSrcweir                             // add point
181cdf0e10cSrcweir                             aCurrPoly.append(B2DPoint(nX, nY));
182cdf0e10cSrcweir                         }
183cdf0e10cSrcweir                         break;
184cdf0e10cSrcweir                     }
1851f882ec4SArmin Le Grand 
186cdf0e10cSrcweir                     case 'v' :
187cdf0e10cSrcweir                     {
188cdf0e10cSrcweir                         bRelative = true;
189cdf0e10cSrcweir                         // FALLTHROUGH intended
190cdf0e10cSrcweir                     }
191cdf0e10cSrcweir                     case 'V' :
192cdf0e10cSrcweir                     {
193cdf0e10cSrcweir                         nPos++;
1941f882ec4SArmin Le Grand                         ::basegfx::internal::lcl_skipSpaces(nPos, rSvgDStatement, nLen);
195cdf0e10cSrcweir 
1961f882ec4SArmin Le Grand                         while(nPos < nLen && ::basegfx::internal::lcl_isOnNumberChar(rSvgDStatement, nPos))
197cdf0e10cSrcweir                         {
198cdf0e10cSrcweir                             double nX(nLastX), nY;
199cdf0e10cSrcweir 
2001f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
201cdf0e10cSrcweir 
202cdf0e10cSrcweir                             if(bRelative)
2031f882ec4SArmin Le Grand                             {
204cdf0e10cSrcweir                                 nY += nLastY;
2051f882ec4SArmin Le Grand                             }
206cdf0e10cSrcweir 
207cdf0e10cSrcweir                             // set last position
208cdf0e10cSrcweir                             nLastY = nY;
2091f882ec4SArmin Le Grand 
210cdf0e10cSrcweir                             // add point
211cdf0e10cSrcweir                             aCurrPoly.append(B2DPoint(nX, nY));
212cdf0e10cSrcweir                         }
213cdf0e10cSrcweir                         break;
214cdf0e10cSrcweir                     }
2151f882ec4SArmin Le Grand 
216cdf0e10cSrcweir                     case 's' :
217cdf0e10cSrcweir                     {
218cdf0e10cSrcweir                         bRelative = true;
219cdf0e10cSrcweir                         // FALLTHROUGH intended
220cdf0e10cSrcweir                     }
221cdf0e10cSrcweir                     case 'S' :
222cdf0e10cSrcweir                     {
223cdf0e10cSrcweir                         nPos++;
2241f882ec4SArmin Le Grand                         ::basegfx::internal::lcl_skipSpaces(nPos, rSvgDStatement, nLen);
225cdf0e10cSrcweir 
2261f882ec4SArmin Le Grand                         while(nPos < nLen && ::basegfx::internal::lcl_isOnNumberChar(rSvgDStatement, nPos))
227cdf0e10cSrcweir                         {
228cdf0e10cSrcweir                             double nX, nY;
229cdf0e10cSrcweir                             double nX2, nY2;
230cdf0e10cSrcweir 
2311f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nX2, nPos, rSvgDStatement, nLen)) return false;
2321f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nY2, nPos, rSvgDStatement, nLen)) return false;
2331f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
2341f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
235cdf0e10cSrcweir 
236cdf0e10cSrcweir                             if(bRelative)
237cdf0e10cSrcweir                             {
238cdf0e10cSrcweir                                 nX2 += nLastX;
239cdf0e10cSrcweir                                 nY2 += nLastY;
240cdf0e10cSrcweir                                 nX += nLastX;
241cdf0e10cSrcweir                                 nY += nLastY;
242cdf0e10cSrcweir                             }
243cdf0e10cSrcweir 
244*07a3d7f1SPedro Giffuni                             // ensure existence of start point
2451f882ec4SArmin Le Grand                             if(!aCurrPoly.count())
2461f882ec4SArmin Le Grand                             {
247cdf0e10cSrcweir                                 aCurrPoly.append(B2DPoint(nLastX, nLastY));
2481f882ec4SArmin Le Grand                             }
249cdf0e10cSrcweir 
2501f882ec4SArmin Le Grand                             // get first control point. It's the reflection of the PrevControlPoint
2511f882ec4SArmin Le Grand                             // of the last point. If not existent, use current point (see SVG)
2521f882ec4SArmin Le Grand                             B2DPoint aPrevControl(B2DPoint(nLastX, nLastY));
2531f882ec4SArmin Le Grand                             const sal_uInt32 nIndex(aCurrPoly.count() - 1);
254cdf0e10cSrcweir 
2551f882ec4SArmin Le Grand                             if(aCurrPoly.areControlPointsUsed() && aCurrPoly.isPrevControlPointUsed(nIndex))
2561f882ec4SArmin Le Grand                             {
2571f882ec4SArmin Le Grand                                 const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(nIndex));
2581f882ec4SArmin Le Grand                                 const B2DPoint aPrevControlPoint(aCurrPoly.getPrevControlPoint(nIndex));
259cdf0e10cSrcweir 
2601f882ec4SArmin Le Grand                                 // use mirrored previous control point
2611f882ec4SArmin Le Grand                                 aPrevControl.setX((2.0 * aPrevPoint.getX()) - aPrevControlPoint.getX());
2621f882ec4SArmin Le Grand                                 aPrevControl.setY((2.0 * aPrevPoint.getY()) - aPrevControlPoint.getY());
2631f882ec4SArmin Le Grand                             }
264cdf0e10cSrcweir 
2651f882ec4SArmin Le Grand                             // append curved edge
2661f882ec4SArmin Le Grand                             aCurrPoly.appendBezierSegment(aPrevControl, B2DPoint(nX2, nY2), B2DPoint(nX, nY));
267cdf0e10cSrcweir 
268cdf0e10cSrcweir                             // set last position
269cdf0e10cSrcweir                             nLastX = nX;
270cdf0e10cSrcweir                             nLastY = nY;
271cdf0e10cSrcweir                         }
272cdf0e10cSrcweir                         break;
273cdf0e10cSrcweir                     }
2741f882ec4SArmin Le Grand 
275cdf0e10cSrcweir                     case 'c' :
276cdf0e10cSrcweir                     {
277cdf0e10cSrcweir                         bRelative = true;
278cdf0e10cSrcweir                         // FALLTHROUGH intended
279cdf0e10cSrcweir                     }
280cdf0e10cSrcweir                     case 'C' :
281cdf0e10cSrcweir                     {
282cdf0e10cSrcweir                         nPos++;
2831f882ec4SArmin Le Grand                         ::basegfx::internal::lcl_skipSpaces(nPos, rSvgDStatement, nLen);
284cdf0e10cSrcweir 
2851f882ec4SArmin Le Grand                         while(nPos < nLen && ::basegfx::internal::lcl_isOnNumberChar(rSvgDStatement, nPos))
286cdf0e10cSrcweir                         {
287cdf0e10cSrcweir                             double nX, nY;
288cdf0e10cSrcweir                             double nX1, nY1;
289cdf0e10cSrcweir                             double nX2, nY2;
290cdf0e10cSrcweir 
2911f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nX1, nPos, rSvgDStatement, nLen)) return false;
2921f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nY1, nPos, rSvgDStatement, nLen)) return false;
2931f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nX2, nPos, rSvgDStatement, nLen)) return false;
2941f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nY2, nPos, rSvgDStatement, nLen)) return false;
2951f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
2961f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
297cdf0e10cSrcweir 
298cdf0e10cSrcweir                             if(bRelative)
299cdf0e10cSrcweir                             {
300cdf0e10cSrcweir                                 nX1 += nLastX;
301cdf0e10cSrcweir                                 nY1 += nLastY;
302cdf0e10cSrcweir                                 nX2 += nLastX;
303cdf0e10cSrcweir                                 nY2 += nLastY;
304cdf0e10cSrcweir                                 nX += nLastX;
305cdf0e10cSrcweir                                 nY += nLastY;
306cdf0e10cSrcweir                             }
307cdf0e10cSrcweir 
308*07a3d7f1SPedro Giffuni                             // ensure existence of start point
3091f882ec4SArmin Le Grand                             if(!aCurrPoly.count())
3101f882ec4SArmin Le Grand                             {
311cdf0e10cSrcweir                                 aCurrPoly.append(B2DPoint(nLastX, nLastY));
3121f882ec4SArmin Le Grand                             }
313cdf0e10cSrcweir 
3141f882ec4SArmin Le Grand                             // append curved edge
3151f882ec4SArmin Le Grand                             aCurrPoly.appendBezierSegment(B2DPoint(nX1, nY1), B2DPoint(nX2, nY2), B2DPoint(nX, nY));
316cdf0e10cSrcweir 
317cdf0e10cSrcweir                             // set last position
318cdf0e10cSrcweir                             nLastX = nX;
319cdf0e10cSrcweir                             nLastY = nY;
320cdf0e10cSrcweir                         }
321cdf0e10cSrcweir                         break;
322cdf0e10cSrcweir                     }
3231f882ec4SArmin Le Grand 
324cdf0e10cSrcweir                     // #100617# quadratic beziers are imported as cubic ones
325cdf0e10cSrcweir                     case 'q' :
326cdf0e10cSrcweir                     {
327cdf0e10cSrcweir                         bRelative = true;
328cdf0e10cSrcweir                         // FALLTHROUGH intended
329cdf0e10cSrcweir                     }
330cdf0e10cSrcweir                     case 'Q' :
331cdf0e10cSrcweir                     {
332cdf0e10cSrcweir                         nPos++;
3331f882ec4SArmin Le Grand                         ::basegfx::internal::lcl_skipSpaces(nPos, rSvgDStatement, nLen);
334cdf0e10cSrcweir 
3351f882ec4SArmin Le Grand                         while(nPos < nLen && ::basegfx::internal::lcl_isOnNumberChar(rSvgDStatement, nPos))
336cdf0e10cSrcweir                         {
337cdf0e10cSrcweir                             double nX, nY;
338cdf0e10cSrcweir                             double nX1, nY1;
339cdf0e10cSrcweir 
3401f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nX1, nPos, rSvgDStatement, nLen)) return false;
3411f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nY1, nPos, rSvgDStatement, nLen)) return false;
3421f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
3431f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
344cdf0e10cSrcweir 
345cdf0e10cSrcweir                             if(bRelative)
346cdf0e10cSrcweir                             {
347cdf0e10cSrcweir                                 nX1 += nLastX;
348cdf0e10cSrcweir                                 nY1 += nLastY;
349cdf0e10cSrcweir                                 nX += nLastX;
350cdf0e10cSrcweir                                 nY += nLastY;
351cdf0e10cSrcweir                             }
352cdf0e10cSrcweir 
353cdf0e10cSrcweir                             // calculate the cubic bezier coefficients from the quadratic ones
354cdf0e10cSrcweir                             const double nX1Prime((nX1 * 2.0 + nLastX) / 3.0);
355cdf0e10cSrcweir                             const double nY1Prime((nY1 * 2.0 + nLastY) / 3.0);
356cdf0e10cSrcweir                             const double nX2Prime((nX1 * 2.0 + nX) / 3.0);
357cdf0e10cSrcweir                             const double nY2Prime((nY1 * 2.0 + nY) / 3.0);
358cdf0e10cSrcweir 
359*07a3d7f1SPedro Giffuni                             // ensure existence of start point
3601f882ec4SArmin Le Grand                             if(!aCurrPoly.count())
3611f882ec4SArmin Le Grand                             {
362cdf0e10cSrcweir                                 aCurrPoly.append(B2DPoint(nLastX, nLastY));
3631f882ec4SArmin Le Grand                             }
364cdf0e10cSrcweir 
3651f882ec4SArmin Le Grand                             // append curved edge
3661f882ec4SArmin Le Grand                             aCurrPoly.appendBezierSegment(B2DPoint(nX1Prime, nY1Prime), B2DPoint(nX2Prime, nY2Prime), B2DPoint(nX, nY));
367cdf0e10cSrcweir 
368cdf0e10cSrcweir                             // set last position
369cdf0e10cSrcweir                             nLastX = nX;
370cdf0e10cSrcweir                             nLastY = nY;
371cdf0e10cSrcweir                         }
372cdf0e10cSrcweir                         break;
373cdf0e10cSrcweir                     }
3741f882ec4SArmin Le Grand 
375cdf0e10cSrcweir                     // #100617# relative quadratic beziers are imported as cubic
376cdf0e10cSrcweir                     case 't' :
377cdf0e10cSrcweir                     {
378cdf0e10cSrcweir                         bRelative = true;
379cdf0e10cSrcweir                         // FALLTHROUGH intended
380cdf0e10cSrcweir                     }
381cdf0e10cSrcweir                     case 'T' :
382cdf0e10cSrcweir                     {
383cdf0e10cSrcweir                         nPos++;
3841f882ec4SArmin Le Grand                         ::basegfx::internal::lcl_skipSpaces(nPos, rSvgDStatement, nLen);
385cdf0e10cSrcweir 
3861f882ec4SArmin Le Grand                         while(nPos < nLen && ::basegfx::internal::lcl_isOnNumberChar(rSvgDStatement, nPos))
387cdf0e10cSrcweir                         {
388cdf0e10cSrcweir                             double nX, nY;
389cdf0e10cSrcweir 
3901f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
3911f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
392cdf0e10cSrcweir 
393cdf0e10cSrcweir                             if(bRelative)
394cdf0e10cSrcweir                             {
395cdf0e10cSrcweir                                 nX += nLastX;
396cdf0e10cSrcweir                                 nY += nLastY;
397cdf0e10cSrcweir                             }
398cdf0e10cSrcweir 
399*07a3d7f1SPedro Giffuni                             // ensure existence of start point
4001f882ec4SArmin Le Grand                             if(!aCurrPoly.count())
4011f882ec4SArmin Le Grand                             {
402cdf0e10cSrcweir                                 aCurrPoly.append(B2DPoint(nLastX, nLastY));
4031f882ec4SArmin Le Grand                             }
4041f882ec4SArmin Le Grand 
4051f882ec4SArmin Le Grand                             // get first control point. It's the reflection of the PrevControlPoint
4061f882ec4SArmin Le Grand                             // of the last point. If not existent, use current point (see SVG)
4071f882ec4SArmin Le Grand                             B2DPoint aPrevControl(B2DPoint(nLastX, nLastY));
4081f882ec4SArmin Le Grand                             const sal_uInt32 nIndex(aCurrPoly.count() - 1);
4091f882ec4SArmin Le Grand                             const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(nIndex));
4101f882ec4SArmin Le Grand 
4111f882ec4SArmin Le Grand                             if(aCurrPoly.areControlPointsUsed() && aCurrPoly.isPrevControlPointUsed(nIndex))
4121f882ec4SArmin Le Grand                             {
4131f882ec4SArmin Le Grand                                 const B2DPoint aPrevControlPoint(aCurrPoly.getPrevControlPoint(nIndex));
4141f882ec4SArmin Le Grand 
4151f882ec4SArmin Le Grand                                 // use mirrored previous control point
4161f882ec4SArmin Le Grand                                 aPrevControl.setX((2.0 * aPrevPoint.getX()) - aPrevControlPoint.getX());
4171f882ec4SArmin Le Grand                                 aPrevControl.setY((2.0 * aPrevPoint.getY()) - aPrevControlPoint.getY());
4181f882ec4SArmin Le Grand                             }
4191f882ec4SArmin Le Grand 
4201f882ec4SArmin Le Grand                             if(!aPrevControl.equal(aPrevPoint))
4211f882ec4SArmin Le Grand                             {
4221f882ec4SArmin Le Grand                                 // there is a prev control point, and we have the already mirrored one
4231f882ec4SArmin Le Grand                                 // in aPrevControl. We also need the quadratic control point for this
4241f882ec4SArmin Le Grand                                 // new quadratic segment to calculate the 2nd cubic control point
4251f882ec4SArmin Le Grand                                 const B2DPoint aQuadControlPoint(
4261f882ec4SArmin Le Grand                                     ((3.0 * aPrevControl.getX()) - aPrevPoint.getX()) / 2.0,
4271f882ec4SArmin Le Grand                                     ((3.0 * aPrevControl.getY()) - aPrevPoint.getY()) / 2.0);
4281f882ec4SArmin Le Grand 
4291f882ec4SArmin Le Grand                                 // calculate the cubic bezier coefficients from the quadratic ones.
4301f882ec4SArmin Le Grand                                 const double nX2Prime((aQuadControlPoint.getX() * 2.0 + nX) / 3.0);
4311f882ec4SArmin Le Grand                                 const double nY2Prime((aQuadControlPoint.getY() * 2.0 + nY) / 3.0);
4321f882ec4SArmin Le Grand 
4331f882ec4SArmin Le Grand                                 // append curved edge, use mirrored cubic control point directly
4341f882ec4SArmin Le Grand                                 aCurrPoly.appendBezierSegment(aPrevControl, B2DPoint(nX2Prime, nY2Prime), B2DPoint(nX, nY));
4351f882ec4SArmin Le Grand                             }
4361f882ec4SArmin Le Grand                             else
4371f882ec4SArmin Le Grand                             {
4381f882ec4SArmin Le Grand                                 // when no previous control, SVG says to use current point -> straight line.
4391f882ec4SArmin Le Grand                                 // Just add end point
4401f882ec4SArmin Le Grand                                 aCurrPoly.append(B2DPoint(nX, nY));
4411f882ec4SArmin Le Grand                             }
442cdf0e10cSrcweir 
443cdf0e10cSrcweir                             // set last position
444cdf0e10cSrcweir                             nLastX = nX;
445cdf0e10cSrcweir                             nLastY = nY;
446cdf0e10cSrcweir                         }
447cdf0e10cSrcweir                         break;
448cdf0e10cSrcweir                     }
449cdf0e10cSrcweir 
450cdf0e10cSrcweir                     case 'a' :
451cdf0e10cSrcweir                     {
452cdf0e10cSrcweir                         bRelative = true;
453cdf0e10cSrcweir                         // FALLTHROUGH intended
454cdf0e10cSrcweir                     }
455cdf0e10cSrcweir                     case 'A' :
456cdf0e10cSrcweir                     {
457cdf0e10cSrcweir                         nPos++;
4581f882ec4SArmin Le Grand                         ::basegfx::internal::lcl_skipSpaces(nPos, rSvgDStatement, nLen);
459cdf0e10cSrcweir 
4601f882ec4SArmin Le Grand                         while(nPos < nLen && ::basegfx::internal::lcl_isOnNumberChar(rSvgDStatement, nPos))
461cdf0e10cSrcweir                         {
462cdf0e10cSrcweir                             double nX, nY;
463cdf0e10cSrcweir                             double fRX, fRY, fPhi;
464cdf0e10cSrcweir                             sal_Int32 bLargeArcFlag, bSweepFlag;
465cdf0e10cSrcweir 
4661f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(fRX, nPos, rSvgDStatement, nLen)) return false;
4671f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(fRY, nPos, rSvgDStatement, nLen)) return false;
4681f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(fPhi, nPos, rSvgDStatement, nLen)) return false;
4691f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importNumberAndSpaces(bLargeArcFlag, nPos, rSvgDStatement, nLen)) return false;
4701f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importNumberAndSpaces(bSweepFlag, nPos, rSvgDStatement, nLen)) return false;
4711f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
4721f882ec4SArmin Le Grand                             if(!::basegfx::internal::lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
473cdf0e10cSrcweir 
474cdf0e10cSrcweir                             if(bRelative)
475cdf0e10cSrcweir                             {
476cdf0e10cSrcweir                                 nX += nLastX;
477cdf0e10cSrcweir                                 nY += nLastY;
478cdf0e10cSrcweir                             }
479cdf0e10cSrcweir 
4801f882ec4SArmin Le Grand                             const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(aCurrPoly.count() - 1));
481cdf0e10cSrcweir 
482cdf0e10cSrcweir                             if( nX == nLastX && nY == nLastY )
483cdf0e10cSrcweir                                 continue; // start==end -> skip according to SVG spec
484cdf0e10cSrcweir 
485cdf0e10cSrcweir                             if( fRX == 0.0 || fRY == 0.0 )
486cdf0e10cSrcweir                             {
487cdf0e10cSrcweir                                 // straight line segment according to SVG spec
488cdf0e10cSrcweir                                 aCurrPoly.append(B2DPoint(nX, nY));
489cdf0e10cSrcweir                             }
490cdf0e10cSrcweir                             else
491cdf0e10cSrcweir                             {
492cdf0e10cSrcweir                                 // normalize according to SVG spec
493cdf0e10cSrcweir                                 fRX=fabs(fRX); fRY=fabs(fRY);
494cdf0e10cSrcweir 
495cdf0e10cSrcweir                                 // from the SVG spec, appendix F.6.4
496cdf0e10cSrcweir 
497cdf0e10cSrcweir                                 // |x1'|   |cos phi   sin phi|  |(x1 - x2)/2|
498cdf0e10cSrcweir                                 // |y1'| = |-sin phi  cos phi|  |(y1 - y2)/2|
499cdf0e10cSrcweir                                 const B2DPoint p1(nLastX, nLastY);
500cdf0e10cSrcweir                                 const B2DPoint p2(nX, nY);
501cdf0e10cSrcweir                                 B2DHomMatrix aTransform(basegfx::tools::createRotateB2DHomMatrix(-fPhi*M_PI/180));
502cdf0e10cSrcweir 
503cdf0e10cSrcweir                                 const B2DPoint p1_prime( aTransform * B2DPoint(((p1-p2)/2.0)) );
504cdf0e10cSrcweir 
505cdf0e10cSrcweir                                 //           ______________________________________       rx y1'
506cdf0e10cSrcweir                                 // |cx'|  + /  rx^2 ry^2 - rx^2 y1'^2 - ry^2 x1^2           ry
507cdf0e10cSrcweir                                 // |cy'| =-/       rx^2y1'^2 + ry^2 x1'^2               - ry x1'
508cdf0e10cSrcweir                                 //                                                          rx
509cdf0e10cSrcweir                                 // chose + if f_A != f_S
510cdf0e10cSrcweir                                 // chose - if f_A  = f_S
511cdf0e10cSrcweir                                 B2DPoint aCenter_prime;
512cdf0e10cSrcweir                                 const double fRadicant(
513cdf0e10cSrcweir                                     (fRX*fRX*fRY*fRY - fRX*fRX*p1_prime.getY()*p1_prime.getY() - fRY*fRY*p1_prime.getX()*p1_prime.getX())/
514cdf0e10cSrcweir                                     (fRX*fRX*p1_prime.getY()*p1_prime.getY() + fRY*fRY*p1_prime.getX()*p1_prime.getX()));
515cdf0e10cSrcweir                                 if( fRadicant < 0.0 )
516cdf0e10cSrcweir                                 {
517cdf0e10cSrcweir                                     // no solution - according to SVG
518cdf0e10cSrcweir                                     // spec, scale up ellipse
519cdf0e10cSrcweir                                     // uniformly such that it passes
520cdf0e10cSrcweir                                     // through end points (denominator
521cdf0e10cSrcweir                                     // of radicant solved for fRY,
522cdf0e10cSrcweir                                     // with s=fRX/fRY)
523cdf0e10cSrcweir                                     const double fRatio(fRX/fRY);
524cdf0e10cSrcweir                                     const double fRadicant2(
525cdf0e10cSrcweir                                         p1_prime.getY()*p1_prime.getY() +
526cdf0e10cSrcweir                                         p1_prime.getX()*p1_prime.getX()/(fRatio*fRatio));
527cdf0e10cSrcweir                                     if( fRadicant2 < 0.0 )
528cdf0e10cSrcweir                                     {
529cdf0e10cSrcweir                                         // only trivial solution, one
530cdf0e10cSrcweir                                         // of the axes 0 -> straight
531cdf0e10cSrcweir                                         // line segment according to
532cdf0e10cSrcweir                                         // SVG spec
533cdf0e10cSrcweir                                         aCurrPoly.append(B2DPoint(nX, nY));
534cdf0e10cSrcweir                                         continue;
535cdf0e10cSrcweir                                     }
536cdf0e10cSrcweir 
537cdf0e10cSrcweir                                     fRY=sqrt(fRadicant2);
538cdf0e10cSrcweir                                     fRX=fRatio*fRY;
539cdf0e10cSrcweir 
540cdf0e10cSrcweir                                     // keep center_prime forced to (0,0)
541cdf0e10cSrcweir                                 }
542cdf0e10cSrcweir                                 else
543cdf0e10cSrcweir                                 {
544cdf0e10cSrcweir                                     const double fFactor(
545cdf0e10cSrcweir                                         (bLargeArcFlag==bSweepFlag ? -1.0 : 1.0) *
546cdf0e10cSrcweir                                         sqrt(fRadicant));
547cdf0e10cSrcweir 
548cdf0e10cSrcweir                                     // actually calculate center_prime
549cdf0e10cSrcweir                                     aCenter_prime = B2DPoint(
550cdf0e10cSrcweir                                         fFactor*fRX*p1_prime.getY()/fRY,
551cdf0e10cSrcweir                                         -fFactor*fRY*p1_prime.getX()/fRX);
552cdf0e10cSrcweir                                 }
553cdf0e10cSrcweir 
554cdf0e10cSrcweir                                 //              +           u - v
555cdf0e10cSrcweir                                 // angle(u,v) =  arccos( ------------ )     (take the sign of (ux vy - uy vx))
556cdf0e10cSrcweir                                 //              -        ||u|| ||v||
557cdf0e10cSrcweir 
558cdf0e10cSrcweir                                 //                  1    | (x1' - cx')/rx |
559cdf0e10cSrcweir                                 // theta1 = angle((   ), |                | )
560cdf0e10cSrcweir                                 //                  0    | (y1' - cy')/ry |
561cdf0e10cSrcweir                                 const B2DPoint aRadii(fRX,fRY);
562cdf0e10cSrcweir                                 double fTheta1(
563cdf0e10cSrcweir                                     B2DVector(1.0,0.0).angle(
564cdf0e10cSrcweir                                         (p1_prime-aCenter_prime)/aRadii));
565cdf0e10cSrcweir 
566cdf0e10cSrcweir                                 //                 |1|    |  (-x1' - cx')/rx |
567cdf0e10cSrcweir                                 // theta2 = angle( | | ,  |                  | )
568cdf0e10cSrcweir                                 //                 |0|    |  (-y1' - cy')/ry |
569cdf0e10cSrcweir                                 double fTheta2(
570cdf0e10cSrcweir                                     B2DVector(1.0,0.0).angle(
571cdf0e10cSrcweir                                         (-p1_prime-aCenter_prime)/aRadii));
572cdf0e10cSrcweir 
573cdf0e10cSrcweir                                 // map both angles to [0,2pi)
574cdf0e10cSrcweir                                 fTheta1 = fmod(2*M_PI+fTheta1,2*M_PI);
575cdf0e10cSrcweir                                 fTheta2 = fmod(2*M_PI+fTheta2,2*M_PI);
576cdf0e10cSrcweir 
577cdf0e10cSrcweir                                 // make sure the large arc is taken
578cdf0e10cSrcweir                                 // (since
579cdf0e10cSrcweir                                 // createPolygonFromEllipseSegment()
580cdf0e10cSrcweir                                 // normalizes to e.g. cw arc)
581ddde725dSArmin Le Grand 
582ddde725dSArmin Le Grand                                 // ALG: In my opinion flipping the segment only
583ddde725dSArmin Le Grand                                 // depends on the sweep flag. At least, this gives
584ddde725dSArmin Le Grand                                 // correct results forthe SVG example (see SVG doc 8.3.8 ff)
585ddde725dSArmin Le Grand                                 //
586ddde725dSArmin Le Grand                                 //const bool bFlipSegment( (bLargeArcFlag!=0) ==
587ddde725dSArmin Le Grand                                 //    (fmod(fTheta2+2*M_PI-fTheta1,
588ddde725dSArmin Le Grand                                 //          2*M_PI)<M_PI) );
589ddde725dSArmin Le Grand                                 const bool bFlipSegment(!bSweepFlag);
590ddde725dSArmin Le Grand 
591cdf0e10cSrcweir                                 if( bFlipSegment )
592cdf0e10cSrcweir                                     std::swap(fTheta1,fTheta2);
593cdf0e10cSrcweir 
594cdf0e10cSrcweir                                 // finally, create bezier polygon from this
595cdf0e10cSrcweir                                 B2DPolygon aSegment(
596cdf0e10cSrcweir                                     tools::createPolygonFromUnitEllipseSegment(
597cdf0e10cSrcweir                                         fTheta1, fTheta2 ));
598cdf0e10cSrcweir 
599cdf0e10cSrcweir                                 // transform ellipse by rotation & move to final center
600cdf0e10cSrcweir                                 aTransform = basegfx::tools::createScaleB2DHomMatrix(fRX, fRY);
601cdf0e10cSrcweir                                 aTransform.translate(aCenter_prime.getX(),
602cdf0e10cSrcweir                                                      aCenter_prime.getY());
603cdf0e10cSrcweir                                 aTransform.rotate(fPhi*M_PI/180);
604cdf0e10cSrcweir                                 const B2DPoint aOffset((p1+p2)/2.0);
605cdf0e10cSrcweir                                 aTransform.translate(aOffset.getX(),
606cdf0e10cSrcweir                                                      aOffset.getY());
607cdf0e10cSrcweir                                 aSegment.transform(aTransform);
608cdf0e10cSrcweir 
609cdf0e10cSrcweir                                 // createPolygonFromEllipseSegment()
610cdf0e10cSrcweir                                 // always creates arcs that are
611cdf0e10cSrcweir                                 // positively oriented - flip polygon
612cdf0e10cSrcweir                                 // if we swapped angles above
613cdf0e10cSrcweir                                 if( bFlipSegment )
614cdf0e10cSrcweir                                     aSegment.flip();
6151f882ec4SArmin Le Grand 
6161f882ec4SArmin Le Grand                                 // remember PointIndex of evtl. added pure helper points
6171f882ec4SArmin Le Grand                                 sal_uInt32 nPointIndex(aCurrPoly.count() + 1);
618cdf0e10cSrcweir                                 aCurrPoly.append(aSegment);
6191f882ec4SArmin Le Grand 
6201f882ec4SArmin Le Grand                                 // if asked for, mark pure helper points by adding them to the index list of
6211f882ec4SArmin Le Grand                                 // helper points
6221f882ec4SArmin Le Grand                                 if(pHelpPointIndexSet && aCurrPoly.count() > 1)
6231f882ec4SArmin Le Grand                                 {
6241f882ec4SArmin Le Grand                                     const sal_uInt32 nPolyIndex(o_rPolyPolygon.count());
6251f882ec4SArmin Le Grand 
6261f882ec4SArmin Le Grand                                     for(;nPointIndex + 1 < aCurrPoly.count(); nPointIndex++)
6271f882ec4SArmin Le Grand                                     {
6281f882ec4SArmin Le Grand                                         pHelpPointIndexSet->insert(PointIndex(nPolyIndex, nPointIndex));
6291f882ec4SArmin Le Grand                                     }
6301f882ec4SArmin Le Grand                                 }
631cdf0e10cSrcweir                             }
632cdf0e10cSrcweir 
633cdf0e10cSrcweir                             // set last position
634cdf0e10cSrcweir                             nLastX = nX;
635cdf0e10cSrcweir                             nLastY = nY;
636cdf0e10cSrcweir                         }
637cdf0e10cSrcweir                         break;
638cdf0e10cSrcweir                     }
639cdf0e10cSrcweir 
640cdf0e10cSrcweir                     default:
641cdf0e10cSrcweir                     {
642cdf0e10cSrcweir                         OSL_ENSURE(false, "importFromSvgD(): skipping tags in svg:d element (unknown)!");
643cdf0e10cSrcweir                         OSL_TRACE("importFromSvgD(): skipping tags in svg:d element (unknown: \"%c\")!", aCurrChar);
644cdf0e10cSrcweir                         ++nPos;
645cdf0e10cSrcweir                         break;
646cdf0e10cSrcweir                     }
647cdf0e10cSrcweir                 }
648cdf0e10cSrcweir             }
649cdf0e10cSrcweir 
6501f882ec4SArmin Le Grand             // if there is polygon data, create non-closed polygon
651cdf0e10cSrcweir             if(aCurrPoly.count())
652cdf0e10cSrcweir             {
6531f882ec4SArmin Le Grand                 o_rPolyPolygon.append(aCurrPoly);
654cdf0e10cSrcweir             }
655cdf0e10cSrcweir 
656cdf0e10cSrcweir             return true;
657cdf0e10cSrcweir         }
658cdf0e10cSrcweir 
importFromSvgPoints(B2DPolygon & o_rPoly,const::rtl::OUString & rSvgPointsAttribute)659cdf0e10cSrcweir         bool importFromSvgPoints( B2DPolygon&            o_rPoly,
660cdf0e10cSrcweir                                   const ::rtl::OUString& rSvgPointsAttribute )
661cdf0e10cSrcweir         {
662cdf0e10cSrcweir             o_rPoly.clear();
663cdf0e10cSrcweir             const sal_Int32 nLen(rSvgPointsAttribute.getLength());
664cdf0e10cSrcweir             sal_Int32 nPos(0);
665cdf0e10cSrcweir             double nX, nY;
666cdf0e10cSrcweir 
667cdf0e10cSrcweir             // skip initial whitespace
6681f882ec4SArmin Le Grand             ::basegfx::internal::lcl_skipSpaces(nPos, rSvgPointsAttribute, nLen);
669cdf0e10cSrcweir 
670cdf0e10cSrcweir             while(nPos < nLen)
671cdf0e10cSrcweir             {
6721f882ec4SArmin Le Grand                 if(!::basegfx::internal::lcl_importDoubleAndSpaces(nX, nPos, rSvgPointsAttribute, nLen)) return false;
6731f882ec4SArmin Le Grand                 if(!::basegfx::internal::lcl_importDoubleAndSpaces(nY, nPos, rSvgPointsAttribute, nLen)) return false;
674cdf0e10cSrcweir 
675cdf0e10cSrcweir                 // add point
676cdf0e10cSrcweir                 o_rPoly.append(B2DPoint(nX, nY));
677cdf0e10cSrcweir 
678cdf0e10cSrcweir                 // skip to next number, or finish
6791f882ec4SArmin Le Grand                 ::basegfx::internal::lcl_skipSpaces(nPos, rSvgPointsAttribute, nLen);
680cdf0e10cSrcweir             }
681cdf0e10cSrcweir 
682cdf0e10cSrcweir             return true;
683cdf0e10cSrcweir         }
684cdf0e10cSrcweir 
exportToSvgPoints(const B2DPolygon & rPoly)6851f882ec4SArmin Le Grand         ::rtl::OUString exportToSvgPoints( const B2DPolygon& rPoly )
6861f882ec4SArmin Le Grand         {
6871f882ec4SArmin Le Grand             OSL_ENSURE(!rPoly.areControlPointsUsed(), "exportToSvgPoints: Only non-bezier polygons allowed (!)");
6881f882ec4SArmin Le Grand             const sal_uInt32 nPointCount(rPoly.count());
6891f882ec4SArmin Le Grand             ::rtl::OUStringBuffer aResult;
6901f882ec4SArmin Le Grand 
6911f882ec4SArmin Le Grand             for(sal_uInt32 a(0); a < nPointCount; a++)
6921f882ec4SArmin Le Grand             {
6931f882ec4SArmin Le Grand                 const basegfx::B2DPoint aPoint(rPoly.getB2DPoint(a));
6941f882ec4SArmin Le Grand 
6951f882ec4SArmin Le Grand                 if(a)
6961f882ec4SArmin Le Grand                 {
6971f882ec4SArmin Le Grand                     aResult.append(sal_Unicode(' '));
6981f882ec4SArmin Le Grand                 }
6991f882ec4SArmin Le Grand 
7001f882ec4SArmin Le Grand                 ::basegfx::internal::lcl_putNumberChar(aResult, aPoint.getX());
7011f882ec4SArmin Le Grand                 aResult.append(sal_Unicode(','));
7021f882ec4SArmin Le Grand                 ::basegfx::internal::lcl_putNumberChar(aResult, aPoint.getY());
7031f882ec4SArmin Le Grand             }
7041f882ec4SArmin Le Grand 
7051f882ec4SArmin Le Grand             return aResult.makeStringAndClear();
7061f882ec4SArmin Le Grand         }
7071f882ec4SArmin Le Grand 
exportToSvgD(const B2DPolyPolygon & rPolyPolygon,bool bUseRelativeCoordinates,bool bDetectQuadraticBeziers,bool bHandleRelativeNextPointCompatible)708cdf0e10cSrcweir         ::rtl::OUString exportToSvgD(
7091f882ec4SArmin Le Grand             const B2DPolyPolygon& rPolyPolygon,
7101f882ec4SArmin Le Grand             bool bUseRelativeCoordinates,
7111f882ec4SArmin Le Grand             bool bDetectQuadraticBeziers,
7121f882ec4SArmin Le Grand             bool bHandleRelativeNextPointCompatible)
713cdf0e10cSrcweir         {
714cdf0e10cSrcweir             const sal_uInt32 nCount(rPolyPolygon.count());
715cdf0e10cSrcweir             ::rtl::OUStringBuffer aResult;
716cdf0e10cSrcweir             B2DPoint aCurrentSVGPosition(0.0, 0.0); // SVG assumes (0,0) as the initial current point
717cdf0e10cSrcweir 
718cdf0e10cSrcweir             for(sal_uInt32 i(0); i < nCount; i++)
719cdf0e10cSrcweir             {
720cdf0e10cSrcweir                 const B2DPolygon aPolygon(rPolyPolygon.getB2DPolygon(i));
721cdf0e10cSrcweir                 const sal_uInt32 nPointCount(aPolygon.count());
722cdf0e10cSrcweir 
7231f882ec4SArmin Le Grand                 if(nPointCount)
7241f882ec4SArmin Le Grand                 {
7251f882ec4SArmin Le Grand                     const bool bPolyUsesControlPoints(aPolygon.areControlPointsUsed());
7261f882ec4SArmin Le Grand                     const sal_uInt32 nEdgeCount(aPolygon.isClosed() ? nPointCount : nPointCount - 1);
7271f882ec4SArmin Le Grand                     sal_Unicode aLastSVGCommand(' '); // last SVG command char
7281f882ec4SArmin Le Grand                     B2DPoint aLeft, aRight; // for quadratic bezier test
7291f882ec4SArmin Le Grand 
7301f882ec4SArmin Le Grand                     // handle polygon start point
7311f882ec4SArmin Le Grand                     B2DPoint aEdgeStart(aPolygon.getB2DPoint(0));
7321f882ec4SArmin Le Grand                     bool bUseRelativeCoordinatesForFirstPoint(bUseRelativeCoordinates);
7331f882ec4SArmin Le Grand 
7341f882ec4SArmin Le Grand                     if(bHandleRelativeNextPointCompatible)
7351f882ec4SArmin Le Grand                     {
7361f882ec4SArmin Le Grand                         // To get around the error that the start point for the next polygon is the
7371f882ec4SArmin Le Grand                         // start point of the current one (and not the last as it was handled up to now)
7381f882ec4SArmin Le Grand                         // do force to write an absolute 'M' command as start for the next polygon
7391f882ec4SArmin Le Grand                         bUseRelativeCoordinatesForFirstPoint = false;
7401f882ec4SArmin Le Grand                     }
7411f882ec4SArmin Le Grand 
7421f882ec4SArmin Le Grand                     // Write 'moveto' and the 1st coordinates, set aLastSVGCommand to 'lineto'
7431f882ec4SArmin Le Grand                     aResult.append(::basegfx::internal::lcl_getCommand('M', 'm', bUseRelativeCoordinatesForFirstPoint));
7441f882ec4SArmin Le Grand                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeStart.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinatesForFirstPoint);
7451f882ec4SArmin Le Grand                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeStart.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinatesForFirstPoint);
7461f882ec4SArmin Le Grand                     aLastSVGCommand =  ::basegfx::internal::lcl_getCommand('L', 'l', bUseRelativeCoordinatesForFirstPoint);
7471f882ec4SArmin Le Grand                     aCurrentSVGPosition = aEdgeStart;
7481f882ec4SArmin Le Grand 
7491f882ec4SArmin Le Grand                     for(sal_uInt32 nIndex(0); nIndex < nEdgeCount; nIndex++)
7501f882ec4SArmin Le Grand                     {
7511f882ec4SArmin Le Grand                         // prepare access to next point
7521f882ec4SArmin Le Grand                         const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
7531f882ec4SArmin Le Grand                         const B2DPoint aEdgeEnd(aPolygon.getB2DPoint(nNextIndex));
7541f882ec4SArmin Le Grand 
7551f882ec4SArmin Le Grand                         // handle edge from (aEdgeStart, aEdgeEnd) using indices (nIndex, nNextIndex)
7561f882ec4SArmin Le Grand                         const bool bEdgeIsBezier(bPolyUsesControlPoints
7571f882ec4SArmin Le Grand                             && (aPolygon.isNextControlPointUsed(nIndex) || aPolygon.isPrevControlPointUsed(nNextIndex)));
7581f882ec4SArmin Le Grand 
7591f882ec4SArmin Le Grand                         if(bEdgeIsBezier)
7601f882ec4SArmin Le Grand                         {
7611f882ec4SArmin Le Grand                             // handle bezier edge
7621f882ec4SArmin Le Grand                             const B2DPoint aControlEdgeStart(aPolygon.getNextControlPoint(nIndex));
7631f882ec4SArmin Le Grand                             const B2DPoint aControlEdgeEnd(aPolygon.getPrevControlPoint(nNextIndex));
7641f882ec4SArmin Le Grand                             bool bIsQuadraticBezier(false);
7651f882ec4SArmin Le Grand 
7661f882ec4SArmin Le Grand                             // check continuity at current edge's start point. For SVG, do NOT use an
7671f882ec4SArmin Le Grand                             // existing continuity since no 'S' or 's' statement should be written. At
7681f882ec4SArmin Le Grand                             // import, that 'previous' control vector is not available. SVG documentation
7691f882ec4SArmin Le Grand                             // says for interpretation:
7701f882ec4SArmin Le Grand                             //
7711f882ec4SArmin Le Grand                             // "(If there is no previous command or if the previous command was
7721f882ec4SArmin Le Grand                             // not an C, c, S or s, assume the first control point is coincident
7731f882ec4SArmin Le Grand                             // with the current point.)"
7741f882ec4SArmin Le Grand                             //
7751f882ec4SArmin Le Grand                             // That's what is done from our import, so avoid exporting it as first statement
7761f882ec4SArmin Le Grand                             // is necessary.
7771f882ec4SArmin Le Grand                             const bool bSymmetricAtEdgeStart(
7781f882ec4SArmin Le Grand                                 0 != nIndex
7791f882ec4SArmin Le Grand                                 && CONTINUITY_C2 == aPolygon.getContinuityInPoint(nIndex));
7801f882ec4SArmin Le Grand 
7811f882ec4SArmin Le Grand                             if(bDetectQuadraticBeziers)
7821f882ec4SArmin Le Grand                             {
7831f882ec4SArmin Le Grand                                 // check for quadratic beziers - that's
7841f882ec4SArmin Le Grand                                 // the case if both control points are in
7851f882ec4SArmin Le Grand                                 // the same place when they are prolonged
7861f882ec4SArmin Le Grand                                 // to the common quadratic control point
7871f882ec4SArmin Le Grand                                 //
7881f882ec4SArmin Le Grand                                 // Left: P = (3P1 - P0) / 2
7891f882ec4SArmin Le Grand                                 // Right: P = (3P2 - P3) / 2
7901f882ec4SArmin Le Grand                                 aLeft = B2DPoint((3.0 * aControlEdgeStart - aEdgeStart) / 2.0);
7911f882ec4SArmin Le Grand                                 aRight= B2DPoint((3.0 * aControlEdgeEnd - aEdgeEnd) / 2.0);
7921f882ec4SArmin Le Grand                                 bIsQuadraticBezier = aLeft.equal(aRight);
7931f882ec4SArmin Le Grand                             }
7941f882ec4SArmin Le Grand 
7951f882ec4SArmin Le Grand                             if(bIsQuadraticBezier)
7961f882ec4SArmin Le Grand                             {
7971f882ec4SArmin Le Grand                                 // approximately equal, export as quadratic bezier
7981f882ec4SArmin Le Grand                                 if(bSymmetricAtEdgeStart)
7991f882ec4SArmin Le Grand                                 {
8001f882ec4SArmin Le Grand                                     const sal_Unicode aCommand(::basegfx::internal::lcl_getCommand('T', 't', bUseRelativeCoordinates));
8011f882ec4SArmin Le Grand 
8021f882ec4SArmin Le Grand                                     if(aLastSVGCommand != aCommand)
8031f882ec4SArmin Le Grand                                     {
804cdf0e10cSrcweir                                         aResult.append(aCommand);
8051f882ec4SArmin Le Grand                                         aLastSVGCommand = aCommand;
8061f882ec4SArmin Le Grand                                     }
807cdf0e10cSrcweir 
8081f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
8091f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
8101f882ec4SArmin Le Grand                                     aLastSVGCommand = aCommand;
8111f882ec4SArmin Le Grand                                     aCurrentSVGPosition = aEdgeEnd;
8121f882ec4SArmin Le Grand                                 }
8131f882ec4SArmin Le Grand                                 else
8141f882ec4SArmin Le Grand                                 {
8151f882ec4SArmin Le Grand                                     const sal_Unicode aCommand(::basegfx::internal::lcl_getCommand('Q', 'q', bUseRelativeCoordinates));
8161f882ec4SArmin Le Grand 
8171f882ec4SArmin Le Grand                                     if(aLastSVGCommand != aCommand)
8181f882ec4SArmin Le Grand                                     {
819cdf0e10cSrcweir                                         aResult.append(aCommand);
8201f882ec4SArmin Le Grand                                         aLastSVGCommand = aCommand;
8211f882ec4SArmin Le Grand                                     }
822cdf0e10cSrcweir 
8231f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aLeft.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
8241f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aLeft.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
8251f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
8261f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
8271f882ec4SArmin Le Grand                                     aLastSVGCommand = aCommand;
8281f882ec4SArmin Le Grand                                     aCurrentSVGPosition = aEdgeEnd;
8291f882ec4SArmin Le Grand                                 }
8301f882ec4SArmin Le Grand                             }
8311f882ec4SArmin Le Grand                             else
8321f882ec4SArmin Le Grand                             {
8331f882ec4SArmin Le Grand                                 // export as cubic bezier
8341f882ec4SArmin Le Grand                                 if(bSymmetricAtEdgeStart)
8351f882ec4SArmin Le Grand                                 {
8361f882ec4SArmin Le Grand                                     const sal_Unicode aCommand(::basegfx::internal::lcl_getCommand('S', 's', bUseRelativeCoordinates));
8371f882ec4SArmin Le Grand 
8381f882ec4SArmin Le Grand                                     if(aLastSVGCommand != aCommand)
8391f882ec4SArmin Le Grand                                     {
840cdf0e10cSrcweir                                         aResult.append(aCommand);
8411f882ec4SArmin Le Grand                                         aLastSVGCommand = aCommand;
8421f882ec4SArmin Le Grand                                     }
843cdf0e10cSrcweir 
8441f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
8451f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
8461f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
8471f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
8481f882ec4SArmin Le Grand                                     aLastSVGCommand = aCommand;
8491f882ec4SArmin Le Grand                                     aCurrentSVGPosition = aEdgeEnd;
8501f882ec4SArmin Le Grand                                 }
8511f882ec4SArmin Le Grand                                 else
8521f882ec4SArmin Le Grand                                 {
8531f882ec4SArmin Le Grand                                     const sal_Unicode aCommand(::basegfx::internal::lcl_getCommand('C', 'c', bUseRelativeCoordinates));
8541f882ec4SArmin Le Grand 
8551f882ec4SArmin Le Grand                                     if(aLastSVGCommand != aCommand)
8561f882ec4SArmin Le Grand                                     {
857cdf0e10cSrcweir                                         aResult.append(aCommand);
8581f882ec4SArmin Le Grand                                         aLastSVGCommand = aCommand;
8591f882ec4SArmin Le Grand                                     }
860cdf0e10cSrcweir 
8611f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aControlEdgeStart.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
8621f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aControlEdgeStart.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
8631f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
8641f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
8651f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
8661f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
8671f882ec4SArmin Le Grand                                     aLastSVGCommand = aCommand;
8681f882ec4SArmin Le Grand                                     aCurrentSVGPosition = aEdgeEnd;
8691f882ec4SArmin Le Grand                                 }
8701f882ec4SArmin Le Grand                             }
8711f882ec4SArmin Le Grand                         }
8721f882ec4SArmin Le Grand                         else
8731f882ec4SArmin Le Grand                         {
8741f882ec4SArmin Le Grand                             // straight edge
8751f882ec4SArmin Le Grand                             if(0 == nNextIndex)
8761f882ec4SArmin Le Grand                             {
8771f882ec4SArmin Le Grand                                 // it's a closed polygon's last edge and it's not a bezier edge, so there is
8781f882ec4SArmin Le Grand                                 // no need to write it
8791f882ec4SArmin Le Grand                             }
8801f882ec4SArmin Le Grand                             else
8811f882ec4SArmin Le Grand                             {
8821f882ec4SArmin Le Grand                                 const bool bXEqual(aEdgeStart.getX() == aEdgeEnd.getX());
8831f882ec4SArmin Le Grand                                 const bool bYEqual(aEdgeStart.getY() == aEdgeEnd.getY());
8841f882ec4SArmin Le Grand 
8851f882ec4SArmin Le Grand                                 if(bXEqual && bYEqual)
8861f882ec4SArmin Le Grand                                 {
8871f882ec4SArmin Le Grand                                     // point is a double point; do not export at all
8881f882ec4SArmin Le Grand                                 }
8891f882ec4SArmin Le Grand                                 else if(bXEqual)
8901f882ec4SArmin Le Grand                                 {
8911f882ec4SArmin Le Grand                                     // export as vertical line
8921f882ec4SArmin Le Grand                                     const sal_Unicode aCommand(::basegfx::internal::lcl_getCommand('V', 'v', bUseRelativeCoordinates));
8931f882ec4SArmin Le Grand 
8941f882ec4SArmin Le Grand                                     if(aLastSVGCommand != aCommand)
8951f882ec4SArmin Le Grand                                     {
896cdf0e10cSrcweir                                         aResult.append(aCommand);
8971f882ec4SArmin Le Grand                                         aLastSVGCommand = aCommand;
8981f882ec4SArmin Le Grand                                     }
8991f882ec4SArmin Le Grand 
9001f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
9011f882ec4SArmin Le Grand                                     aCurrentSVGPosition = aEdgeEnd;
9021f882ec4SArmin Le Grand                                 }
9031f882ec4SArmin Le Grand                                 else if(bYEqual)
9041f882ec4SArmin Le Grand                                 {
9051f882ec4SArmin Le Grand                                     // export as horizontal line
9061f882ec4SArmin Le Grand                                     const sal_Unicode aCommand(::basegfx::internal::lcl_getCommand('H', 'h', bUseRelativeCoordinates));
9071f882ec4SArmin Le Grand 
9081f882ec4SArmin Le Grand                                     if(aLastSVGCommand != aCommand)
9091f882ec4SArmin Le Grand                                     {
910cdf0e10cSrcweir                                         aResult.append(aCommand);
9111f882ec4SArmin Le Grand                                         aLastSVGCommand = aCommand;
9121f882ec4SArmin Le Grand                                     }
9131f882ec4SArmin Le Grand 
9141f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
9151f882ec4SArmin Le Grand                                     aCurrentSVGPosition = aEdgeEnd;
9161f882ec4SArmin Le Grand                                 }
9171f882ec4SArmin Le Grand                                 else
9181f882ec4SArmin Le Grand                                 {
9191f882ec4SArmin Le Grand                                     // export as line
9201f882ec4SArmin Le Grand                                     const sal_Unicode aCommand(::basegfx::internal::lcl_getCommand('L', 'l', bUseRelativeCoordinates));
9211f882ec4SArmin Le Grand 
9221f882ec4SArmin Le Grand                                     if(aLastSVGCommand != aCommand)
9231f882ec4SArmin Le Grand                                     {
924cdf0e10cSrcweir                                         aResult.append(aCommand);
9251f882ec4SArmin Le Grand                                         aLastSVGCommand = aCommand;
9261f882ec4SArmin Le Grand                                     }
9271f882ec4SArmin Le Grand 
9281f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
9291f882ec4SArmin Le Grand                                     ::basegfx::internal::lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
9301f882ec4SArmin Le Grand                                     aCurrentSVGPosition = aEdgeEnd;
9311f882ec4SArmin Le Grand                                 }
9321f882ec4SArmin Le Grand                             }
9331f882ec4SArmin Le Grand                         }
9341f882ec4SArmin Le Grand 
9351f882ec4SArmin Le Grand                         // prepare edge start for next loop step
9361f882ec4SArmin Le Grand                         aEdgeStart = aEdgeEnd;
9371f882ec4SArmin Le Grand                     }
9381f882ec4SArmin Le Grand 
9391f882ec4SArmin Le Grand                     // close path if closed poly (Z and z are equivalent here, but looks nicer when case is matched)
9401f882ec4SArmin Le Grand                     if(aPolygon.isClosed())
9411f882ec4SArmin Le Grand                     {
9421f882ec4SArmin Le Grand                         aResult.append(::basegfx::internal::lcl_getCommand('Z', 'z', bUseRelativeCoordinates));
9431f882ec4SArmin Le Grand                     }
9441f882ec4SArmin Le Grand 
9451f882ec4SArmin Le Grand                     if(!bHandleRelativeNextPointCompatible)
9461f882ec4SArmin Le Grand                     {
9471f882ec4SArmin Le Grand                         // SVG defines that "the next subpath starts at the same initial point as the current subpath",
9481f882ec4SArmin Le Grand                         // so set aCurrentSVGPosition to the 1st point of the current, now ended and written path
9491f882ec4SArmin Le Grand                         aCurrentSVGPosition = aPolygon.getB2DPoint(0);
9501f882ec4SArmin Le Grand                     }
9511f882ec4SArmin Le Grand                 }
952cdf0e10cSrcweir             }
953cdf0e10cSrcweir 
954cdf0e10cSrcweir             return aResult.makeStringAndClear();
955cdf0e10cSrcweir         }
956cdf0e10cSrcweir     }
957cdf0e10cSrcweir }
958cdf0e10cSrcweir 
959cdf0e10cSrcweir // eof
960