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