gimpvectors-import.c 49.7 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2 3
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
4
 * GimpVectors Import
5
 * Copyright (C) 2003-2004  Sven Neumann <sven@gimp.org>
6 7 8 9
 *
 * Some code here is based on code from librsvg that was originally
 * written by Raph Levien <raph@artofcode.com> for Gill.
 *
10 11 12 13 14 15
 * This SVG path importer implements a subset of SVG that is
 * sufficient to parse path elements and basic shapes and to apply
 * transformations as described by the SVG specification:
 * http://www.w3.org/TR/SVG/.  It must handle the SVG files exported
 * by GIMP but it is also supposed to be able to extract paths and
 * 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 21 22 23 24 25 26 27
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * 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 32 33
 */

#include "config.h"

#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 41
#include "libgimpmath/gimpmath.h"

42 43
#include "vectors-types.h"

44 45
#include "config/gimpxmlparser.h"

46
#include "core/gimperror.h"
47
#include "core/gimpimage.h"
48
#include "core/gimpimage-undo.h"
49

50
#include "gimpbezierstroke.h"
51 52 53 54 55 56 57
#include "gimpstroke.h"
#include "gimpvectors.h"
#include "gimpvectors-import.h"

#include "gimp-intl.h"


58 59 60 61 62 63 64 65 66 67 68 69 70
#define COORDS_INIT   \
  {                   \
    .x         = 0.0, \
    .y         = 0.0, \
    .pressure  = 1.0, \
    .xtilt     = 0.0, \
    .ytilt     = 0.0, \
    .wheel     = 0.5, \
    .velocity  = 0.0, \
    .direction = 0.0  \
  }


71 72
typedef struct
{
73 74 75 76
  GQueue       *stack;
  GimpImage    *image;
  gboolean      scale;
  gint          svg_depth;
77 78
} SvgParser;

79

80
typedef struct _SvgHandler SvgHandler;
81

82
struct _SvgHandler
83
{
84
  const gchar  *name;
85 86 87 88 89

  void (* start) (SvgHandler   *handler,
                  const gchar **names,
                  const gchar **values,
                  SvgParser    *parser);
Sven Neumann's avatar
Sven Neumann committed
90 91
  void (* end)   (SvgHandler   *handler,
                  SvgParser    *parser);
92

93
  gdouble       width;
94
  gdouble       height;
95
  gchar        *id;
96
  GList        *paths;
97 98 99
  GimpMatrix3  *transform;
};

100

101 102 103 104 105 106
typedef struct
{
  gchar        *id;
  GList        *strokes;
} SvgPath;

107

108
static gboolean  gimp_vectors_import  (GimpImage            *image,
109
                                       GFile                *file,
110
                                       const gchar          *str,
111
                                       gsize                 len,
112 113
                                       gboolean              merge,
                                       gboolean              scale,
114
                                       GimpVectors          *parent,
115
                                       gint                  position,
116
                                       GList               **ret_vectors,
117 118
                                       GError              **error);

119 120 121 122 123 124 125 126 127 128
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);
static void  svg_parser_end_element   (GMarkupParseContext  *context,
                                       const gchar          *element_name,
                                       gpointer              user_data,
                                       GError              **error);
129 130 131

static const GMarkupParser markup_parser =
{
132 133
  svg_parser_start_element,
  svg_parser_end_element,
134 135 136 137 138 139
  NULL,  /*  characters   */
  NULL,  /*  passthrough  */
  NULL   /*  error        */
};


140 141 142 143 144 145 146 147 148 149 150 151 152 153
static void  svg_handler_svg_start     (SvgHandler   *handler,
                                        const gchar **names,
                                        const gchar **values,
                                        SvgParser    *parser);
static void  svg_handler_svg_end       (SvgHandler   *handler,
                                        SvgParser    *parser);
static void  svg_handler_group_start   (SvgHandler   *handler,
                                        const gchar **names,
                                        const gchar **values,
                                        SvgParser    *parser);
static void  svg_handler_path_start    (SvgHandler   *handler,
                                        const gchar **names,
                                        const gchar **values,
                                        SvgParser    *parser);
154 155 156 157
static void  svg_handler_rect_start    (SvgHandler   *handler,
                                        const gchar **names,
                                        const gchar **values,
                                        SvgParser    *parser);
158 159 160 161 162 163 164 165 166 167 168 169
static void  svg_handler_ellipse_start (SvgHandler   *handler,
                                        const gchar **names,
                                        const gchar **values,
                                        SvgParser    *parser);
static void  svg_handler_line_start    (SvgHandler   *handler,
                                        const gchar **names,
                                        const gchar **values,
                                        SvgParser    *parser);
static void  svg_handler_poly_start    (SvgHandler   *handler,
                                        const gchar **names,
                                        const gchar **values,
                                        SvgParser    *parser);
170

171
static const SvgHandler svg_handlers[] =
172
{
173 174 175
  { "svg",      svg_handler_svg_start,     svg_handler_svg_end },
  { "g",        svg_handler_group_start,   NULL                },
  { "path",     svg_handler_path_start,    NULL                },
176
  { "rect",     svg_handler_rect_start,    NULL                },
177 178 179 180 181
  { "circle",   svg_handler_ellipse_start, NULL                },
  { "ellipse",  svg_handler_ellipse_start, NULL                },
  { "line",     svg_handler_line_start,    NULL                },
  { "polyline", svg_handler_poly_start,    NULL                },
  { "polygon",  svg_handler_poly_start,    NULL                }
182 183 184
};


185 186 187 188
static gboolean   parse_svg_length    (const gchar  *value,
                                       gdouble       reference,
                                       gdouble       resolution,
                                       gdouble      *length);
189
static gboolean   parse_svg_viewbox   (const gchar  *value,
190 191
                                       gdouble      *width,
                                       gdouble      *height,
192 193 194 195 196 197
                                       GimpMatrix3  *matrix);
static gboolean   parse_svg_transform (const gchar  *value,
                                       GimpMatrix3  *matrix);
static GList    * parse_path_data     (const gchar  *data);


198
/**
199
 * gimp_vectors_import_file:
200
 * @image:    the #GimpImage to add the paths to
201
 * @file:     a SVG file
202 203 204 205
 * @merge:    should multiple paths be merged into a single #GimpVectors object
 * @scale:    should the SVG be scaled to fit the image dimensions
 * @position: position in the image's vectors stack where to add the vectors
 * @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
212
gimp_vectors_import_file (GimpImage    *image,
213
                          GFile        *file,
214 215
                          gboolean      merge,
                          gboolean      scale,
216
                          GimpVectors  *parent,
217
                          gint          position,
218
                          GList       **ret_vectors,
219 220 221
                          GError      **error)
{
  g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
222
  g_return_val_if_fail (G_IS_FILE (file), FALSE);
223 224 225
  g_return_val_if_fail (parent == NULL ||
                        parent == GIMP_IMAGE_ACTIVE_PARENT ||
                        GIMP_IS_VECTORS (parent), FALSE);
226 227 228 229 230 231 232 233 234 235 236
  g_return_val_if_fail (parent == NULL ||
                        parent == GIMP_IMAGE_ACTIVE_PARENT ||
                        gimp_item_is_attached (GIMP_ITEM (parent)), FALSE);
  g_return_val_if_fail (parent == NULL ||
                        parent == GIMP_IMAGE_ACTIVE_PARENT ||
                        gimp_item_get_image (GIMP_ITEM (parent)) == image,
                        FALSE);
  g_return_val_if_fail (parent == NULL ||
                        parent == GIMP_IMAGE_ACTIVE_PARENT ||
                        gimp_viewable_get_children (GIMP_VIEWABLE (parent)),
                        FALSE);
237
  g_return_val_if_fail (ret_vectors == NULL || *ret_vectors == NULL, FALSE);
238 239
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

240
  return gimp_vectors_import (image, file, NULL, 0, merge, scale,
241
                              parent, position,
242
                              ret_vectors, error);
243 244 245 246 247 248 249 250 251 252 253
}

/**
 * gimp_vectors_import_string:
 * @image:  the #GimpImage to add the paths to
 * @buffer: a character buffer to parse
 * @len:    number of bytes in @str or -1 if @str is %NUL-terminated
 * @merge:  should multiple paths be merged into a single #GimpVectors object
 * @scale:  should the SVG be scaled to fit the image dimensions
 * @error:  location to store possible errors
 *
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 259 260
 **/
gboolean
gimp_vectors_import_buffer (GimpImage    *image,
                            const gchar  *buffer,
261
                            gsize         len,
262 263
                            gboolean      merge,
                            gboolean      scale,
264
                            GimpVectors  *parent,
265
                            gint          position,
266
                            GList       **ret_vectors,
267 268 269 270
                            GError      **error)
{
  g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
  g_return_val_if_fail (buffer != NULL || len == 0, FALSE);
271 272 273
  g_return_val_if_fail (parent == NULL ||
                        parent == GIMP_IMAGE_ACTIVE_PARENT ||
                        GIMP_IS_VECTORS (parent), FALSE);
274 275 276 277 278 279 280 281 282 283 284
  g_return_val_if_fail (parent == NULL ||
                        parent == GIMP_IMAGE_ACTIVE_PARENT ||
                        gimp_item_is_attached (GIMP_ITEM (parent)), FALSE);
  g_return_val_if_fail (parent == NULL ||
                        parent == GIMP_IMAGE_ACTIVE_PARENT ||
                        gimp_item_get_image (GIMP_ITEM (parent)) == image,
                        FALSE);
  g_return_val_if_fail (parent == NULL ||
                        parent == GIMP_IMAGE_ACTIVE_PARENT ||
                        gimp_viewable_get_children (GIMP_VIEWABLE (parent)),
                        FALSE);
285
  g_return_val_if_fail (ret_vectors == NULL || *ret_vectors == NULL, FALSE);
286 287
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

288 289
  return gimp_vectors_import (image, NULL, buffer, len, merge, scale,
                              parent, position,
290
                              ret_vectors, error);
291 292 293
}

static gboolean
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 304
                     GError      **error)
{
305 306
  GimpXmlParser *xml_parser;
  SvgParser      parser;
307 308 309
  GList         *paths;
  SvgHandler    *base;
  gboolean       success = TRUE;
310

Sven Neumann's avatar
Sven Neumann committed
311 312 313 314
  parser.stack     = g_queue_new ();
  parser.image     = image;
  parser.scale     = scale;
  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 320
  base->width  = gimp_image_get_width  (image);
  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 327
  if (file)
    success = gimp_xml_parser_parse_gfile (xml_parser, file, error);
328 329
  else
    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 342
          merge = merge && base->paths->next;

343 344 345
          gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_VECTORS_IMPORT,
                                       _("Import Paths"));

346
          for (paths = base->paths; paths; paths = paths->next)
347
            {
348 349
              SvgPath *path = paths->data;
              GList   *list;
350

Sven Neumann's avatar
Sven Neumann committed
351
              if (! merge || ! vectors)
352
                {
353
                  vectors = gimp_vectors_new (image,
Sven Neumann's avatar
Sven Neumann committed
354
                                              ((merge || ! path->id) ?
355
                                               _("Imported Path") : path->id));
356 357
                  gimp_image_add_vectors (image, vectors,
                                          parent, position, TRUE);
358
                  gimp_vectors_freeze (vectors);
359

360 361 362
                  if (ret_vectors)
                    *ret_vectors = g_list_prepend (*ret_vectors, vectors);

363 364
                  if (position != -1)
                    position++;
365 366
                }

367 368 369
              for (list = path->strokes; list; list = list->next)
                gimp_vectors_stroke_add (vectors, GIMP_STROKE (list->data));

Sven Neumann's avatar
Sven Neumann committed
370
              if (! merge)
371 372
                gimp_vectors_thaw (vectors);

Massimo Valentini's avatar
Massimo Valentini committed
373
              g_list_free_full (path->strokes, g_object_unref);
374
              path->strokes = NULL;
375
            }
376

377 378
          if (merge)
            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 393 394
          success = FALSE;
        }
    }
395
  else if (error && *error && file) /*  parser reported an error  */
396 397 398 399 400
    {
      gchar *msg = (*error)->message;

      (*error)->message =
        g_strdup_printf (_("Failed to import paths from '%s': %s"),
401
                         gimp_file_get_utf8_name (file), msg);
402 403 404

      g_free (msg);
    }
405

406
  while ((base = g_queue_pop_head (parser.stack)) != NULL)
407 408 409
    {
      for (paths = base->paths; paths; paths = paths->next)
        {
410 411 412
          SvgPath *path = paths->data;
          GList   *list;

413 414
          g_free (path->id);

415
          for (list = path->strokes; list; list = list->next)
416 417
            g_object_unref (list->data);

418
          g_list_free (path->strokes);
419

420
          g_slice_free (SvgPath, path);
421 422 423
        }

      g_list_free (base->paths);
424

425 426
      g_slice_free (GimpMatrix3, base->transform);
      g_slice_free (SvgHandler, base);
427 428
    }

429
  g_queue_free (parser.stack);
430

431 432
  return success;
}
433

434
static void
435 436 437 438 439 440
svg_parser_start_element (GMarkupParseContext  *context,
                          const gchar          *element_name,
                          const gchar         **attribute_names,
                          const gchar         **attribute_values,
                          gpointer              user_data,
                          GError              **error)
441
{
442 443
  SvgParser  *parser = user_data;
  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 451
  /* if the element is not rendered, always use the generic handler */
  if (base->width <= 0.0 || base->height <= 0.0)
452 453 454 455 456 457 458 459 460
    i = G_N_ELEMENTS (svg_handlers);

  for (; i < G_N_ELEMENTS (svg_handlers); i++)
    if (strcmp (svg_handlers[i].name, element_name) == 0)
      {
        handler->name  = svg_handlers[i].name;
        handler->start = svg_handlers[i].start;
        break;
      }
461

462 463
  handler->width  = base->width;
  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
472 473 474 475
svg_parser_end_element (GMarkupParseContext  *context,
                        const gchar          *element_name,
                        gpointer              user_data,
                        GError              **error)
476
{
477
  SvgParser  *parser = user_data;
478 479
  SvgHandler *handler;
  SvgHandler *base;
480
  GList      *paths;
481

482 483 484 485 486
  handler = g_queue_pop_head (parser->stack);

  g_return_if_fail (handler != NULL &&
                    (handler->name == NULL ||
                     strcmp (handler->name, element_name) == 0));
487

Sven Neumann's avatar
Sven Neumann committed
488 489 490
  if (handler->end)
    handler->end (handler, parser);

491
  if (handler->paths)
492 493 494
    {
      if (handler->transform)
        {
495 496
          for (paths = handler->paths; paths; paths = paths->next)
            {
497 498
              SvgPath *path = paths->data;
              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
Sven Neumann's avatar
Sven Neumann committed
516 517 518 519
svg_handler_svg_start (SvgHandler   *handler,
                       const gchar **names,
                       const gchar **values,
                       SvgParser    *parser)
520
{
521 522 523 524 525 526 527
  GimpMatrix3 *matrix;
  GimpMatrix3  box;
  const gchar *viewbox = NULL;
  gdouble      x = 0;
  gdouble      y = 0;
  gdouble      w = handler->width;
  gdouble      h = handler->height;
528 529
  gdouble      xres;
  gdouble      yres;
530

531
  matrix = g_slice_new (GimpMatrix3);
532 533
  gimp_matrix3_identity (matrix);

534 535
  gimp_image_get_resolution (parser->image, &xres, &yres);

536 537
  while (*names)
    {
538
      switch (*names[0])
539
        {
540 541
        case 'x':
          if (strcmp (*names, "x") == 0)
542
            parse_svg_length (*values, handler->width, xres, &x);
543 544 545 546
          break;

        case 'y':
          if (strcmp (*names, "y") == 0)
547
            parse_svg_length (*values, handler->height, yres, &y);
548 549 550 551
          break;

        case 'w':
          if (strcmp (*names, "width") == 0)
552
            parse_svg_length (*values, handler->width, xres, &w);
553 554 555 556
          break;

        case 'h':
          if (strcmp (*names, "height") == 0)
557
            parse_svg_length (*values, handler->height, yres, &h);
558 559 560 561 562 563
          break;

        case 'v':
          if (strcmp (*names, "viewBox") == 0)
            viewbox = *values;
          break;
564 565
        }

566 567 568
      names++;
      values++;
    }
569 570 571 572

  if (x || y)
    {
      /* according to the spec offsets are meaningless on the outermost svg */
Sven Neumann's avatar
Sven Neumann committed
573
      if (parser->svg_depth > 0)
574 575 576
        gimp_matrix3_translate (matrix, x, y);
    }

577 578 579 580 581
  if (viewbox && parse_svg_viewbox (viewbox, &w, &h, &box))
    {
      gimp_matrix3_mult (&box, matrix);
    }

582
  /*  optionally scale the outermost svg to image size  */
Sven Neumann's avatar
Sven Neumann committed
583
  if (parser->scale && parser->svg_depth == 0)
584 585
    {
      if (w > 0.0 && h > 0.0)
Sven Neumann's avatar
Sven Neumann committed
586
        gimp_matrix3_scale (matrix,
587 588
                            gimp_image_get_width  (parser->image) / w,
                            gimp_image_get_height (parser->image) / h);
589 590
    }

591 592
  handler->width  = w;
  handler->height = h;
593 594

  handler->transform = matrix;
Sven Neumann's avatar
Sven Neumann committed
595 596

  parser->svg_depth++;
597 598 599
}

static void
Sven Neumann's avatar
Sven Neumann committed
600 601 602 603 604 605 606 607 608 609 610
svg_handler_svg_end (SvgHandler   *handler,
                     SvgParser    *parser)
{
  parser->svg_depth--;
}

static void
svg_handler_group_start (SvgHandler   *handler,
                         const gchar **names,
                         const gchar **values,
                         SvgParser    *parser)
611
{
612 613
  while (*names)
    {
Sven Neumann's avatar
Sven Neumann committed
614
      if (strcmp (*names, "transform") == 0 && ! handler->transform)
615 616
        {
          GimpMatrix3  matrix;
617

618
          if (parse_svg_transform (*values, &matrix))
619
            {
Michael Natterer's avatar
Michael Natterer committed
620
              handler->transform = g_slice_dup (GimpMatrix3, &matrix);
621 622

#ifdef DEBUG_VECTORS_IMPORT
623 624
              g_printerr ("transform %s: %g %g %g   %g %g %g   %g %g %g\n",
                          handler->id ? handler->id : "(null)",
625 626 627 628 629 630 631 632 633 634 635
                          handler->transform->coeff[0][0],
                          handler->transform->coeff[0][1],
                          handler->transform->coeff[0][2],
                          handler->transform->coeff[1][0],
                          handler->transform->coeff[1][1],
                          handler->transform->coeff[1][2],
                          handler->transform->coeff[2][0],
                          handler->transform->coeff[2][1],
                          handler->transform->coeff[2][2]);
#endif
            }
636
        }
637

638 639 640
      names++;
      values++;
    }
641 642
}

643
static void
Sven Neumann's avatar
Sven Neumann committed
644 645 646 647
svg_handler_path_start (SvgHandler   *handler,
                        const gchar **names,
                        const gchar **values,
                        SvgParser    *parser)
648
{
649
  SvgPath *path = g_slice_new0 (SvgPath);
650

651
  while (*names)
652
    {
653
      switch (*names[0])
654
        {
655
        case 'i':
Sven Neumann's avatar
Sven Neumann committed
656
          if (strcmp (*names, "id") == 0 && ! path->id)
657 658 659 660
            path->id = g_strdup (*values);
          break;

        case 'd':
Sven Neumann's avatar
Sven Neumann committed
661
          if (strcmp (*names, "d") == 0 && ! path->strokes)
662 663 664 665
            path->strokes = parse_path_data (*values);
          break;

        case 't':
Sven Neumann's avatar
Sven Neumann committed
666
          if (strcmp (*names, "transform") == 0 && ! handler->transform)
667 668
            {
              GimpMatrix3  matrix;
669

670
              if (parse_svg_transform (*values, &matrix))
671
                {
Michael Natterer's avatar
Michael Natterer committed
672
                  handler->transform = g_slice_dup (GimpMatrix3, &matrix);
673
                }
674 675
            }
          break;
676
        }
677

678 679 680
      names++;
      values++;
    }
681 682

  handler->paths = g_list_prepend (handler->paths, path);
683
}
684

685 686 687 688 689 690
static void
svg_handler_rect_start (SvgHandler   *handler,
                        const gchar **names,
                        const gchar **values,
                        SvgParser    *parser)
{
691 692 693 694 695 696 697 698 699 700 701
  SvgPath *path   = g_slice_new0 (SvgPath);
  gdouble  x      = 0.0;
  gdouble  y      = 0.0;
  gdouble  width  = 0.0;
  gdouble  height = 0.0;
  gdouble  rx     = 0.0;
  gdouble  ry     = 0.0;
  gdouble  xres;
  gdouble  yres;

  gimp_image_get_resolution (parser->image, &xres, &yres);
702 703 704

  while (*names)
    {
705
      switch (*names[0])
706
        {
707
        case 'i':
Sven Neumann's avatar
Sven Neumann committed
708
          if (strcmp (*names, "id") == 0 && ! path->id)
709 710 711 712 713
            path->id = g_strdup (*values);
          break;

        case 'x':
          if (strcmp (*names, "x") == 0)
714
            parse_svg_length (*values, handler->width, xres, &x);
715 716 717 718
          break;

        case 'y':
          if (strcmp (*names, "y") == 0)
719
            parse_svg_length (*values, handler->height, yres, &y);
720 721 722 723
          break;

        case 'w':
          if (strcmp (*names, "width") == 0)
724
            parse_svg_length (*values, handler->width, xres, &width);
725 726 727 728
          break;

        case 'h':
          if (strcmp (*names, "height") == 0)
729
            parse_svg_length (*values, handler->height, yres, &height);
730 731 732 733
          break;

        case 'r':
          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 738 739
          break;

        case 't':
Sven Neumann's avatar
Sven Neumann committed
740
          if (strcmp (*names, "transform") == 0 && ! handler->transform)
741 742
            {
              GimpMatrix3  matrix;
743

744
              if (parse_svg_transform (*values, &matrix))
745
                {
Michael Natterer's avatar
Michael Natterer committed
746
                  handler->transform = g_slice_dup (GimpMatrix3, &matrix);
747
                }
748 749
            }
          break;
750 751 752 753 754 755 756 757 758
        }

      names++;
      values++;
    }

  if (width > 0.0 && height > 0.0 && rx >= 0.0 && ry >= 0.0)
    {
      GimpStroke *stroke;
759
      GimpCoords  point = COORDS_INIT;
760 761 762 763 764 765 766 767 768 769 770

      if (rx == 0.0)
        rx = ry;
      if (ry == 0.0)
        ry = rx;

      rx = MIN (rx, width / 2);
      ry = MIN (ry, height / 2);

      point.x = x + width - rx;
      point.y = y;
771
      stroke = gimp_bezier_stroke_new_moveto (&point);
772 773 774

      if (rx)
        {
775
          GimpCoords  end = COORDS_INIT;
776 777 778 779

          end.x = x + width;
          end.y = y + ry;

780 781 782 783 784 785 786 787 788
          gimp_bezier_stroke_arcto (stroke, rx, ry, 0, FALSE, TRUE, &end);
        }

      point.x = x + width;
      point.y = y + height - ry;
      gimp_bezier_stroke_lineto (stroke, &point);

      if (rx)
        {
789
          GimpCoords  end = COORDS_INIT;
790 791 792 793

          end.x = x + width - rx;
          end.y = y + height;

794 795 796 797 798 799 800 801 802
          gimp_bezier_stroke_arcto (stroke, rx, ry, 0, FALSE, TRUE, &end);
        }

      point.x = x + rx;
      point.y = y + height;
      gimp_bezier_stroke_lineto (stroke, &point);

      if (rx)
        {
803
          GimpCoords  end = COORDS_INIT;
804 805 806 807

          end.x = x;
          end.y = y + height - ry;

808 809 810 811 812 813 814 815 816
          gimp_bezier_stroke_arcto (stroke, rx, ry, 0, FALSE, TRUE, &end);
        }

      point.x = x;
      point.y = y + ry;
      gimp_bezier_stroke_lineto (stroke, &point);

      if (rx)
        {
817
          GimpCoords  end = COORDS_INIT;
818 819 820 821

          end.x = x + rx;
          end.y = y;

822 823 824
          gimp_bezier_stroke_arcto (stroke, rx, ry, 0, FALSE, TRUE, &end);
        }

825 826 827
      /* the last line is handled by closing the stroke */
      gimp_stroke_close (stroke);

828 829 830 831 832 833
      path->strokes = g_list_prepend (path->strokes, stroke);
    }

  handler->paths = g_list_prepend (handler->paths, path);
}

834 835 836 837 838 839
static void
svg_handler_ellipse_start (SvgHandler   *handler,
                           const gchar **names,
                           const gchar **values,
                           SvgParser    *parser)
{
840
  SvgPath    *path   = g_slice_new0 (SvgPath);
841
  GimpCoords  center = COORDS_INIT;
842 843
  gdouble     rx     = 0.0;
  gdouble     ry     = 0.0;
844 845 846 847
  gdouble     xres;
  gdouble     yres;

  gimp_image_get_resolution (parser->image, &xres, &yres);
848 849 850

  while (*names)
    {
851
      switch (*names[0])
852
        {
853
        case 'i':
Sven Neumann's avatar
Sven Neumann committed
854
          if (strcmp (*names, "id") == 0 && ! path->id)
855 856 857 858 859
            path->id = g_strdup (*values);
          break;

        case 'c':
          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 864 865 866
          break;

        case 'r':
          if (strcmp (*names, "r") == 0)
867
            {
868 869
              parse_svg_length (*values, handler->width,  xres, &rx);
              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 880 881
          break;

        case 't':
Sven Neumann's avatar
Sven Neumann committed
882
          if (strcmp (*names, "transform") == 0 && ! handler->transform)
883 884
            {
              GimpMatrix3  matrix;
885

886
              if (parse_svg_transform (*values, &matrix))
887
                {
Michael Natterer's avatar
Michael Natterer committed
888
                  handler->transform = g_slice_dup (GimpMatrix3, &matrix);
889
                }
890 891
            }
          break;
892 893 894 895 896 897 898 899 900
        }

      names++;
      values++;
    }

  if (rx >= 0.0 && ry >= 0.0)
    path->strokes = g_list_prepend (path->strokes,
                                    gimp_bezier_stroke_new_ellipse (&center,
901 902
                                                                    rx, ry,
                                                                    0.0));
903 904 905 906

  handler->paths = g_list_prepend (handler->paths, path);
}

907 908 909 910 911 912
static void
svg_handler_line_start (SvgHandler   *handler,
                        const gchar **names,
                        const gchar **values,
                        SvgParser    *parser)
{
913
  SvgPath    *path  = g_slice_new0 (SvgPath);
914 915
  GimpCoords  start = COORDS_INIT;
  GimpCoords  end   = COORDS_INIT;
916
  GimpStroke *stroke;
917 918 919 920
  gdouble     xres;
  gdouble     yres;

  gimp_image_get_resolution (parser->image, &xres, &yres);
921 922 923

  while (*names)
    {
924
      switch (*names[0])
925
        {
926
        case 'i':
Sven Neumann's avatar
Sven Neumann committed
927
          if (strcmp (*names, "id") == 0 && ! path->id)
928 929 930 931 932
            path->id = g_strdup (*values);
          break;

        case 'x':
          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 937 938 939
          break;

        case 'y':
          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 944 945
          break;

        case 't':
Sven Neumann's avatar
Sven Neumann committed
946
          if (strcmp (*names, "transform") == 0 && ! handler->transform)
947 948
            {
              GimpMatrix3  matrix;
949

950
              if (parse_svg_transform (*values, &matrix))
951
                {
Michael Natterer's avatar
Michael Natterer committed
952
                  handler->transform = g_slice_dup (GimpMatrix3, &matrix);
953
                }
954 955
            }
          break;
956 957 958 959 960 961 962 963 964 965 966 967 968 969
        }

      names++;
      values++;
    }

  stroke = gimp_bezier_stroke_new_moveto (&start);
  gimp_bezier_stroke_lineto (stroke, &end);

  path->strokes = g_list_prepend (path->strokes, stroke);

  handler->paths = g_list_prepend (handler->paths, path);
}

970 971 972 973 974 975
static void
svg_handler_poly_start (SvgHandler   *handler,
                        const gchar **names,
                        const gchar **values,
                        SvgParser    *parser)
{
976
  SvgPath *path   = g_slice_new0 (SvgPath);
977 978 979 980
  GString *points = NULL;

  while (*names)
    {
981
      switch (*names[0])
982
        {
983
        case 'i':
Sven Neumann's avatar
Sven Neumann committed
984
          if (strcmp (*names, "id") == 0 && ! path->id)
985 986
            path->id = g_strdup (*values);
          break;
987

988
        case 'p':
Sven Neumann's avatar
Sven Neumann committed
989
          if (strcmp (*names, "points") == 0 && ! points)
990
            {
991 992 993 994
              const gchar *p = *values;
              const gchar *m = NULL;
              const gchar *l = NULL;
              gint         n = 0;
995

996
              while (*p)
997
                {
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
                  while (g_ascii_isspace (*p) || *p == ',')
                    p++;

                  switch (n)
                    {
                    case 0:
                      m = p;
                      break;
                    case 2:
                      l = p;
                      break;
                    }

1011 1012 1013
                  if (*p)
                    n++;

Sven Neumann's avatar
Sven Neumann committed
1014
                  while (*p && ! g_ascii_isspace (*p) && *p != ',')
1015
                    p++;
1016 1017
                }

1018 1019 1020
              if ((n > 3) && (n % 2 == 0))
                {
                  points = g_string_sized_new (p - *values + 8);
1021

1022 1023
                  g_string_append_len (points, "M ", 2);
                  g_string_append_len (points, m, l - m);
1024

1025 1026
                  g_string_append_len (points, "L ", 2);
                  g_string_append_len (points, l, p - l);
1027

1028 1029 1030 1031 1032
                  if (strcmp (handler->name, "polygon") == 0)
                    g_string_append_c (points, 'Z');
                }
            }
          break;
1033

1034
        case 't':
Sven Neumann's avatar
Sven Neumann committed
1035
          if (strcmp (*names, "transform") == 0 && ! handler->transform)
1036 1037
            {
              GimpMatrix3  matrix;
1038

1039
              if (parse_svg_transform (*values, &matrix))
1040
                {
Michael Natterer's avatar
Michael Natterer committed
1041
                  handler->transform = g_slice_dup (GimpMatrix3, &matrix);
1042
                }
1043
            }
1044
          break;
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
        }

      names++;
      values++;
    }

  if (points)
    {
      path->strokes = parse_path_data (points->str);
      g_string_free (points, TRUE);
    }

  handler->paths = g_list_prepend (handler->paths, path);
}

1060
static gboolean
1061 1062 1063 1064
parse_svg_length (const gchar *value,
                  gdouble      reference,
                  gdouble      resolution,
                  gdouble     *length)
1065
{
1066 1067 1068
  GimpUnit  unit = GIMP_UNIT_PIXEL;
  gdouble   len;
  gchar    *ptr;
1069

1070
  len = g_ascii_strtod (value, &ptr);
1071

1072 1073
  while (g_ascii_isspace (*ptr))
    ptr++;
1074

1075
  switch (ptr[0])
1076
    {
1077 1078 1079 1080 1081
    case '\0':
      break;

    case 'p':
      switch (ptr[1])
1082
        {
1083 1084 1085 1086 1087
        case 'x':                         break;
        case 't': unit = GIMP_UNIT_POINT; break;
        case 'c': unit = GIMP_UNIT_PICA;  break;
        default:
          return FALSE;
1088
        }
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
      ptr += 2;
      break;

    case 'c':
      if (ptr[1] == 'm')
        len *= 10.0, unit = GIMP_UNIT_MM;
      else
        return FALSE;
      ptr += 2;
      break;

    case 'm':
      if (ptr[1] == 'm')
        unit = GIMP_UNIT_MM;
      else
        return FALSE;
      ptr += 2;
      break;

    case 'i':
      if (ptr[1] == 'n')
        unit = GIMP_UNIT_INCH;
      else
        return FALSE;
      ptr += 2;
      break;

    case '%':
      unit = GIMP_UNIT_PERCENT;
      ptr += 1;
      break;

    default:
      return FALSE;
1123 1124
    }

1125 1126
  while (g_ascii_isspace (*ptr))
    ptr++;
1127

1128
  if (*ptr)
1129 1130
    return FALSE;

1131 1132 1133 1134 1135 1136 1137 1138 1139
  switch (unit)
    {
    case GIMP_UNIT_PERCENT:
      *length = len * reference / 100.0;
      break;

    case GIMP_UNIT_PIXEL:
      *length = len;
      break;
1140 1141

    default:
Sven Neumann's avatar
Sven Neumann committed
1142 1143
      *length = len * resolution / gimp_unit_get_factor (unit);
      break;
1144 1145 1146 1147 1148 1149 1150
    }

  return TRUE;
}

static gboolean
parse_svg_viewbox (const gchar *value,
1151 1152
                   gdouble     *width,
                   gdouble     *height,
1153 1154
                   GimpMatrix3 *matrix)
{
1155 1156 1157 1158
  gdouble   x, y, w, h;
  gchar    *tok;
  gchar    *str     = g_strdup (value);
  gboolean  success = FALSE;
1159

1160
  x = y = w = h = 0;
1161

1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
  tok = strtok (str, ", \t");
  if (tok)
    {
      x = g_ascii_strtod (tok, NULL);
      tok = strtok (NULL, ", \t");
      if (tok)
        {
          y = g_ascii_strtod (tok, NULL);
          tok = strtok (NULL, ", \t");
          if (tok != NULL)
            {
              w = g_ascii_strtod (tok, NULL);
              tok = strtok (NULL, ", \t");
              if (tok)
                {
                  h = g_ascii_strtod (tok, NULL);
                  success = TRUE;
                }
            }
        }
1182 1183
    }

1184
  g_free (str);
1185

1186 1187 1188
  if (success)
    {
      gimp_matrix3_identity (matrix);
1189
      gimp_matrix3_translate (matrix, -x,  -y);
1190

1191 1192 1193 1194 1195 1196
      if (w > 0.0 && h > 0.0)
        {
          gimp_matrix3_scale (matrix, *width / w, *height / h);
        }
      else  /* disable rendering of the element */
        {
1197
#ifdef DEBUG_VECTORS_IMPORT
Sven Neumann's avatar
Sven Neumann committed
1198
          g_printerr ("empty viewBox");
1199
#endif
1200 1201 1202
          *width = *height = 0.0;
        }
    }
1203 1204 1205 1206
  else
    {
      g_printerr ("SVG import: cannot parse viewBox attribute\n");
    }
1207

1208
  return success;
1209 1210
}

1211
static gboolean
1212 1213 1214
parse_svg_transform (const gchar *value,
                     GimpMatrix3 *matrix)
{
1215
  gint i;
1216 1217 1218

  gimp_matrix3_identity (matrix);

1219
  for (i = 0; value[i]; i++)
1220
    {
1221 1222