1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * GimpVectors Import
5 * Copyright (C) 2003-2004 Sven Neumann <sven@gimp.org>
6 *
7 * Some code here is based on code from librsvg that was originally
8 * written by Raph Levien <raph@artofcode.com> for Gill.
9 *
10 * This SVG path importer implements a subset of SVG that is
11 * sufficient to parse path elements and basic shapes and to apply
12 * transformations as described by the SVG specification:
13 * http://www.w3.org/TR/SVG/. It must handle the SVG files exported
14 * by GIMP but it is also supposed to be able to extract paths and
15 * shapes from foreign SVG documents.
16 *
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <https://www.gnu.org/licenses/>.
29 */
30
31 #include "config.h"
32
33 #include <string.h>
34 #include <errno.h>
35
36 #include <gdk-pixbuf/gdk-pixbuf.h>
37 #include <gegl.h>
38
39 #include "libgimpbase/gimpbase.h"
40 #include "libgimpmath/gimpmath.h"
41
42 #include "vectors-types.h"
43
44 #include "config/gimpxmlparser.h"
45
46 #include "core/gimperror.h"
47 #include "core/gimpimage.h"
48 #include "core/gimpimage-undo.h"
49
50 #include "gimpbezierstroke.h"
51 #include "gimpstroke.h"
52 #include "gimpvectors.h"
53 #include "gimpvectors-import.h"
54
55 #include "gimp-intl.h"
56
57
58 #define COORDS_INIT \
59 { \
60 .x = 0.0, \
61 .y = 0.0, \
62 .pressure = 1.0, \
63 .xtilt = 0.0, \
64 .ytilt = 0.0, \
65 .wheel = 0.5, \
66 .velocity = 0.0, \
67 .direction = 0.0 \
68 }
69
70
71 typedef struct
72 {
73 GQueue *stack;
74 GimpImage *image;
75 gboolean scale;
76 gint svg_depth;
77 } SvgParser;
78
79
80 typedef struct _SvgHandler SvgHandler;
81
82 struct _SvgHandler
83 {
84 const gchar *name;
85
86 void (* start) (SvgHandler *handler,
87 const gchar **names,
88 const gchar **values,
89 SvgParser *parser);
90 void (* end) (SvgHandler *handler,
91 SvgParser *parser);
92
93 gdouble width;
94 gdouble height;
95 gchar *id;
96 GList *paths;
97 GimpMatrix3 *transform;
98 };
99
100
101 typedef struct
102 {
103 gchar *id;
104 GList *strokes;
105 } SvgPath;
106
107
108 static gboolean gimp_vectors_import (GimpImage *image,
109 GFile *file,
110 const gchar *str,
111 gsize len,
112 gboolean merge,
113 gboolean scale,
114 GimpVectors *parent,
115 gint position,
116 GList **ret_vectors,
117 GError **error);
118
119 static void svg_parser_start_element (GMarkupParseContext *context,
120 const gchar *element_name,
121 const gchar **attribute_names,
122 const gchar **attribute_values,
123 gpointer user_data,
124 GError **error);
125 static void svg_parser_end_element (GMarkupParseContext *context,
126 const gchar *element_name,
127 gpointer user_data,
128 GError **error);
129
130 static const GMarkupParser markup_parser =
131 {
132 svg_parser_start_element,
133 svg_parser_end_element,
134 NULL, /* characters */
135 NULL, /* passthrough */
136 NULL /* error */
137 };
138
139
140 static void svg_handler_svg_start (SvgHandler *handler,
141 const gchar **names,
142 const gchar **values,
143 SvgParser *parser);
144 static void svg_handler_svg_end (SvgHandler *handler,
145 SvgParser *parser);
146 static void svg_handler_group_start (SvgHandler *handler,
147 const gchar **names,
148 const gchar **values,
149 SvgParser *parser);
150 static void svg_handler_path_start (SvgHandler *handler,
151 const gchar **names,
152 const gchar **values,
153 SvgParser *parser);
154 static void svg_handler_rect_start (SvgHandler *handler,
155 const gchar **names,
156 const gchar **values,
157 SvgParser *parser);
158 static void svg_handler_ellipse_start (SvgHandler *handler,
159 const gchar **names,
160 const gchar **values,
161 SvgParser *parser);
162 static void svg_handler_line_start (SvgHandler *handler,
163 const gchar **names,
164 const gchar **values,
165 SvgParser *parser);
166 static void svg_handler_poly_start (SvgHandler *handler,
167 const gchar **names,
168 const gchar **values,
169 SvgParser *parser);
170
171 static const SvgHandler svg_handlers[] =
172 {
173 { "svg", svg_handler_svg_start, svg_handler_svg_end },
174 { "g", svg_handler_group_start, NULL },
175 { "path", svg_handler_path_start, NULL },
176 { "rect", svg_handler_rect_start, NULL },
177 { "circle", svg_handler_ellipse_start, NULL },
178 { "ellipse", svg_handler_ellipse_start, NULL },
179 { "line", svg_handler_line_start, NULL },
180 { "polyline", svg_handler_poly_start, NULL },
181 { "polygon", svg_handler_poly_start, NULL }
182 };
183
184
185 static gboolean parse_svg_length (const gchar *value,
186 gdouble reference,
187 gdouble resolution,
188 gdouble *length);
189 static gboolean parse_svg_viewbox (const gchar *value,
190 gdouble *width,
191 gdouble *height,
192 GimpMatrix3 *matrix);
193 static gboolean parse_svg_transform (const gchar *value,
194 GimpMatrix3 *matrix);
195 static GList * parse_path_data (const gchar *data);
196
197
198 /**
199 * gimp_vectors_import_file:
200 * @image: the #GimpImage to add the paths to
201 * @file: a SVG file
202 * @merge: should multiple paths be merged into a single #GimpVectors object
203 * @scale: should the SVG be scaled to fit the image dimensions
204 * @position: position in the image's vectors stack where to add the vectors
205 * @error: location to store possible errors
206 *
207 * Imports one or more paths and basic shapes from a SVG file.
208 *
209 * Return value: %TRUE on success, %FALSE if an error occurred
210 **/
211 gboolean
gimp_vectors_import_file(GimpImage * image,GFile * file,gboolean merge,gboolean scale,GimpVectors * parent,gint position,GList ** ret_vectors,GError ** error)212 gimp_vectors_import_file (GimpImage *image,
213 GFile *file,
214 gboolean merge,
215 gboolean scale,
216 GimpVectors *parent,
217 gint position,
218 GList **ret_vectors,
219 GError **error)
220 {
221 g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
222 g_return_val_if_fail (G_IS_FILE (file), FALSE);
223 g_return_val_if_fail (parent == NULL ||
224 parent == GIMP_IMAGE_ACTIVE_PARENT ||
225 GIMP_IS_VECTORS (parent), FALSE);
226 g_return_val_if_fail (parent == NULL ||
227 parent == GIMP_IMAGE_ACTIVE_PARENT ||
228 gimp_item_is_attached (GIMP_ITEM (parent)), FALSE);
229 g_return_val_if_fail (parent == NULL ||
230 parent == GIMP_IMAGE_ACTIVE_PARENT ||
231 gimp_item_get_image (GIMP_ITEM (parent)) == image,
232 FALSE);
233 g_return_val_if_fail (parent == NULL ||
234 parent == GIMP_IMAGE_ACTIVE_PARENT ||
235 gimp_viewable_get_children (GIMP_VIEWABLE (parent)),
236 FALSE);
237 g_return_val_if_fail (ret_vectors == NULL || *ret_vectors == NULL, FALSE);
238 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
239
240 return gimp_vectors_import (image, file, NULL, 0, merge, scale,
241 parent, position,
242 ret_vectors, error);
243 }
244
245 /**
246 * gimp_vectors_import_string:
247 * @image: the #GimpImage to add the paths to
248 * @buffer: a character buffer to parse
249 * @len: number of bytes in @str or -1 if @str is %NUL-terminated
250 * @merge: should multiple paths be merged into a single #GimpVectors object
251 * @scale: should the SVG be scaled to fit the image dimensions
252 * @error: location to store possible errors
253 *
254 * Imports one or more paths and basic shapes from a SVG file.
255 *
256 * Return value: %TRUE on success, %FALSE if an error occurred
257 **/
258 gboolean
gimp_vectors_import_buffer(GimpImage * image,const gchar * buffer,gsize len,gboolean merge,gboolean scale,GimpVectors * parent,gint position,GList ** ret_vectors,GError ** error)259 gimp_vectors_import_buffer (GimpImage *image,
260 const gchar *buffer,
261 gsize len,
262 gboolean merge,
263 gboolean scale,
264 GimpVectors *parent,
265 gint position,
266 GList **ret_vectors,
267 GError **error)
268 {
269 g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
270 g_return_val_if_fail (buffer != NULL || len == 0, FALSE);
271 g_return_val_if_fail (parent == NULL ||
272 parent == GIMP_IMAGE_ACTIVE_PARENT ||
273 GIMP_IS_VECTORS (parent), FALSE);
274 g_return_val_if_fail (parent == NULL ||
275 parent == GIMP_IMAGE_ACTIVE_PARENT ||
276 gimp_item_is_attached (GIMP_ITEM (parent)), FALSE);
277 g_return_val_if_fail (parent == NULL ||
278 parent == GIMP_IMAGE_ACTIVE_PARENT ||
279 gimp_item_get_image (GIMP_ITEM (parent)) == image,
280 FALSE);
281 g_return_val_if_fail (parent == NULL ||
282 parent == GIMP_IMAGE_ACTIVE_PARENT ||
283 gimp_viewable_get_children (GIMP_VIEWABLE (parent)),
284 FALSE);
285 g_return_val_if_fail (ret_vectors == NULL || *ret_vectors == NULL, FALSE);
286 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
287
288 return gimp_vectors_import (image, NULL, buffer, len, merge, scale,
289 parent, position,
290 ret_vectors, error);
291 }
292
293 static gboolean
gimp_vectors_import(GimpImage * image,GFile * file,const gchar * str,gsize len,gboolean merge,gboolean scale,GimpVectors * parent,gint position,GList ** ret_vectors,GError ** error)294 gimp_vectors_import (GimpImage *image,
295 GFile *file,
296 const gchar *str,
297 gsize len,
298 gboolean merge,
299 gboolean scale,
300 GimpVectors *parent,
301 gint position,
302 GList **ret_vectors,
303 GError **error)
304 {
305 GimpXmlParser *xml_parser;
306 SvgParser parser;
307 GList *paths;
308 SvgHandler *base;
309 gboolean success = TRUE;
310
311 parser.stack = g_queue_new ();
312 parser.image = image;
313 parser.scale = scale;
314 parser.svg_depth = 0;
315
316 /* the base of the stack, defines the size of the view-port */
317 base = g_slice_new0 (SvgHandler);
318 base->name = "image";
319 base->width = gimp_image_get_width (image);
320 base->height = gimp_image_get_height (image);
321
322 g_queue_push_head (parser.stack, base);
323
324 xml_parser = gimp_xml_parser_new (&markup_parser, &parser);
325
326 if (file)
327 success = gimp_xml_parser_parse_gfile (xml_parser, file, error);
328 else
329 success = gimp_xml_parser_parse_buffer (xml_parser, str, len, error);
330
331 gimp_xml_parser_free (xml_parser);
332
333 if (success)
334 {
335 if (base->paths)
336 {
337 GimpVectors *vectors = NULL;
338
339 base->paths = g_list_reverse (base->paths);
340
341 merge = merge && base->paths->next;
342
343 gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_VECTORS_IMPORT,
344 _("Import Paths"));
345
346 for (paths = base->paths; paths; paths = paths->next)
347 {
348 SvgPath *path = paths->data;
349 GList *list;
350
351 if (! merge || ! vectors)
352 {
353 vectors = gimp_vectors_new (image,
354 ((merge || ! path->id) ?
355 _("Imported Path") : path->id));
356 gimp_image_add_vectors (image, vectors,
357 parent, position, TRUE);
358 gimp_vectors_freeze (vectors);
359
360 if (ret_vectors)
361 *ret_vectors = g_list_prepend (*ret_vectors, vectors);
362
363 if (position != -1)
364 position++;
365 }
366
367 for (list = path->strokes; list; list = list->next)
368 gimp_vectors_stroke_add (vectors, GIMP_STROKE (list->data));
369
370 if (! merge)
371 gimp_vectors_thaw (vectors);
372
373 g_list_free_full (path->strokes, g_object_unref);
374 path->strokes = NULL;
375 }
376
377 if (merge)
378 gimp_vectors_thaw (vectors);
379
380 gimp_image_undo_group_end (image);
381 }
382 else
383 {
384 if (file)
385 g_set_error (error, GIMP_ERROR, GIMP_FAILED,
386 _("No paths found in '%s'"),
387 gimp_file_get_utf8_name (file));
388 else
389 g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
390 _("No paths found in the buffer"));
391
392 success = FALSE;
393 }
394 }
395 else if (error && *error && file) /* parser reported an error */
396 {
397 gchar *msg = (*error)->message;
398
399 (*error)->message =
400 g_strdup_printf (_("Failed to import paths from '%s': %s"),
401 gimp_file_get_utf8_name (file), msg);
402
403 g_free (msg);
404 }
405
406 while ((base = g_queue_pop_head (parser.stack)) != NULL)
407 {
408 for (paths = base->paths; paths; paths = paths->next)
409 {
410 SvgPath *path = paths->data;
411 GList *list;
412
413 g_free (path->id);
414
415 for (list = path->strokes; list; list = list->next)
416 g_object_unref (list->data);
417
418 g_list_free (path->strokes);
419
420 g_slice_free (SvgPath, path);
421 }
422
423 g_list_free (base->paths);
424
425 g_slice_free (GimpMatrix3, base->transform);
426 g_slice_free (SvgHandler, base);
427 }
428
429 g_queue_free (parser.stack);
430
431 return success;
432 }
433
434 static void
svg_parser_start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)435 svg_parser_start_element (GMarkupParseContext *context,
436 const gchar *element_name,
437 const gchar **attribute_names,
438 const gchar **attribute_values,
439 gpointer user_data,
440 GError **error)
441 {
442 SvgParser *parser = user_data;
443 SvgHandler *handler;
444 SvgHandler *base;
445 gint i = 0;
446
447 handler = g_slice_new0 (SvgHandler);
448 base = g_queue_peek_head (parser->stack);
449
450 /* if the element is not rendered, always use the generic handler */
451 if (base->width <= 0.0 || base->height <= 0.0)
452 i = G_N_ELEMENTS (svg_handlers);
453
454 for (; i < G_N_ELEMENTS (svg_handlers); i++)
455 if (strcmp (svg_handlers[i].name, element_name) == 0)
456 {
457 handler->name = svg_handlers[i].name;
458 handler->start = svg_handlers[i].start;
459 break;
460 }
461
462 handler->width = base->width;
463 handler->height = base->height;
464
465 g_queue_push_head (parser->stack, handler);
466
467 if (handler->start)
468 handler->start (handler, attribute_names, attribute_values, parser);
469 }
470
471 static void
svg_parser_end_element(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)472 svg_parser_end_element (GMarkupParseContext *context,
473 const gchar *element_name,
474 gpointer user_data,
475 GError **error)
476 {
477 SvgParser *parser = user_data;
478 SvgHandler *handler;
479 SvgHandler *base;
480 GList *paths;
481
482 handler = g_queue_pop_head (parser->stack);
483
484 g_return_if_fail (handler != NULL &&
485 (handler->name == NULL ||
486 strcmp (handler->name, element_name) == 0));
487
488 if (handler->end)
489 handler->end (handler, parser);
490
491 if (handler->paths)
492 {
493 if (handler->transform)
494 {
495 for (paths = handler->paths; paths; paths = paths->next)
496 {
497 SvgPath *path = paths->data;
498 GList *list;
499
500 for (list = path->strokes; list; list = list->next)
501 gimp_stroke_transform (GIMP_STROKE (list->data),
502 handler->transform, NULL);
503 }
504
505 g_slice_free (GimpMatrix3, handler->transform);
506 }
507
508 base = g_queue_peek_head (parser->stack);
509 base->paths = g_list_concat (base->paths, handler->paths);
510 }
511
512 g_slice_free (SvgHandler, handler);
513 }
514
515 static void
svg_handler_svg_start(SvgHandler * handler,const gchar ** names,const gchar ** values,SvgParser * parser)516 svg_handler_svg_start (SvgHandler *handler,
517 const gchar **names,
518 const gchar **values,
519 SvgParser *parser)
520 {
521 GimpMatrix3 *matrix;
522 GimpMatrix3 box;
523 const gchar *viewbox = NULL;
524 gdouble x = 0;
525 gdouble y = 0;
526 gdouble w = handler->width;
527 gdouble h = handler->height;
528 gdouble xres;
529 gdouble yres;
530
531 matrix = g_slice_new (GimpMatrix3);
532 gimp_matrix3_identity (matrix);
533
534 gimp_image_get_resolution (parser->image, &xres, &yres);
535
536 while (*names)
537 {
538 switch (*names[0])
539 {
540 case 'x':
541 if (strcmp (*names, "x") == 0)
542 parse_svg_length (*values, handler->width, xres, &x);
543 break;
544
545 case 'y':
546 if (strcmp (*names, "y") == 0)
547 parse_svg_length (*values, handler->height, yres, &y);
548 break;
549
550 case 'w':
551 if (strcmp (*names, "width") == 0)
552 parse_svg_length (*values, handler->width, xres, &w);
553 break;
554
555 case 'h':
556 if (strcmp (*names, "height") == 0)
557 parse_svg_length (*values, handler->height, yres, &h);
558 break;
559
560 case 'v':
561 if (strcmp (*names, "viewBox") == 0)
562 viewbox = *values;
563 break;
564 }
565
566 names++;
567 values++;
568 }
569
570 if (x || y)
571 {
572 /* according to the spec offsets are meaningless on the outermost svg */
573 if (parser->svg_depth > 0)
574 gimp_matrix3_translate (matrix, x, y);
575 }
576
577 if (viewbox && parse_svg_viewbox (viewbox, &w, &h, &box))
578 {
579 gimp_matrix3_mult (&box, matrix);
580 }
581
582 /* optionally scale the outermost svg to image size */
583 if (parser->scale && parser->svg_depth == 0)
584 {
585 if (w > 0.0 && h > 0.0)
586 gimp_matrix3_scale (matrix,
587 gimp_image_get_width (parser->image) / w,
588 gimp_image_get_height (parser->image) / h);
589 }
590
591 handler->width = w;
592 handler->height = h;
593
594 handler->transform = matrix;
595
596 parser->svg_depth++;
597 }
598
599 static void
svg_handler_svg_end(SvgHandler * handler,SvgParser * parser)600 svg_handler_svg_end (SvgHandler *handler,
601 SvgParser *parser)
602 {
603 parser->svg_depth--;
604 }
605
606 static void
svg_handler_group_start(SvgHandler * handler,const gchar ** names,const gchar ** values,SvgParser * parser)607 svg_handler_group_start (SvgHandler *handler,
608 const gchar **names,
609 const gchar **values,
610 SvgParser *parser)
611 {
612 while (*names)
613 {
614 if (strcmp (*names, "transform") == 0 && ! handler->transform)
615 {
616 GimpMatrix3 matrix;
617
618 if (parse_svg_transform (*values, &matrix))
619 {
620 handler->transform = g_slice_dup (GimpMatrix3, &matrix);
621
622 #ifdef DEBUG_VECTORS_IMPORT
623 g_printerr ("transform %s: %g %g %g %g %g %g %g %g %g\n",
624 handler->id ? handler->id : "(null)",
625 handler->transform->coeff[0][0],
626 handler->transform->coeff[0][1],
627 handler->transform->coeff[0][2],
628 handler->transform->coeff[1][0],
629 handler->transform->coeff[1][1],
630 handler->transform->coeff[1][2],
631 handler->transform->coeff[2][0],
632 handler->transform->coeff[2][1],
633 handler->transform->coeff[2][2]);
634 #endif
635 }
636 }
637
638 names++;
639 values++;
640 }
641 }
642
643 static void
svg_handler_path_start(SvgHandler * handler,const gchar ** names,const gchar ** values,SvgParser * parser)644 svg_handler_path_start (SvgHandler *handler,
645 const gchar **names,
646 const gchar **values,
647 SvgParser *parser)
648 {
649 SvgPath *path = g_slice_new0 (SvgPath);
650
651 while (*names)
652 {
653 switch (*names[0])
654 {
655 case 'i':
656 if (strcmp (*names, "id") == 0 && ! path->id)
657 path->id = g_strdup (*values);
658 break;
659
660 case 'd':
661 if (strcmp (*names, "d") == 0 && ! path->strokes)
662 path->strokes = parse_path_data (*values);
663 break;
664
665 case 't':
666 if (strcmp (*names, "transform") == 0 && ! handler->transform)
667 {
668 GimpMatrix3 matrix;
669
670 if (parse_svg_transform (*values, &matrix))
671 {
672 handler->transform = g_slice_dup (GimpMatrix3, &matrix);
673 }
674 }
675 break;
676 }
677
678 names++;
679 values++;
680 }
681
682 handler->paths = g_list_prepend (handler->paths, path);
683 }
684
685 static void
svg_handler_rect_start(SvgHandler * handler,const gchar ** names,const gchar ** values,SvgParser * parser)686 svg_handler_rect_start (SvgHandler *handler,
687 const gchar **names,
688 const gchar **values,
689 SvgParser *parser)
690 {
691 SvgPath *path = g_slice_new0 (SvgPath);
692 gdouble x = 0.0;
693 gdouble y = 0.0;
694 gdouble width = 0.0;
695 gdouble height = 0.0;
696 gdouble rx = 0.0;
697 gdouble ry = 0.0;
698 gdouble xres;
699 gdouble yres;
700
701 gimp_image_get_resolution (parser->image, &xres, &yres);
702
703 while (*names)
704 {
705 switch (*names[0])
706 {
707 case 'i':
708 if (strcmp (*names, "id") == 0 && ! path->id)
709 path->id = g_strdup (*values);
710 break;
711
712 case 'x':
713 if (strcmp (*names, "x") == 0)
714 parse_svg_length (*values, handler->width, xres, &x);
715 break;
716
717 case 'y':
718 if (strcmp (*names, "y") == 0)
719 parse_svg_length (*values, handler->height, yres, &y);
720 break;
721
722 case 'w':
723 if (strcmp (*names, "width") == 0)
724 parse_svg_length (*values, handler->width, xres, &width);
725 break;
726
727 case 'h':
728 if (strcmp (*names, "height") == 0)
729 parse_svg_length (*values, handler->height, yres, &height);
730 break;
731
732 case 'r':
733 if (strcmp (*names, "rx") == 0)
734 parse_svg_length (*values, handler->width, xres, &rx);
735 else if (strcmp (*names, "ry") == 0)
736 parse_svg_length (*values, handler->height, yres, &ry);
737 break;
738
739 case 't':
740 if (strcmp (*names, "transform") == 0 && ! handler->transform)
741 {
742 GimpMatrix3 matrix;
743
744 if (parse_svg_transform (*values, &matrix))
745 {
746 handler->transform = g_slice_dup (GimpMatrix3, &matrix);
747 }
748 }
749 break;
750 }
751
752 names++;
753 values++;
754 }
755
756 if (width > 0.0 && height > 0.0 && rx >= 0.0 && ry >= 0.0)
757 {
758 GimpStroke *stroke;
759 GimpCoords point = COORDS_INIT;
760
761 if (rx == 0.0)
762 rx = ry;
763 if (ry == 0.0)
764 ry = rx;
765
766 rx = MIN (rx, width / 2);
767 ry = MIN (ry, height / 2);
768
769 point.x = x + width - rx;
770 point.y = y;
771 stroke = gimp_bezier_stroke_new_moveto (&point);
772
773 if (rx)
774 {
775 GimpCoords end = COORDS_INIT;
776
777 end.x = x + width;
778 end.y = y + ry;
779
780 gimp_bezier_stroke_arcto (stroke, rx, ry, 0, FALSE, TRUE, &end);
781 }
782
783 point.x = x + width;
784 point.y = y + height - ry;
785 gimp_bezier_stroke_lineto (stroke, &point);
786
787 if (rx)
788 {
789 GimpCoords end = COORDS_INIT;
790
791 end.x = x + width - rx;
792 end.y = y + height;
793
794 gimp_bezier_stroke_arcto (stroke, rx, ry, 0, FALSE, TRUE, &end);
795 }
796
797 point.x = x + rx;
798 point.y = y + height;
799 gimp_bezier_stroke_lineto (stroke, &point);
800
801 if (rx)
802 {
803 GimpCoords end = COORDS_INIT;
804
805 end.x = x;
806 end.y = y + height - ry;
807
808 gimp_bezier_stroke_arcto (stroke, rx, ry, 0, FALSE, TRUE, &end);
809 }
810
811 point.x = x;
812 point.y = y + ry;
813 gimp_bezier_stroke_lineto (stroke, &point);
814
815 if (rx)
816 {
817 GimpCoords end = COORDS_INIT;
818
819 end.x = x + rx;
820 end.y = y;
821
822 gimp_bezier_stroke_arcto (stroke, rx, ry, 0, FALSE, TRUE, &end);
823 }
824
825 /* the last line is handled by closing the stroke */
826 gimp_stroke_close (stroke);
827
828 path->strokes = g_list_prepend (path->strokes, stroke);
829 }
830
831 handler->paths = g_list_prepend (handler->paths, path);
832 }
833
834 static void
svg_handler_ellipse_start(SvgHandler * handler,const gchar ** names,const gchar ** values,SvgParser * parser)835 svg_handler_ellipse_start (SvgHandler *handler,
836 const gchar **names,
837 const gchar **values,
838 SvgParser *parser)
839 {
840 SvgPath *path = g_slice_new0 (SvgPath);
841 GimpCoords center = COORDS_INIT;
842 gdouble rx = 0.0;
843 gdouble ry = 0.0;
844 gdouble xres;
845 gdouble yres;
846
847 gimp_image_get_resolution (parser->image, &xres, &yres);
848
849 while (*names)
850 {
851 switch (*names[0])
852 {
853 case 'i':
854 if (strcmp (*names, "id") == 0 && ! path->id)
855 path->id = g_strdup (*values);
856 break;
857
858 case 'c':
859 if (strcmp (*names, "cx") == 0)
860 parse_svg_length (*values, handler->width, xres, ¢er.x);
861 else if (strcmp (*names, "cy") == 0)
862 parse_svg_length (*values, handler->height, yres, ¢er.y);
863 break;
864
865 case 'r':
866 if (strcmp (*names, "r") == 0)
867 {
868 parse_svg_length (*values, handler->width, xres, &rx);
869 parse_svg_length (*values, handler->height, yres, &ry);
870 }
871 else if (strcmp (*names, "rx") == 0)
872 {
873 parse_svg_length (*values, handler->width, xres, &rx);
874 }
875 else if (strcmp (*names, "ry") == 0)
876 {
877 parse_svg_length (*values, handler->height, yres, &ry);
878 }
879 break;
880
881 case 't':
882 if (strcmp (*names, "transform") == 0 && ! handler->transform)
883 {
884 GimpMatrix3 matrix;
885
886 if (parse_svg_transform (*values, &matrix))
887 {
888 handler->transform = g_slice_dup (GimpMatrix3, &matrix);
889 }
890 }
891 break;
892 }
893
894 names++;
895 values++;
896 }
897
898 if (rx >= 0.0 && ry >= 0.0)
899 path->strokes = g_list_prepend (path->strokes,
900 gimp_bezier_stroke_new_ellipse (¢er,
901 rx, ry,
902 0.0));
903
904 handler->paths = g_list_prepend (handler->paths, path);
905 }
906
907 static void
svg_handler_line_start(SvgHandler * handler,const gchar ** names,const gchar ** values,SvgParser * parser)908 svg_handler_line_start (SvgHandler *handler,
909 const gchar **names,
910 const gchar **values,
911 SvgParser *parser)
912 {
913 SvgPath *path = g_slice_new0 (SvgPath);
914 GimpCoords start = COORDS_INIT;
915 GimpCoords end = COORDS_INIT;
916 GimpStroke *stroke;
917 gdouble xres;
918 gdouble yres;
919
920 gimp_image_get_resolution (parser->image, &xres, &yres);
921
922 while (*names)
923 {
924 switch (*names[0])
925 {
926 case 'i':
927 if (strcmp (*names, "id") == 0 && ! path->id)
928 path->id = g_strdup (*values);
929 break;
930
931 case 'x':
932 if (strcmp (*names, "x1") == 0)
933 parse_svg_length (*values, handler->width, xres, &start.x);
934 else if (strcmp (*names, "x2") == 0)
935 parse_svg_length (*values, handler->width, xres, &end.x);
936 break;
937
938 case 'y':
939 if (strcmp (*names, "y1") == 0)
940 parse_svg_length (*values, handler->height, yres, &start.y);
941 else if (strcmp (*names, "y2") == 0)
942 parse_svg_length (*values, handler->height, yres, &end.y);
943 break;
944
945 case 't':
946 if (strcmp (*names, "transform") == 0 && ! handler->transform)
947 {
948 GimpMatrix3 matrix;
949
950 if (parse_svg_transform (*values, &matrix))
951 {
952 handler->transform = g_slice_dup (GimpMatrix3, &matrix);
953 }
954 }
955 break;
956 }
957
958 names++;
959 values++;
960 }
961
962 stroke = gimp_bezier_stroke_new_moveto (&start);
963 gimp_bezier_stroke_lineto (stroke, &end);
964
965 path->strokes = g_list_prepend (path->strokes, stroke);
966
967 handler->paths = g_list_prepend (handler->paths, path);
968 }
969
970 static void
svg_handler_poly_start(SvgHandler * handler,const gchar ** names,const gchar ** values,SvgParser * parser)971 svg_handler_poly_start (SvgHandler *handler,
972 const gchar **names,
973 const gchar **values,
974 SvgParser *parser)
975 {
976 SvgPath *path = g_slice_new0 (SvgPath);
977 GString *points = NULL;
978
979 while (*names)
980 {
981 switch (*names[0])
982 {
983 case 'i':
984 if (strcmp (*names, "id") == 0 && ! path->id)
985 path->id = g_strdup (*values);
986 break;
987
988 case 'p':
989 if (strcmp (*names, "points") == 0 && ! points)
990 {
991 const gchar *p = *values;
992 const gchar *m = NULL;
993 const gchar *l = NULL;
994 gint n = 0;
995
996 while (*p)
997 {
998 while (g_ascii_isspace (*p) || *p == ',')
999 p++;
1000
1001 switch (n)
1002 {
1003 case 0:
1004 m = p;
1005 break;
1006 case 2:
1007 l = p;
1008 break;
1009 }
1010
1011 if (*p)
1012 n++;
1013
1014 while (*p && ! g_ascii_isspace (*p) && *p != ',')
1015 p++;
1016 }
1017
1018 if ((n > 3) && (n % 2 == 0))
1019 {
1020 points = g_string_sized_new (p - *values + 8);
1021
1022 g_string_append_len (points, "M ", 2);
1023 g_string_append_len (points, m, l - m);
1024
1025 g_string_append_len (points, "L ", 2);
1026 g_string_append_len (points, l, p - l);
1027
1028 if (strcmp (handler->name, "polygon") == 0)
1029 g_string_append_c (points, 'Z');
1030 }
1031 }
1032 break;
1033
1034 case 't':
1035 if (strcmp (*names, "transform") == 0 && ! handler->transform)
1036 {
1037 GimpMatrix3 matrix;
1038
1039 if (parse_svg_transform (*values, &matrix))
1040 {
1041 handler->transform = g_slice_dup (GimpMatrix3, &matrix);
1042 }
1043 }
1044 break;
1045 }
1046
1047 names++;
1048 values++;
1049 }
1050
1051 if (points)
1052 {
1053 path->strokes = parse_path_data (points->str);
1054 g_string_free (points, TRUE);
1055 }
1056
1057 handler->paths = g_list_prepend (handler->paths, path);
1058 }
1059
1060 static gboolean
parse_svg_length(const gchar * value,gdouble reference,gdouble resolution,gdouble * length)1061 parse_svg_length (const gchar *value,
1062 gdouble reference,
1063 gdouble resolution,
1064 gdouble *length)
1065 {
1066 GimpUnit unit = GIMP_UNIT_PIXEL;
1067 gdouble len;
1068 gchar *ptr;
1069
1070 len = g_ascii_strtod (value, &ptr);
1071
1072 while (g_ascii_isspace (*ptr))
1073 ptr++;
1074
1075 switch (ptr[0])
1076 {
1077 case '\0':
1078 break;
1079
1080 case 'p':
1081 switch (ptr[1])
1082 {
1083 case 'x': break;
1084 case 't': unit = GIMP_UNIT_POINT; break;
1085 case 'c': unit = GIMP_UNIT_PICA; break;
1086 default:
1087 return FALSE;
1088 }
1089 ptr += 2;
1090 break;
1091
1092 case 'c':
1093 if (ptr[1] == 'm')
1094 len *= 10.0, unit = GIMP_UNIT_MM;
1095 else
1096 return FALSE;
1097 ptr += 2;
1098 break;
1099
1100 case 'm':
1101 if (ptr[1] == 'm')
1102 unit = GIMP_UNIT_MM;
1103 else
1104 return FALSE;
1105 ptr += 2;
1106 break;
1107
1108 case 'i':
1109 if (ptr[1] == 'n')
1110 unit = GIMP_UNIT_INCH;
1111 else
1112 return FALSE;
1113 ptr += 2;
1114 break;
1115
1116 case '%':
1117 unit = GIMP_UNIT_PERCENT;
1118 ptr += 1;
1119 break;
1120
1121 default:
1122 return FALSE;
1123 }
1124
1125 while (g_ascii_isspace (*ptr))
1126 ptr++;
1127
1128 if (*ptr)
1129 return FALSE;
1130
1131 switch (unit)
1132 {
1133 case GIMP_UNIT_PERCENT:
1134 *length = len * reference / 100.0;
1135 break;
1136
1137 case GIMP_UNIT_PIXEL:
1138 *length = len;
1139 break;
1140
1141 default:
1142 *length = len * resolution / gimp_unit_get_factor (unit);
1143 break;
1144 }
1145
1146 return TRUE;
1147 }
1148
1149 static gboolean
parse_svg_viewbox(const gchar * value,gdouble * width,gdouble * height,GimpMatrix3 * matrix)1150 parse_svg_viewbox (const gchar *value,
1151 gdouble *width,
1152 gdouble *height,
1153 GimpMatrix3 *matrix)
1154 {
1155 gdouble x, y, w, h;
1156 gchar *tok;
1157 gchar *str = g_strdup (value);
1158 gboolean success = FALSE;
1159
1160 x = y = w = h = 0;
1161
1162 tok = strtok (str, ", \t");
1163 if (tok)
1164 {
1165 x = g_ascii_strtod (tok, NULL);
1166 tok = strtok (NULL, ", \t");
1167 if (tok)
1168 {
1169 y = g_ascii_strtod (tok, NULL);
1170 tok = strtok (NULL, ", \t");
1171 if (tok != NULL)
1172 {
1173 w = g_ascii_strtod (tok, NULL);
1174 tok = strtok (NULL, ", \t");
1175 if (tok)
1176 {
1177 h = g_ascii_strtod (tok, NULL);
1178 success = TRUE;
1179 }
1180 }
1181 }
1182 }
1183
1184 g_free (str);
1185
1186 if (success)
1187 {
1188 gimp_matrix3_identity (matrix);
1189 gimp_matrix3_translate (matrix, -x, -y);
1190
1191 if (w > 0.0 && h > 0.0)
1192 {
1193 gimp_matrix3_scale (matrix, *width / w, *height / h);
1194 }
1195 else /* disable rendering of the element */
1196 {
1197 #ifdef DEBUG_VECTORS_IMPORT
1198 g_printerr ("empty viewBox");
1199 #endif
1200 *width = *height = 0.0;
1201 }
1202 }
1203 else
1204 {
1205 g_printerr ("SVG import: cannot parse viewBox attribute\n");
1206 }
1207
1208 return success;
1209 }
1210
1211 static gboolean
parse_svg_transform(const gchar * value,GimpMatrix3 * matrix)1212 parse_svg_transform (const gchar *value,
1213 GimpMatrix3 *matrix)
1214 {
1215 gint i;
1216
1217 gimp_matrix3_identity (matrix);
1218
1219 for (i = 0; value[i]; i++)
1220 {
1221 GimpMatrix3 trafo;
1222 gchar keyword[32];
1223 gdouble args[6];
1224 gint n_args;
1225 gint key_len;
1226
1227 gimp_matrix3_identity (&trafo);
1228
1229 /* skip initial whitespace */
1230 while (g_ascii_isspace (value[i]))
1231 i++;
1232
1233 /* parse keyword */
1234 for (key_len = 0; key_len < sizeof (keyword); key_len++)
1235 {
1236 gchar c = value[i];
1237
1238 if (g_ascii_isalpha (c) || c == '-')
1239 keyword[key_len] = value[i++];
1240 else
1241 break;
1242 }
1243
1244 if (key_len >= sizeof (keyword))
1245 return FALSE;
1246
1247 keyword[key_len] = '\0';
1248
1249 /* skip whitespace */
1250 while (g_ascii_isspace (value[i]))
1251 i++;
1252
1253 if (value[i] != '(')
1254 return FALSE;
1255 i++;
1256
1257 for (n_args = 0; ; n_args++)
1258 {
1259 gchar c;
1260 gchar *end_ptr;
1261
1262 /* skip whitespace */
1263 while (g_ascii_isspace (value[i]))
1264 i++;
1265
1266 c = value[i];
1267 if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.')
1268 {
1269 if (n_args == G_N_ELEMENTS (args))
1270 return FALSE; /* too many args */
1271
1272 args[n_args] = g_ascii_strtod (value + i, &end_ptr);
1273 i = end_ptr - value;
1274
1275 while (g_ascii_isspace (value[i]))
1276 i++;
1277
1278 /* skip optional comma */
1279 if (value[i] == ',')
1280 i++;
1281 }
1282 else if (c == ')')
1283 break;
1284 else
1285 return FALSE;
1286 }
1287
1288 /* OK, have parsed keyword and args, now calculate the transform matrix */
1289
1290 if (strcmp (keyword, "matrix") == 0)
1291 {
1292 if (n_args != 6)
1293 return FALSE;
1294
1295 gimp_matrix3_affine (&trafo,
1296 args[0], args[1],
1297 args[2], args[3],
1298 args[4], args[5]);
1299 }
1300 else if (strcmp (keyword, "translate") == 0)
1301 {
1302 if (n_args == 1)
1303 args[1] = 0.0;
1304 else if (n_args != 2)
1305 return FALSE;
1306
1307 gimp_matrix3_translate (&trafo, args[0], args[1]);
1308 }
1309 else if (strcmp (keyword, "scale") == 0)
1310 {
1311 if (n_args == 1)
1312 args[1] = args[0];
1313 else if (n_args != 2)
1314 return FALSE;
1315
1316 gimp_matrix3_scale (&trafo, args[0], args[1]);
1317 }
1318 else if (strcmp (keyword, "rotate") == 0)
1319 {
1320 if (n_args == 1)
1321 {
1322 gimp_matrix3_rotate (&trafo, gimp_deg_to_rad (args[0]));
1323 }
1324 else if (n_args == 3)
1325 {
1326 gimp_matrix3_translate (&trafo, -args[1], -args[2]);
1327 gimp_matrix3_rotate (&trafo, gimp_deg_to_rad (args[0]));
1328 gimp_matrix3_translate (&trafo, args[1], args[2]);
1329 }
1330 else
1331 return FALSE;
1332 }
1333 else if (strcmp (keyword, "skewX") == 0)
1334 {
1335 if (n_args != 1)
1336 return FALSE;
1337
1338 gimp_matrix3_xshear (&trafo, tan (gimp_deg_to_rad (args[0])));
1339 }
1340 else if (strcmp (keyword, "skewY") == 0)
1341 {
1342 if (n_args != 1)
1343 return FALSE;
1344
1345 gimp_matrix3_yshear (&trafo, tan (gimp_deg_to_rad (args[0])));
1346 }
1347 else
1348 {
1349 return FALSE; /* unknown keyword */
1350 }
1351
1352 gimp_matrix3_invert (&trafo);
1353 gimp_matrix3_mult (&trafo, matrix);
1354 }
1355
1356 gimp_matrix3_invert (matrix);
1357
1358 return TRUE;
1359 }
1360
1361
1362 /**********************************************************/
1363 /* Below is the code that parses the actual path data. */
1364 /* */
1365 /* This code is taken from librsvg and was originally */
1366 /* written by Raph Levien <raph@artofcode.com> for Gill. */
1367 /**********************************************************/
1368
1369 typedef struct
1370 {
1371 GList *strokes;
1372 GimpStroke *stroke;
1373 gdouble cpx, cpy; /* current point */
1374 gdouble rpx, rpy; /* reflection point (for 's' and 't' commands) */
1375 gchar cmd; /* current command (lowercase) */
1376 gint param; /* number of parameters */
1377 gboolean rel; /* true if relative coords */
1378 gdouble params[7]; /* parameters that have been parsed */
1379 } ParsePathContext;
1380
1381
1382 static void parse_path_default_xy (ParsePathContext *ctx,
1383 gint n_params);
1384 static void parse_path_do_cmd (ParsePathContext *ctx,
1385 gboolean final);
1386
1387
1388 static GList *
parse_path_data(const gchar * data)1389 parse_path_data (const gchar *data)
1390 {
1391 ParsePathContext ctx;
1392
1393 gboolean in_num = FALSE;
1394 gboolean in_frac = FALSE;
1395 gboolean in_exp = FALSE;
1396 gboolean exp_wait_sign = FALSE;
1397 gdouble val = 0.0;
1398 gchar c = 0;
1399 gint sign = 0;
1400 gint exp = 0;
1401 gint exp_sign = 0;
1402 gdouble frac = 0.0;
1403 gint i;
1404
1405 memset (&ctx, 0, sizeof (ParsePathContext));
1406
1407 for (i = 0; ; i++)
1408 {
1409 c = data[i];
1410
1411 if (c >= '0' && c <= '9')
1412 {
1413 /* digit */
1414 if (in_num)
1415 {
1416 if (in_exp)
1417 {
1418 exp = (exp * 10) + c - '0';
1419 exp_wait_sign = FALSE;
1420 }
1421 else if (in_frac)
1422 val += (frac *= 0.1) * (c - '0');
1423 else
1424 val = (val * 10) + c - '0';
1425 }
1426 else
1427 {
1428 in_num = TRUE;
1429 in_frac = FALSE;
1430 in_exp = FALSE;
1431 exp = 0;
1432 exp_sign = 1;
1433 exp_wait_sign = FALSE;
1434 val = c - '0';
1435 sign = 1;
1436 }
1437 }
1438 else if (c == '.')
1439 {
1440 if (! in_num)
1441 {
1442 in_num = TRUE;
1443 val = 0;
1444 }
1445
1446 in_frac = TRUE;
1447 frac = 1;
1448 }
1449 else if ((c == 'E' || c == 'e') && in_num)
1450 {
1451 in_exp = TRUE;
1452 exp_wait_sign = TRUE;
1453 exp = 0;
1454 exp_sign = 1;
1455 }
1456 else if ((c == '+' || c == '-') && in_exp)
1457 {
1458 exp_sign = c == '+' ? 1 : -1;
1459 }
1460 else if (in_num)
1461 {
1462 /* end of number */
1463
1464 val *= sign * pow (10, exp_sign * exp);
1465
1466 if (ctx.rel)
1467 {
1468 /* Handle relative coordinates. This switch statement attempts
1469 to determine _what_ the coords are relative to. This is
1470 underspecified in the 12 Apr working draft. */
1471 switch (ctx.cmd)
1472 {
1473 case 'l':
1474 case 'm':
1475 case 'c':
1476 case 's':
1477 case 'q':
1478 case 't':
1479 /* rule: even-numbered params are x-relative, odd-numbered
1480 are y-relative */
1481 if ((ctx.param & 1) == 0)
1482 val += ctx.cpx;
1483 else if ((ctx.param & 1) == 1)
1484 val += ctx.cpy;
1485 break;
1486
1487 case 'a':
1488 /* rule: sixth and seventh are x and y, rest are not
1489 relative */
1490 if (ctx.param == 5)
1491 val += ctx.cpx;
1492 else if (ctx.param == 6)
1493 val += ctx.cpy;
1494 break;
1495
1496 case 'h':
1497 /* rule: x-relative */
1498 val += ctx.cpx;
1499 break;
1500
1501 case 'v':
1502 /* rule: y-relative */
1503 val += ctx.cpy;
1504 break;
1505 }
1506 }
1507
1508 ctx.params[ctx.param++] = val;
1509 parse_path_do_cmd (&ctx, FALSE);
1510 in_num = FALSE;
1511 }
1512
1513 if (c == '\0')
1514 {
1515 break;
1516 }
1517 else if ((c == '+' || c == '-') && ! exp_wait_sign)
1518 {
1519 sign = c == '+' ? 1 : -1;
1520 val = 0;
1521 in_num = TRUE;
1522 in_frac = FALSE;
1523 in_exp = FALSE;
1524 exp = 0;
1525 exp_sign = 1;
1526 exp_wait_sign = FALSE;
1527 }
1528 else if (c == 'z' || c == 'Z')
1529 {
1530 if (ctx.param)
1531 parse_path_do_cmd (&ctx, TRUE);
1532
1533 if (ctx.stroke)
1534 gimp_stroke_close (ctx.stroke);
1535 }
1536 else if (c >= 'A' && c <= 'Z' && c != 'E')
1537 {
1538 if (ctx.param)
1539 parse_path_do_cmd (&ctx, TRUE);
1540
1541 ctx.cmd = c + 'a' - 'A';
1542 ctx.rel = FALSE;
1543 }
1544 else if (c >= 'a' && c <= 'z' && c != 'e')
1545 {
1546 if (ctx.param)
1547 parse_path_do_cmd (&ctx, TRUE);
1548
1549 ctx.cmd = c;
1550 ctx.rel = TRUE;
1551 }
1552 /* else c _should_ be whitespace or , */
1553 }
1554
1555 return g_list_reverse (ctx.strokes);
1556 }
1557
1558 /* supply defaults for missing parameters, assuming relative coordinates
1559 are to be interpreted as x,y */
1560 static void
parse_path_default_xy(ParsePathContext * ctx,gint n_params)1561 parse_path_default_xy (ParsePathContext *ctx,
1562 gint n_params)
1563 {
1564 gint i;
1565
1566 if (ctx->rel)
1567 {
1568 for (i = ctx->param; i < n_params; i++)
1569 {
1570 if (i > 2)
1571 ctx->params[i] = ctx->params[i - 2];
1572 else if (i == 1)
1573 ctx->params[i] = ctx->cpy;
1574 else if (i == 0)
1575 /* we shouldn't get here (ctx->param > 0 as precondition) */
1576 ctx->params[i] = ctx->cpx;
1577 }
1578 }
1579 else
1580 {
1581 for (i = ctx->param; i < n_params; i++)
1582 ctx->params[i] = 0.0;
1583 }
1584 }
1585
1586 static void
parse_path_do_cmd(ParsePathContext * ctx,gboolean final)1587 parse_path_do_cmd (ParsePathContext *ctx,
1588 gboolean final)
1589 {
1590 GimpCoords coords = COORDS_INIT;
1591
1592 switch (ctx->cmd)
1593 {
1594 case 'm':
1595 /* moveto */
1596 if (ctx->param == 2 || final)
1597 {
1598 parse_path_default_xy (ctx, 2);
1599
1600 coords.x = ctx->cpx = ctx->rpx = ctx->params[0];
1601 coords.y = ctx->cpy = ctx->rpy = ctx->params[1];
1602
1603 ctx->stroke = gimp_bezier_stroke_new_moveto (&coords);
1604 ctx->strokes = g_list_prepend (ctx->strokes, ctx->stroke);
1605
1606 ctx->param = 0;
1607
1608 /* If a moveto is followed by multiple pairs of coordinates,
1609 * the subsequent pairs are treated as implicit lineto commands.
1610 */
1611 ctx->cmd = 'l';
1612 }
1613 break;
1614
1615 case 'l':
1616 /* lineto */
1617 if (ctx->param == 2 || final)
1618 {
1619 parse_path_default_xy (ctx, 2);
1620
1621 coords.x = ctx->cpx = ctx->rpx = ctx->params[0];
1622 coords.y = ctx->cpy = ctx->rpy = ctx->params[1];
1623
1624 gimp_bezier_stroke_lineto (ctx->stroke, &coords);
1625
1626 ctx->param = 0;
1627 }
1628 break;
1629
1630 case 'c':
1631 /* curveto */
1632 if (ctx->param == 6 || final)
1633 {
1634 GimpCoords ctrl1 = COORDS_INIT;
1635 GimpCoords ctrl2 = COORDS_INIT;
1636
1637 parse_path_default_xy (ctx, 6);
1638
1639 ctrl1.x = ctx->params[0];
1640 ctrl1.y = ctx->params[1];
1641 ctrl2.x = ctx->rpx = ctx->params[2];
1642 ctrl2.y = ctx->rpy = ctx->params[3];
1643 coords.x = ctx->cpx = ctx->params[4];
1644 coords.y = ctx->cpy = ctx->params[5];
1645
1646 gimp_bezier_stroke_cubicto (ctx->stroke, &ctrl1, &ctrl2, &coords);
1647
1648 ctx->param = 0;
1649 }
1650 break;
1651
1652 case 's':
1653 /* smooth curveto */
1654 if (ctx->param == 4 || final)
1655 {
1656 GimpCoords ctrl1 = COORDS_INIT;
1657 GimpCoords ctrl2 = COORDS_INIT;
1658
1659 parse_path_default_xy (ctx, 4);
1660
1661 ctrl1.x = 2 * ctx->cpx - ctx->rpx;
1662 ctrl1.y = 2 * ctx->cpy - ctx->rpy;
1663 ctrl2.x = ctx->rpx = ctx->params[0];
1664 ctrl2.y = ctx->rpy = ctx->params[1];
1665 coords.x = ctx->cpx = ctx->params[2];
1666 coords.y = ctx->cpy = ctx->params[3];
1667
1668 gimp_bezier_stroke_cubicto (ctx->stroke, &ctrl1, &ctrl2, &coords);
1669
1670 ctx->param = 0;
1671 }
1672 break;
1673
1674 case 'h':
1675 /* horizontal lineto */
1676 if (ctx->param == 1)
1677 {
1678 coords.x = ctx->cpx = ctx->rpx = ctx->params[0];
1679 coords.y = ctx->cpy;
1680
1681 gimp_bezier_stroke_lineto (ctx->stroke, &coords);
1682
1683 ctx->param = 0;
1684 }
1685 break;
1686
1687 case 'v':
1688 /* vertical lineto */
1689 if (ctx->param == 1)
1690 {
1691 coords.x = ctx->cpx;
1692 coords.y = ctx->cpy = ctx->rpy = ctx->params[0];
1693
1694 gimp_bezier_stroke_lineto (ctx->stroke, &coords);
1695
1696 ctx->param = 0;
1697 }
1698 break;
1699
1700 case 'q':
1701 /* quadratic bezier curveto */
1702 if (ctx->param == 4 || final)
1703 {
1704 GimpCoords ctrl = COORDS_INIT;
1705
1706 parse_path_default_xy (ctx, 4);
1707
1708 ctrl.x = ctx->rpx = ctx->params[0];
1709 ctrl.y = ctx->rpy = ctx->params[1];
1710 coords.x = ctx->cpx = ctx->params[2];
1711 coords.y = ctx->cpy = ctx->params[3];
1712
1713 gimp_bezier_stroke_conicto (ctx->stroke, &ctrl, &coords);
1714
1715 ctx->param = 0;
1716 }
1717 break;
1718
1719 case 't':
1720 /* truetype quadratic bezier curveto */
1721 if (ctx->param == 2 || final)
1722 {
1723 GimpCoords ctrl = COORDS_INIT;
1724
1725 parse_path_default_xy (ctx, 2);
1726
1727 ctrl.x = ctx->rpx = 2 * ctx->cpx - ctx->rpx;
1728 ctrl.y = ctx->rpy = 2 * ctx->cpy - ctx->rpy;
1729 coords.x = ctx->cpx = ctx->params[0];
1730 coords.y = ctx->cpy = ctx->params[1];
1731
1732 gimp_bezier_stroke_conicto (ctx->stroke, &ctrl, &coords);
1733
1734 ctx->param = 0;
1735 }
1736 else if (final)
1737 {
1738 if (ctx->param > 2)
1739 {
1740 GimpCoords ctrl = COORDS_INIT;
1741
1742 parse_path_default_xy (ctx, 4);
1743
1744 ctrl.x = ctx->rpx = ctx->params[0];
1745 ctrl.y = ctx->rpy = ctx->params[1];
1746 coords.x = ctx->cpx = ctx->params[2];
1747 coords.y = ctx->cpy = ctx->params[3];
1748
1749 gimp_bezier_stroke_conicto (ctx->stroke, &ctrl, &coords);
1750 }
1751 else
1752 {
1753 parse_path_default_xy (ctx, 2);
1754
1755 coords.x = ctx->cpx = ctx->rpx = ctx->params[0];
1756 coords.y = ctx->cpy = ctx->rpy = ctx->params[1];
1757
1758 gimp_bezier_stroke_lineto (ctx->stroke, &coords);
1759 }
1760
1761 ctx->param = 0;
1762 }
1763 break;
1764
1765 case 'a':
1766 if (ctx->param == 7 || final)
1767 {
1768 coords.x = ctx->cpx = ctx->rpx = ctx->params[5];
1769 coords.y = ctx->cpy = ctx->rpy = ctx->params[6];
1770
1771 gimp_bezier_stroke_arcto (ctx->stroke,
1772 ctx->params[0], ctx->params[1],
1773 gimp_deg_to_rad (ctx->params[2]),
1774 ctx->params[3], ctx->params[4],
1775 &coords);
1776 ctx->param = 0;
1777 }
1778 break;
1779
1780 default:
1781 ctx->param = 0;
1782 break;
1783 }
1784 }
1785