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, &center.x);
861           else if (strcmp (*names, "cy") == 0)
862             parse_svg_length (*values, handler->height, yres, &center.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 (&center,
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