GCC Code Coverage Report


Directory: ./
File: src/2geom/svg-path-parser.rl
Date: 2024-03-18 17:01:34
Exec Total Coverage
Lines: 152 186 81.7%
Functions: 18 23 78.3%
Branches: 93 220 42.3%

Line Branch Exec Source
1 /**
2 * \file
3 * \brief parse SVG path specifications
4 *
5 * Copyright 2007 MenTaLguY <mental@rydia.net>
6 * Copyright 2007 Aaron Spike <aaron@ekips.org>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it either under the terms of the GNU Lesser General Public
10 * License version 2.1 as published by the Free Software Foundation
11 * (the "LGPL") or, at your option, under the terms of the Mozilla
12 * Public License Version 1.1 (the "MPL"). If you do not alter this
13 * notice, a recipient may use your version of this file under either
14 * the MPL or the LGPL.
15 *
16 * You should have received a copy of the LGPL along with this library
17 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 * You should have received a copy of the MPL along with this library
20 * in the file COPYING-MPL-1.1
21 *
22 * The contents of this file are subject to the Mozilla Public License
23 * Version 1.1 (the "License"); you may not use this file except in
24 * compliance with the License. You may obtain a copy of the License at
25 * http://www.mozilla.org/MPL/
26 *
27 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
28 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
29 * the specific language governing rights and limitations.
30 *
31 */
32
33 #include <cstdio>
34 #include <cmath>
35 #include <vector>
36 #include <glib.h>
37
38 #include <2geom/point.h>
39 #include <2geom/svg-path-parser.h>
40 #include <2geom/angle.h>
41
42 namespace Geom {
43
44 %%{
45 machine svg_path;
46 write data noerror;
47 }%%
48
49 836 SVGPathParser::SVGPathParser(PathSink &sink)
50 836 : _absolute(false)
51 836 , _sink(sink)
52 836 , _z_snap_threshold(0)
53 836 , _curve(NULL)
54 {
55
1/2
✓ Branch 1 taken 836 times.
✗ Branch 2 not taken.
836 reset();
56 836 }
57
58 836 SVGPathParser::~SVGPathParser()
59 {
60
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 836 times.
836 delete _curve;
61 836 }
62
63 1672 void SVGPathParser::reset() {
64 1672 _absolute = false;
65 1672 _current = _initial = Point(0, 0);
66 1672 _quad_tangent = _cubic_tangent = Point(0, 0);
67 1672 _params.clear();
68
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1672 times.
1672 delete _curve;
69 1672 _curve = NULL;
70
71 %%{
72 write init;
73 }%%
74 1672 }
75
76 836 void SVGPathParser::parse(char const *str, int len)
77 {
78
1/2
✓ Branch 0 taken 836 times.
✗ Branch 1 not taken.
836 if (len < 0) {
79 836 len = std::strlen(str);
80 }
81 836 _parse(str, str + len, true);
82 836 }
83
84 void SVGPathParser::parse(std::string const &s)
85 {
86 _parse(s.c_str(), s.c_str() + s.size(), true);
87 }
88
89 void SVGPathParser::feed(char const *str, int len)
90 {
91 if (len < 0) {
92 len = std::strlen(str);
93 }
94 _parse(str, str + len, false);
95 }
96
97 void SVGPathParser::feed(std::string const &s)
98 {
99 _parse(s.c_str(), s.c_str() + s.size(), false);
100 }
101
102 void SVGPathParser::finish()
103 {
104 char const *empty = "";
105 _parse(empty, empty, true);
106 }
107
108 146005 void SVGPathParser::_push(Coord value)
109 {
110 146005 _params.push_back(value);
111 146005 }
112
113 146005 Coord SVGPathParser::_pop()
114 {
115 146005 Coord value = _params.back();
116 146005 _params.pop_back();
117 146005 return value;
118 }
119
120 376 bool SVGPathParser::_pop_flag()
121 {
122 376 return _pop() != 0.0;
123 }
124
125 145065 Coord SVGPathParser::_pop_coord(Dim2 axis)
126 {
127
2/2
✓ Branch 0 taken 143781 times.
✓ Branch 1 taken 1284 times.
145065 if (_absolute) {
128 143781 return _pop();
129 } else {
130 1284 return _pop() + _current[axis];
131 }
132 }
133
134 72333 Point SVGPathParser::_pop_point()
135 {
136 72333 Coord y = _pop_coord(Y);
137 72333 Coord x = _pop_coord(X);
138 72333 return Point(x, y);
139 }
140
141 1406 void SVGPathParser::_moveTo(Point const &p)
142 {
143 1406 _pushCurve(NULL); // flush
144 1406 _sink.moveTo(p);
145 1406 _quad_tangent = _cubic_tangent = _current = _initial = p;
146 1406 }
147
148 1652 void SVGPathParser::_lineTo(Point const &p)
149 {
150
2/6
✓ Branch 2 taken 1652 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1652 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
1652 _pushCurve(new LineSegment(_current, p));
151 1652 _quad_tangent = _cubic_tangent = _current = p;
152 1652 }
153
154 23136 void SVGPathParser::_curveTo(Point const &c0, Point const &c1, Point const &p)
155 {
156
2/6
✓ Branch 2 taken 23136 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 23136 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
23136 _pushCurve(new CubicBezier(_current, c0, c1, p));
157 23136 _quad_tangent = _current = p;
158 23136 _cubic_tangent = p + ( p - c1 );
159 23136 }
160
161 61 void SVGPathParser::_quadTo(Point const &c, Point const &p)
162 {
163
2/6
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 61 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
61 _pushCurve(new QuadraticBezier(_current, c, p));
164 61 _cubic_tangent = _current = p;
165 61 _quad_tangent = p + ( p - c );
166 61 }
167
168 188 void SVGPathParser::_arcTo(Coord rx, Coord ry, Coord angle,
169 bool large_arc, bool sweep, Point const &p)
170 {
171
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 157 times.
188 if (_current == p) {
172 31 return; // ignore invalid (ambiguous) arc segments where start and end point are the same (per SVG spec)
173 }
174
175
2/6
✓ Branch 2 taken 157 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 157 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
157 _pushCurve(new EllipticalArc(_current, fabs(rx), fabs(ry), angle, large_arc, sweep, p));
176 157 _quad_tangent = _cubic_tangent = _current = p;
177 }
178
179 282 void SVGPathParser::_closePath()
180 {
181
7/8
✓ Branch 0 taken 281 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 192 times.
✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 192 times.
✓ Branch 6 taken 58 times.
✓ Branch 7 taken 224 times.
371 if (_curve && (!_absolute || !_moveto_was_absolute) &&
182
2/2
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 31 times.
89 are_near(_initial, _current, _z_snap_threshold))
183 {
184 58 _curve->setFinal(_initial);
185 }
186
187 282 _pushCurve(NULL); // flush
188 282 _sink.closePath();
189 282 _quad_tangent = _cubic_tangent = _current = _initial;
190 282 }
191
192 27530 void SVGPathParser::_pushCurve(Curve *c)
193 {
194
2/2
✓ Branch 0 taken 25006 times.
✓ Branch 1 taken 2524 times.
27530 if (_curve) {
195 25006 _sink.feed(*_curve, false);
196
1/2
✓ Branch 0 taken 25006 times.
✗ Branch 1 not taken.
25006 delete _curve;
197 }
198 27530 _curve = c;
199 27530 }
200
201 836 void SVGPathParser::_parse(char const *str, char const *strend, bool finish)
202 {
203 836 char const *p = str;
204 836 char const *pe = strend;
205
1/2
✓ Branch 0 taken 836 times.
✗ Branch 1 not taken.
836 char const *eof = finish ? pe : NULL;
206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 836 times.
836 char const *start = NULL;
207
208 %%{
209 action start_number {
210 145629 start = p;
211 }
212 145629
213 145070 action push_number {
214
2/4
✓ Branch 0 taken 145070 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 559 times.
✗ Branch 3 not taken.
145629 if (start) {
215
2/4
✓ Branch 3 taken 145070 times.
✗ Branch 4 not taken.
✓ Branch 9 taken 559 times.
✗ Branch 10 not taken.
436887 std::string buf(start, p);
216
4/8
✓ Branch 2 taken 145070 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 145070 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 559 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 559 times.
✗ Branch 13 not taken.
145629 _push(g_ascii_strtod(buf.c_str(), NULL));
217 145629 start = NULL;
218 145629 } else {
219 std::string buf(str, p);
220 _push(g_ascii_strtod((_number_part + buf).c_str(), NULL));
221 _number_part.clear();
222 }
223 }
224 145629
225 273 action push_true {
226 243 _push(1.0);
227 }
228 243
229 133 action push_false {
230 133 _push(0.0);
231 }
232 133
233 3601 action mode_abs {
234 3601 _absolute = true;
235 }
236 3601
237 265 action mode_rel {
238 265 _absolute = false;
239 }
240 265
241 1376 action moveto {
242 1406 _moveto_was_absolute = _absolute;
243
4/8
✓ Branch 2 taken 1376 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1376 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 30 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 30 times.
✗ Branch 14 not taken.
1406 _moveTo(_pop_point());
244 }
245 1406
246 1253 action lineto {
247
4/8
✓ Branch 2 taken 1134 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1134 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 119 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 119 times.
✗ Branch 14 not taken.
1253 _lineTo(_pop_point());
248 }
249 1253
250 192 action horizontal_lineto {
251
4/8
✓ Branch 3 taken 181 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 181 times.
✗ Branch 8 not taken.
✓ Branch 13 taken 11 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 11 times.
✗ Branch 18 not taken.
192 _lineTo(Point(_pop_coord(X), _current[Y]));
252 }
253 192
254 207 action vertical_lineto {
255
4/8
✓ Branch 2 taken 200 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 200 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 7 times.
✗ Branch 13 not taken.
✓ Branch 17 taken 7 times.
✗ Branch 18 not taken.
207 _lineTo(Point(_current[X], _pop_coord(Y)));
256 }
257 207
258 23092 action curveto {
259
2/4
✓ Branch 2 taken 22762 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 330 times.
✗ Branch 7 not taken.
23092 Point p = _pop_point();
260
2/4
✓ Branch 2 taken 22762 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 330 times.
✗ Branch 7 not taken.
23092 Point c1 = _pop_point();
261
2/4
✓ Branch 2 taken 22762 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 330 times.
✗ Branch 7 not taken.
23092 Point c0 = _pop_point();
262
2/4
✓ Branch 1 taken 22762 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 330 times.
✗ Branch 5 not taken.
23092 _curveTo(c0, c1, p);
263 }
264
265 44 action smooth_curveto {
266
2/4
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
44 Point p = _pop_point();
267
2/4
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
44 Point c1 = _pop_point();
268
2/4
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 31 times.
✗ Branch 5 not taken.
44 _curveTo(_cubic_tangent, c1, p);
269 }
270
271 61 action quadratic_bezier_curveto {
272
2/4
✓ Branch 2 taken 40 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 21 times.
✗ Branch 7 not taken.
61 Point p = _pop_point();
273
2/4
✓ Branch 2 taken 40 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 21 times.
✗ Branch 7 not taken.
61 Point c = _pop_point();
274
2/4
✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
61 _quadTo(c, p);
275 }
276
277 action smooth_quadratic_bezier_curveto {
278 Point p = _pop_point();
279 _quadTo(_quad_tangent, p);
280 }
281
282 188 action elliptical_arc {
283
2/4
✓ Branch 2 taken 178 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 10 times.
✗ Branch 7 not taken.
188 Point point = _pop_point();
284
2/4
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
188 bool sweep = _pop_flag();
285
2/4
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
188 bool large_arc = _pop_flag();
286
2/4
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
188 double angle = rad_from_deg(_pop());
287
2/4
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
188 double ry = _pop();
288
2/4
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
188 double rx = _pop();
289
290
2/4
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
188 _arcTo(rx, ry, angle, large_arc, sweep, point);
291 }
292
293 282 action closepath {
294 282 _closePath();
295 }
296 282
297 wsp = (' ' | 9 | 10 | 13);
298 sign = ('+' | '-');
299 digit_sequence = digit+;
300 exponent = ('e' | 'E') sign? digit_sequence;
301 fractional_constant =
302 digit_sequence? '.' digit_sequence
303 | digit_sequence '.';
304 floating_point_constant =
305 fractional_constant exponent?
306 | digit_sequence exponent;
307 integer_constant = digit_sequence;
308 comma = ',';
309 comma_wsp = (wsp+ comma? wsp*) | (comma wsp*);
310
311 flag = ('0' %push_false | '1' %push_true);
312
313 number =
314 ( sign? integer_constant
315 | sign? floating_point_constant )
316 >start_number %push_number;
317
318 nonnegative_number =
319 ( integer_constant
320 | floating_point_constant)
321 >start_number %push_number;
322
323 coordinate = number $(number,1) %(number,0);
324 coordinate_pair = (coordinate $(coordinate_pair_a,1) %(coordinate_pair_a,0) comma_wsp? coordinate $(coordinate_pair_b,1) %(coordinate_pair_b,0)) $(coordinate_pair,1) %(coordinate_pair,0);
325 elliptical_arc_argument =
326 (number $(elliptical_arg_a,1) %(elliptical_arg_a,0) comma_wsp?
327 number $(elliptical_arg_b,1) %(elliptical_arg_b,0) comma_wsp?
328 number comma_wsp
329 flag comma_wsp? flag comma_wsp?
330 coordinate_pair)
331 %elliptical_arc;
332 elliptical_arc_argument_sequence =
333 elliptical_arc_argument $1 %0
334 (comma_wsp? elliptical_arc_argument $1 %0)*;
335 elliptical_arc =
336 ('A' %mode_abs| 'a' %mode_rel) wsp*
337 elliptical_arc_argument_sequence;
338
339 smooth_quadratic_bezier_curveto_argument =
340 coordinate_pair %smooth_quadratic_bezier_curveto;
341 smooth_quadratic_bezier_curveto_argument_sequence =
342 smooth_quadratic_bezier_curveto_argument $1 %0
343 (comma_wsp?
344 smooth_quadratic_bezier_curveto_argument $1 %0)*;
345 smooth_quadratic_bezier_curveto =
346 ('T' %mode_abs| 't' %mode_rel) wsp*
347 smooth_quadratic_bezier_curveto_argument_sequence;
348
349 quadratic_bezier_curveto_argument =
350 (coordinate_pair $1 %0 comma_wsp? coordinate_pair)
351 %quadratic_bezier_curveto;
352 quadratic_bezier_curveto_argument_sequence =
353 quadratic_bezier_curveto_argument $1 %0
354 (comma_wsp? quadratic_bezier_curveto_argument $1 %0)*;
355 quadratic_bezier_curveto =
356 ('Q' %mode_abs| 'q' %mode_rel) wsp*
357 quadratic_bezier_curveto_argument_sequence;
358
359 smooth_curveto_argument =
360 (coordinate_pair $1 %0 comma_wsp? coordinate_pair)
361 %smooth_curveto;
362 smooth_curveto_argument_sequence =
363 smooth_curveto_argument $1 %0
364 (comma_wsp? smooth_curveto_argument $1 %0)*;
365 smooth_curveto =
366 ('S' %mode_abs| 's' %mode_rel)
367 wsp* smooth_curveto_argument_sequence;
368
369 curveto_argument =
370 (coordinate_pair $1 %0 comma_wsp?
371 coordinate_pair $1 %0 comma_wsp?
372 coordinate_pair)
373 %curveto;
374 curveto_argument_sequence =
375 curveto_argument $1 %0
376 (comma_wsp? curveto_argument $1 %0)*;
377 curveto =
378 ('C' %mode_abs| 'c' %mode_rel)
379 wsp* curveto_argument_sequence;
380
381 vertical_lineto_argument = coordinate %vertical_lineto;
382 vertical_lineto_argument_sequence =
383 vertical_lineto_argument $(vertical_lineto_argument_a,1) %(vertical_lineto_argument_a,0)
384 (comma_wsp? vertical_lineto_argument $(vertical_lineto_argument_b,1) %(vertical_lineto_argument_b,0))*;
385 vertical_lineto =
386 ('V' %mode_abs| 'v' %mode_rel)
387 wsp* vertical_lineto_argument_sequence;
388
389 horizontal_lineto_argument = coordinate %horizontal_lineto;
390 horizontal_lineto_argument_sequence =
391 horizontal_lineto_argument $(horizontal_lineto_argument_a,1) %(horizontal_lineto_argument_a,0)
392 (comma_wsp? horizontal_lineto_argument $(horizontal_lineto_argument_b,1) %(horizontal_lineto_argument_b,0))*;
393 horizontal_lineto =
394 ('H' %mode_abs| 'h' %mode_rel)
395 wsp* horizontal_lineto_argument_sequence;
396
397 lineto_argument = coordinate_pair %lineto;
398 lineto_argument_sequence =
399 lineto_argument $1 %0
400 (comma_wsp? lineto_argument $1 %0)*;
401 lineto =
402 ('L' %mode_abs| 'l' %mode_rel) wsp*
403 lineto_argument_sequence;
404
405 closepath = ('Z' | 'z') %closepath;
406
407 moveto_argument = coordinate_pair %moveto;
408 moveto_argument_sequence =
409 moveto_argument $1 %0
410 (comma_wsp? lineto_argument $1 %0)*;
411 moveto =
412 ('M' %mode_abs | 'm' %mode_rel)
413 wsp* moveto_argument_sequence;
414
415 drawto_command =
416 closepath | lineto |
417 horizontal_lineto | vertical_lineto |
418 curveto | smooth_curveto |
419 quadratic_bezier_curveto |
420 smooth_quadratic_bezier_curveto |
421 elliptical_arc;
422
423 drawto_commands = drawto_command (wsp* drawto_command)*;
424 moveto_drawto_command_group = moveto wsp* drawto_commands?;
425 moveto_drawto_command_groups =
426 moveto_drawto_command_group
427 (wsp* moveto_drawto_command_group)*;
428
429 svg_path = wsp* moveto_drawto_command_groups? wsp*;
430
431
432 main := svg_path;
433
434 write exec;
435 }%%
436
437
1/2
✓ Branch 0 taken 836 times.
✗ Branch 1 not taken.
836 if (finish) {
438
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 836 times.
836 if (cs < svg_path_first_final) {
439 throw SVGPathParseError();
440 }
441 } else if (start != NULL) {
442 _number_part = std::string(start, pe);
443 }
444
445
1/2
✓ Branch 0 taken 836 times.
✗ Branch 1 not taken.
836 if (finish) {
446 836 _pushCurve(NULL);
447 836 _sink.flush();
448 836 reset();
449 }
450 836 }
451
452 836 void parse_svg_path(char const *str, PathSink &sink)
453 {
454
1/2
✓ Branch 2 taken 836 times.
✗ Branch 3 not taken.
836 SVGPathParser parser(sink);
455
1/2
✓ Branch 1 taken 836 times.
✗ Branch 2 not taken.
836 parser.parse(str);
456 1672 }
457
458 void parse_svg_path_file(FILE *fi, PathSink &sink)
459 {
460 static const size_t BUFFER_SIZE = 4096;
461 char buffer[BUFFER_SIZE];
462 size_t bytes_read;
463 SVGPathParser parser(sink);
464
465 while (true) {
466 bytes_read = fread(buffer, 1, BUFFER_SIZE, fi);
467 if (bytes_read < BUFFER_SIZE) {
468 parser.parse(buffer, bytes_read);
469 break;
470 } else {
471 parser.feed(buffer, bytes_read);
472 }
473 }
474 }
475
476 } // namespace Geom
477
478 /*
479 Local Variables:
480 mode:c++
481 c-file-style:"stroustrup"
482 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
483 indent-tabs-mode:nil
484 fill-column:99
485 End:
486 */
487 // vim: filetype=ragel:cindent:expandtab:shiftwidth=4:softtabstop=4:fileencoding=utf-8:textwidth=99 :
488