file-svg.c 30.1 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2 3
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7 8 9 10 11 12 13 14
 * (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
15
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16 17
 */

18
/* SVG loader plug-in
19
 * (C) Copyright 2003  Dom Lachowicz <cinamod@hotmail.com>
Dom Lachowicz's avatar
Dom Lachowicz committed
20
 *
21
 * Largely rewritten in September 2003 by Sven Neumann <sven@gimp.org>
22 23 24 25
 */

#include "config.h"

26
#include <stdlib.h>
27
#include <string.h>
28 29

#include <librsvg/rsvg.h>
30

31 32
#include "libgimp/gimp.h"
#include "libgimp/gimpui.h"
33

34 35 36
#include "libgimp/stdplugins-intl.h"


37 38
#define LOAD_PROC               "file-svg-load"
#define LOAD_THUMB_PROC         "file-svg-load-thumb"
39
#define PLUG_IN_BINARY          "file-svg"
40
#define PLUG_IN_ROLE            "gimp-file-svg"
41
#define SVG_VERSION             "2.5.0"
42
#define SVG_DEFAULT_RESOLUTION  90.0
43
#define SVG_DEFAULT_SIZE        500
44
#define SVG_PREVIEW_SIZE        128
45

46

47 48
typedef struct
{
49 50 51 52 53
  gdouble    resolution;
  gint       width;
  gint       height;
  gboolean   import;
  gboolean   merge;
54 55 56 57
} SvgLoadVals;

static SvgLoadVals load_vals =
{
58 59 60 61 62
  SVG_DEFAULT_RESOLUTION,
  0,
  0,
  FALSE,
  FALSE
63 64 65
};


66 67 68 69 70 71 72
static void  query (void);
static void  run   (const gchar      *name,
                    gint              nparams,
                    const GimpParam  *param,
                    gint             *nreturn_vals,
                    GimpParam       **return_vals);

73 74 75 76 77 78 79 80 81 82
static gint32              load_image        (const gchar  *filename,
                                              GError      **error);
static GdkPixbuf         * load_rsvg_pixbuf  (const gchar  *filename,
                                              SvgLoadVals  *vals,
                                             GError      **error);
static gboolean            load_rsvg_size    (const gchar  *filename,
                                              SvgLoadVals  *vals,
                                              GError      **error);
static GimpPDBStatusType   load_dialog       (const gchar  *filename,
                                              GError      **error);
83 84


85
const GimpPlugInInfo PLUG_IN_INFO =
86
{
87 88 89 90
  NULL,
  NULL,
  query,
  run,
91
};
92

93
MAIN ()
94 95


96 97 98
static void
query (void)
{
99
  static const GimpParamDef load_args[] =
100
  {
101
    { GIMP_PDB_INT32,  "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }"        },
102
    { GIMP_PDB_STRING, "filename",     "The name of the file to load"        },
103
    { GIMP_PDB_STRING, "raw-filename", "The name of the file to load"        },
104
    { GIMP_PDB_FLOAT,  "resolution",
105
      "Resolution to use for rendering the SVG (defaults to 90 dpi)"         },
106
    { GIMP_PDB_INT32,  "width",
107
      "Width (in pixels) to load the SVG in. "
108
      "(0 for original width, a negative width to specify a maximum width)"  },
109
    { GIMP_PDB_INT32,  "height",
110 111 112 113 114
      "Height (in pixels) to load the SVG in. "
      "(0 for original height, a negative width to specify a maximum height)"},
    { GIMP_PDB_INT32,  "paths",
      "Whether to not import paths (0), import paths individually (1) "
      "or merge all imported paths (2)"                                      }
115
  };
116
  static const GimpParamDef load_return_vals[] =
117
  {
118
    { GIMP_PDB_IMAGE,  "image",        "Output image" }
119 120
  };

121
  static const GimpParamDef thumb_args[] =
122 123
  {
    { GIMP_PDB_STRING, "filename",     "The name of the file to load"  },
124
    { GIMP_PDB_INT32,  "thumb-size",   "Preferred thumbnail size"      }
125
  };
126
  static const GimpParamDef thumb_return_vals[] =
127 128
  {
    { GIMP_PDB_IMAGE,  "image",        "Thumbnail image"               },
129 130
    { GIMP_PDB_INT32,  "image-width",  "Width of full-sized image"     },
    { GIMP_PDB_INT32,  "image-height", "Height of full-sized image"    }
131 132
  };

133
  gimp_install_procedure (LOAD_PROC,
134
                          "Loads files in the SVG file format",
135 136
                          "Renders SVG files to raster graphics using librsvg.",
                          "Dom Lachowicz, Sven Neumann",
137
                          "Dom Lachowicz <cinamod@hotmail.com>",
138
                          SVG_VERSION,
139 140
                          N_("SVG image"),
                          NULL,
141 142 143 144 145
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (load_args),
                          G_N_ELEMENTS (load_return_vals),
                          load_args, load_return_vals);

146 147
  gimp_register_file_handler_mime (LOAD_PROC, "image/svg+xml");
  gimp_register_magic_load_handler (LOAD_PROC,
148 149
                                    "svg", "",
                                    "0,string,<?xml,0,string,<svg");
150

151
  gimp_install_procedure (LOAD_THUMB_PROC,
152 153
                          "Generates a thumbnail of an SVG image",
                          "Renders a thumbnail of an SVG file using librsvg.",
154 155 156
                          "Dom Lachowicz, Sven Neumann",
                          "Dom Lachowicz <cinamod@hotmail.com>",
                          SVG_VERSION,
157 158
                          NULL,
                          NULL,
159 160 161 162 163
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (thumb_args),
                          G_N_ELEMENTS (thumb_return_vals),
                          thumb_args, thumb_return_vals);

164
  gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
165 166 167
}

static void
168
run (const gchar      *name,
169
     gint              nparams,
170
     const GimpParam  *param,
171 172
     gint             *nreturn_vals,
     GimpParam       **return_vals)
173
{
174
  static GimpParam   values[4];
175 176
  GimpRunMode        run_mode;
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
177
  GError            *error  = NULL;
178

179 180
  INIT_I18N ();

181 182 183 184 185 186 187 188
  run_mode = param[0].data.d_int32;

  *nreturn_vals = 1;
  *return_vals  = values;

  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;

189
  if (strcmp (name, LOAD_PROC) == 0)
190
    {
191
      gimp_get_data (LOAD_PROC, &load_vals);
192

193 194 195
      switch (run_mode)
        {
        case GIMP_RUN_NONINTERACTIVE:
196 197 198 199 200 201 202 203
          if (nparams > 3)  load_vals.resolution = param[3].data.d_float;
          if (nparams > 4)  load_vals.width      = param[4].data.d_int32;
          if (nparams > 5)  load_vals.height     = param[5].data.d_int32;
          if (nparams > 6)
            {
              load_vals.import = param[6].data.d_int32 != FALSE;
              load_vals.merge  = param[6].data.d_int32 > TRUE;
            }
204 205 206
          break;

        case GIMP_RUN_INTERACTIVE:
207
          status = load_dialog (param[1].data.d_string, &error);
208 209 210 211
          break;

        case GIMP_RUN_WITH_LAST_VALS:
          break;
212
        }
213

214 215
      /* Don't clamp this; insane values are probably not meant to be
       * used as resolution anyway.
216
       */
217 218
      if (load_vals.resolution < GIMP_MIN_RESOLUTION ||
          load_vals.resolution > GIMP_MAX_RESOLUTION)
219 220 221
        {
          load_vals.resolution = SVG_DEFAULT_RESOLUTION;
        }
222

223
      if (status == GIMP_PDB_SUCCESS)
224 225
        {
          const gchar *filename = param[1].data.d_string;
226
          gint32       image_ID = load_image (filename, &error);
227

228 229
          if (image_ID != -1)
            {
230
              if (load_vals.import)
231 232
                {
                  gint32 *vectors;
233
                  gint    num_vectors;
234

235 236 237
                  gimp_vectors_import_from_file (image_ID, filename,
                                                 load_vals.merge, TRUE,
                                                 &num_vectors, &vectors);
238
                  if (num_vectors > 0)
239 240 241 242
                    g_free (vectors);
                }

              *nreturn_vals = 2;
243

244 245 246 247
              values[1].type         = GIMP_PDB_IMAGE;
              values[1].data.d_image = image_ID;
            }
          else
248 249 250
            {
              status = GIMP_PDB_EXECUTION_ERROR;
            }
251

252
          gimp_set_data (LOAD_PROC, &load_vals, sizeof (load_vals));
253
        }
Jehan's avatar
Jehan committed
254
    }
255
  else if (strcmp (name, LOAD_THUMB_PROC) == 0)
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
    {
      if (nparams < 2)
        {
          status = GIMP_PDB_CALLING_ERROR;
        }
      else
        {
          const gchar *filename = param[0].data.d_string;
          gint         width    = 0;
          gint         height   = 0;
          gint32       image_ID;

          if (load_rsvg_size (filename, &load_vals, NULL))
            {
              width  = load_vals.width;
              height = load_vals.height;
            }

          load_vals.resolution = SVG_DEFAULT_RESOLUTION;
          load_vals.width      = - param[1].data.d_int32;
          load_vals.height     = - param[1].data.d_int32;

278
          image_ID = load_image (filename, &error);
279 280 281

          if (image_ID != -1)
            {
282 283 284 285 286 287 288
              *nreturn_vals = 4;
              values[1].type         = GIMP_PDB_IMAGE;
              values[1].data.d_image = image_ID;
              values[2].type         = GIMP_PDB_INT32;
              values[2].data.d_int32 = width;
              values[3].type         = GIMP_PDB_INT32;
              values[3].data.d_int32 = height;
289 290 291 292 293 294 295
            }
          else
            {
              status = GIMP_PDB_EXECUTION_ERROR;
            }
        }
    }
296
  else
297 298 299
    {
      status = GIMP_PDB_CALLING_ERROR;
    }
300

301 302 303 304 305 306 307
  if (status != GIMP_PDB_SUCCESS && error)
    {
      *nreturn_vals = 2;
      values[1].type          = GIMP_PDB_STRING;
      values[1].data.d_string = error->message;
    }

308
  values[0].data.d_status = status;
309
}
310 311

static gint32
312 313
load_image (const gchar  *filename,
            GError      **load_error)
314 315
{
  gint32        image;
316
  gint32        layer;
317 318 319 320 321
  GdkPixbuf    *pixbuf;
  gint          width;
  gint          height;
  GError       *error = NULL;

322
  pixbuf = load_rsvg_pixbuf (filename, &load_vals, &error);
323

324
  if (! pixbuf)
325
    {
326
      /*  Do not rely on librsvg setting GError on failure!  */
327 328 329 330 331 332 333
      g_set_error (load_error,
                   error ? error->domain : 0, error ? error->code : 0,
                   _("Could not open '%s' for reading: %s"),
                   gimp_filename_to_utf8 (filename),
                   error ? error->message : _("Unknown reason"));
      g_clear_error (&error);

334
      return -1;
335 336
    }

337
  gimp_progress_init (_("Rendering SVG"));
338 339 340 341 342

  width  = gdk_pixbuf_get_width (pixbuf);
  height = gdk_pixbuf_get_height (pixbuf);

  image = gimp_image_new (width, height, GIMP_RGB);
343 344
  gimp_image_undo_disable (image);

345
  gimp_image_set_filename (image, filename);
346 347
  gimp_image_set_resolution (image,
                             load_vals.resolution, load_vals.resolution);
348

349
  layer = gimp_layer_new_from_pixbuf (image, _("Rendered SVG"), pixbuf,
350 351
                                      100,
                                      gimp_image_get_default_new_layer_mode (image),
352
                                      0.0, 1.0);
353
  gimp_image_insert_layer (image, layer, -1, 0);
354

355 356
  gimp_image_undo_enable (image);

357 358 359
  return image;
}

360
/*  This is the callback used from load_rsvg_pixbuf().  */
361
static void
362 363 364
load_set_size_callback (gint     *width,
                        gint     *height,
                        gpointer  data)
365 366 367
{
  SvgLoadVals *vals = data;

368 369 370 371 372
  if (*width < 1 || *height < 1)
    {
      *width  = SVG_DEFAULT_SIZE;
      *height = SVG_DEFAULT_SIZE;
    }
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394

  if (!vals->width || !vals->height)
    return;

  /*  either both arguments negative or none  */
  if ((vals->width * vals->height) < 0)
    return;

  if (vals->width > 0)
    {
      *width  = vals->width;
      *height = vals->height;
    }
  else
    {
      gdouble w      = *width;
      gdouble h      = *height;
      gdouble aspect = (gdouble) vals->width / (gdouble) vals->height;

      if (aspect > (w / h))
        {
          *height = abs (vals->height);
395
          *width  = (gdouble) abs (vals->width) * (w / h) + 0.5;
396 397 398 399 400 401 402 403 404 405 406 407
        }
      else
        {
          *width  = abs (vals->width);
          *height = (gdouble) abs (vals->height) / (w / h) + 0.5;
        }

      vals->width  = *width;
      vals->height = *height;
    }
}

408
/*  This function renders a pixbuf from an SVG file according to vals.  */
409 410 411 412 413 414 415 416
static GdkPixbuf *
load_rsvg_pixbuf (const gchar  *filename,
                  SvgLoadVals  *vals,
                  GError      **error)
{
  GdkPixbuf  *pixbuf  = NULL;
  RsvgHandle *handle;
  GIOChannel *io;
417
  gchar      *uri;
418 419 420 421 422 423 424 425 426
  GIOStatus   status  = G_IO_STATUS_NORMAL;
  gboolean    success = TRUE;

  io = g_io_channel_new_file (filename, "r", error);
  if (!io)
    return NULL;

  g_io_channel_set_encoding (io, NULL, NULL);

427 428
  handle = rsvg_handle_new ();
  rsvg_handle_set_dpi (handle, vals->resolution);
429

430 431 432 433 434 435 436 437 438 439 440 441 442
  /*  set the base URI so that librsvg can resolve relative paths  */
  uri = g_filename_to_uri (filename, NULL, NULL);
  if (uri)
    {
      gchar *p = strrchr (uri, '/');

      if (p)
        *p = '\0';

      rsvg_handle_set_base_uri (handle, uri);
      g_free (uri);
    }

443
  rsvg_handle_set_size_callback (handle, load_set_size_callback, vals, NULL);
444 445 446

  while (success && status != G_IO_STATUS_EOF)
    {
447
      gchar  buf[8192];
448 449 450 451 452 453 454 455 456 457 458 459 460
      gsize  len;

      status = g_io_channel_read_chars (io, buf, sizeof (buf), &len, error);

      switch (status)
        {
        case G_IO_STATUS_ERROR:
          success = FALSE;
          break;
        case G_IO_STATUS_EOF:
          success = rsvg_handle_close (handle, error);
          break;
        case G_IO_STATUS_NORMAL:
461 462
          success = rsvg_handle_write (handle,
                                       (const guchar *) buf, len, error);
463 464 465 466 467 468 469 470 471 472 473
          break;
        case G_IO_STATUS_AGAIN:
          break;
        }
    }

  g_io_channel_unref (io);

  if (success)
    pixbuf = rsvg_handle_get_pixbuf (handle);

474
  g_object_unref (handle);
475 476 477 478

  return pixbuf;
}

479 480
static GtkWidget *size_label = NULL;

481 482
/*  This function retrieves the pixel size from an SVG file. Parsing
 *  stops after the first chunk that provided the parser with enough
483
 *  information to determine the size. This is usually the opening
484 485
 *  <svg> element and should thus be in the first chunk (1024 bytes).
 */
486 487 488 489 490 491 492 493 494
static gboolean
load_rsvg_size (const gchar  *filename,
                SvgLoadVals  *vals,
                GError      **error)
{
  RsvgHandle *handle;
  GIOChannel *io;
  GIOStatus   status  = G_IO_STATUS_NORMAL;
  gboolean    success = TRUE;
495
  gboolean    done    = FALSE;
496 497 498 499 500 501 502

  io = g_io_channel_new_file (filename, "r", error);
  if (!io)
    return FALSE;

  g_io_channel_set_encoding (io, NULL, NULL);

503 504
  handle = rsvg_handle_new ();
  rsvg_handle_set_dpi (handle, vals->resolution);
505

506 507
  vals->width  = SVG_DEFAULT_SIZE;
  vals->height = SVG_DEFAULT_SIZE;
508

509
  while (success && status != G_IO_STATUS_EOF && (! done))
510
    {
511 512 513
      gchar                 buf[1024];
      gsize                 len;
      RsvgDimensionData     dim = { 0, 0, 0.0, 0.0 };
514 515 516 517 518 519 520 521 522 523 524 525

      status = g_io_channel_read_chars (io, buf, sizeof (buf), &len, error);

      switch (status)
        {
        case G_IO_STATUS_ERROR:
          success = FALSE;
          break;
        case G_IO_STATUS_EOF:
          success = rsvg_handle_close (handle, error);
          break;
        case G_IO_STATUS_NORMAL:
526 527
          success = rsvg_handle_write (handle,
                                       (const guchar *) buf, len, error);
528 529 530 531 532 533 534 535 536
          rsvg_handle_get_dimensions (handle, &dim);

          if (dim.width > 0 && dim.height > 0)
            {
              vals->width  = dim.width;
              vals->height = dim.height;

              done = TRUE;
            }
537 538 539 540 541 542
          break;
        case G_IO_STATUS_AGAIN:
          break;
        }
    }

543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
    if (size_label)
      {
        if (done)
          {
            gchar *text = g_strdup_printf (_("%d × %d"),
                                           vals->width, vals->height);
            gtk_label_set_text (GTK_LABEL (size_label), text);
            g_free (text);
          }
        else
          {
            gtk_label_set_text (GTK_LABEL (size_label),
                                _("SVG file does not\nspecify a size!"));
          }
      }

559
  g_io_channel_unref (io);
560
  g_object_unref (handle);
561

562 563 564
  if (vals->width  < 1)  vals->width  = 1;
  if (vals->height < 1)  vals->height = 1;

565 566
  return success;
}
567

568 569 570 571

/*  User interface  */

static GimpSizeEntry *size       = NULL;
572 573
static GtkAdjustment *xadj       = NULL;
static GtkAdjustment *yadj       = NULL;
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
static GtkWidget     *constrain  = NULL;
static gdouble        ratio_x    = 1.0;
static gdouble        ratio_y    = 1.0;
static gint           svg_width  = 0;
static gint           svg_height = 0;

static void  load_dialog_set_ratio (gdouble x,
                                    gdouble y);


static void
load_dialog_size_callback (GtkWidget *widget,
                           gpointer   data)
{
  if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (constrain)))
    {
      gdouble x = gimp_size_entry_get_refval (size, 0) / (gdouble) svg_width;
      gdouble y = gimp_size_entry_get_refval (size, 1) / (gdouble) svg_height;
592

593 594 595 596 597 598 599 600 601 602
      if (x != ratio_x)
        {
          load_dialog_set_ratio (x, x);
        }
      else if (y != ratio_y)
        {
          load_dialog_set_ratio (y, y);
        }
    }
}
603

604
static void
605 606
load_dialog_ratio_callback (GtkAdjustment *adj,
                            gpointer       data)
607
{
608 609
  gdouble x = gtk_adjustment_get_value (xadj);
  gdouble y = gtk_adjustment_get_value (yadj);
610

611 612 613 614 615 616
  if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (constrain)))
    {
      if (x != ratio_x)
        y = x;
      else
        x = y;
617
    }
618

619 620 621 622 623 624 625 626 627 628 629 630 631 632
  load_dialog_set_ratio (x, y);
}

static void
load_dialog_resolution_callback (GimpSizeEntry *res,
                                 const gchar   *filename)
{
  SvgLoadVals  vals = { 0.0, 0, 0 };

  load_vals.resolution = vals.resolution = gimp_size_entry_get_refval (res, 0);

  if (!load_rsvg_size (filename, &vals, NULL))
    return;

633 634 635 636 637 638 639 640 641 642 643 644 645
  g_signal_handlers_block_by_func (size, load_dialog_size_callback, NULL);

  gimp_size_entry_set_resolution (size, 0, load_vals.resolution, FALSE);
  gimp_size_entry_set_resolution (size, 1, load_vals.resolution, FALSE);

  g_signal_handlers_unblock_by_func (size, load_dialog_size_callback, NULL);

  if (gimp_size_entry_get_unit (size) != GIMP_UNIT_PIXEL)
    {
      ratio_x = gimp_size_entry_get_refval (size, 0) / vals.width;
      ratio_y = gimp_size_entry_get_refval (size, 1) / vals.height;
    }

646 647
  svg_width  = vals.width;
  svg_height = vals.height;
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668

  load_dialog_set_ratio (ratio_x, ratio_y);
}

static void
load_dialog_set_ratio (gdouble x,
                       gdouble y)
{
  ratio_x = x;
  ratio_y = y;

  g_signal_handlers_block_by_func (size, load_dialog_size_callback, NULL);

  gimp_size_entry_set_refval (size, 0, svg_width  * x);
  gimp_size_entry_set_refval (size, 1, svg_height * y);

  g_signal_handlers_unblock_by_func (size, load_dialog_size_callback, NULL);

  g_signal_handlers_block_by_func (xadj, load_dialog_ratio_callback, NULL);
  g_signal_handlers_block_by_func (yadj, load_dialog_ratio_callback, NULL);

669 670
  gtk_adjustment_set_value (xadj, x);
  gtk_adjustment_set_value (yadj, y);
671 672 673

  g_signal_handlers_unblock_by_func (xadj, load_dialog_ratio_callback, NULL);
  g_signal_handlers_unblock_by_func (yadj, load_dialog_ratio_callback, NULL);
674 675
}

676 677 678
static GimpPDBStatusType
load_dialog (const gchar  *filename,
             GError      **load_error)
679
{
680 681 682 683 684 685
  GtkWidget     *dialog;
  GtkWidget     *frame;
  GtkWidget     *hbox;
  GtkWidget     *vbox;
  GtkWidget     *image;
  GdkPixbuf     *preview;
Simon Budig's avatar
Simon Budig committed
686 687
  GtkWidget     *grid;
  GtkWidget     *grid2;
688 689 690 691 692 693 694 695 696
  GtkWidget     *res;
  GtkWidget     *label;
  GtkWidget     *spinbutton;
  GtkWidget     *toggle;
  GtkWidget     *toggle2;
  GtkAdjustment *adj;
  gboolean       run;
  GError        *error = NULL;
  SvgLoadVals    vals  =
697 698 699 700 701
  {
    SVG_DEFAULT_RESOLUTION,
    - SVG_PREVIEW_SIZE,
    - SVG_PREVIEW_SIZE
  };
702

703
  preview = load_rsvg_pixbuf (filename, &vals, &error);
704

705
  if (! preview)
706 707
    {
      /*  Do not rely on librsvg setting GError on failure!  */
708 709 710 711 712 713 714 715
      g_set_error (load_error,
                   error ? error->domain : 0, error ? error->code : 0,
                   _("Could not open '%s' for reading: %s"),
                   gimp_filename_to_utf8 (filename),
                   error ? error->message : _("Unknown reason"));
      g_clear_error (&error);

      return GIMP_PDB_EXECUTION_ERROR;
716
    }
717

718
  gimp_ui_init (PLUG_IN_BINARY, FALSE);
719

720
  /* Scalable Vector Graphics is SVG, should perhaps not be translated */
721
  dialog = gimp_dialog_new (_("Render Scalable Vector Graphics"),
722
                            PLUG_IN_ROLE,
723
                            NULL, 0,
724
                            gimp_standard_help_func, LOAD_PROC,
725

726 727
                            _("_Cancel"), GTK_RESPONSE_CANCEL,
                            _("_OK"),     GTK_RESPONSE_OK,
728 729 730

                            NULL);

731
  gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
732 733 734
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);
735

736 737
  gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);

738 739
  gimp_window_set_transient (GTK_WINDOW (dialog));

740
  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
741
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
742 743
  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
                      hbox, TRUE, TRUE, 0);
744 745
  gtk_widget_show (hbox);

746
  /*  The SVG preview  */
747
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
748 749 750
  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
  gtk_widget_show (vbox);

751 752
  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
753
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
754
  gtk_widget_show (frame);
755

756 757 758
  image = gtk_image_new_from_pixbuf (preview);
  gtk_container_add (GTK_CONTAINER (frame), image);
  gtk_widget_show (image);
759

760 761 762 763 764 765 766 767 768 769 770 771 772
  size_label = gtk_label_new (NULL);
  gtk_label_set_justify (GTK_LABEL (size_label), GTK_JUSTIFY_CENTER);
  gtk_box_pack_start (GTK_BOX (vbox), size_label, TRUE, TRUE, 4);
  gtk_widget_show (size_label);

  /*  query the initial size after the size label is created  */
  vals.resolution = load_vals.resolution;

  load_rsvg_size (filename, &vals, NULL);

  svg_width  = vals.width;
  svg_height = vals.height;

Simon Budig's avatar
Simon Budig committed
773 774 775 776 777
  grid = gtk_grid_new ();
  gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
  gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
  gtk_box_pack_start (GTK_BOX (hbox), grid, TRUE, TRUE, 0);
  gtk_widget_show (grid);
778

779
  /*  Width and Height  */
780
  label = gtk_label_new (_("Width:"));
781
  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
Simon Budig's avatar
Simon Budig committed
782 783
  gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
                    // GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
784 785
  gtk_widget_show (label);

786
  label = gtk_label_new (_("Height:"));
787
  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
Simon Budig's avatar
Simon Budig committed
788 789
  gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
                    // GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
790
  gtk_widget_show (label);
791

792
  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
Simon Budig's avatar
Simon Budig committed
793 794
  gtk_grid_attach (GTK_GRID (grid), hbox, 1, 0, 1, 1);
                    // GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
795
  gtk_widget_show (hbox);
796

797
  adj = gtk_adjustment_new (1, 1, 1, 1, 10, 0);
798 799
  spinbutton = gtk_spin_button_new (adj, 1.0, 2);
  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
800
  gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
801
  gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
802 803
  gtk_widget_show (spinbutton);

804
  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
Simon Budig's avatar
Simon Budig committed
805 806
  gtk_grid_attach (GTK_GRID (grid), hbox, 1, 1, 1, 1);
                    // GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
807
  gtk_widget_show (hbox);
808

809 810 811
  size = GIMP_SIZE_ENTRY (gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a",
                                               TRUE, FALSE, FALSE, 10,
                                               GIMP_SIZE_ENTRY_UPDATE_SIZE));
812

813
  gimp_size_entry_add_field (size, GTK_SPIN_BUTTON (spinbutton), NULL);
814

815
  gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (size), FALSE, FALSE, 0);
816
  gtk_widget_show (GTK_WIDGET (size));
817

818
  gimp_size_entry_set_refval_boundaries (size, 0,
819 820
                                         GIMP_MIN_IMAGE_SIZE,
                                         GIMP_MAX_IMAGE_SIZE);
821
  gimp_size_entry_set_refval_boundaries (size, 1,
822 823 824
                                         GIMP_MIN_IMAGE_SIZE,
                                         GIMP_MAX_IMAGE_SIZE);

825 826
  gimp_size_entry_set_refval (size, 0, svg_width);
  gimp_size_entry_set_refval (size, 1, svg_height);
827

828 829
  gimp_size_entry_set_resolution (size, 0, load_vals.resolution, FALSE);
  gimp_size_entry_set_resolution (size, 1, load_vals.resolution, FALSE);
830

831
  g_signal_connect (size, "value-changed",
832
                    G_CALLBACK (load_dialog_size_callback),
833 834 835
                    NULL);

  /*  Scale ratio  */
836
  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
Simon Budig's avatar
Simon Budig committed
837 838
  gtk_grid_attach (GTK_GRID (grid), hbox, 1, 2, 1, 2);
                    // GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
839 840
  gtk_widget_show (hbox);

Simon Budig's avatar
Simon Budig committed
841 842
  grid2 = gtk_grid_new ();
  gtk_box_pack_start (GTK_BOX (hbox), grid2, FALSE, FALSE, 0);
843

844 845 846 847
  xadj = gtk_adjustment_new (ratio_x,
                             (gdouble) GIMP_MIN_IMAGE_SIZE / (gdouble) svg_width,
                             (gdouble) GIMP_MAX_IMAGE_SIZE / (gdouble) svg_width,
                             0.01, 0.1, 0);
848 849
  spinbutton = gtk_spin_button_new (xadj, 0.01, 4);
  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
850
  gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
Simon Budig's avatar
Simon Budig committed
851
  gtk_grid_attach (GTK_GRID (grid2), spinbutton, 0, 0, 1, 1);
852 853
  gtk_widget_show (spinbutton);

854
  g_signal_connect (xadj, "value-changed",
855 856
                    G_CALLBACK (load_dialog_ratio_callback),
                    NULL);
857

858
  label = gtk_label_new_with_mnemonic (_("_X ratio:"));
859
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
860
  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
Simon Budig's avatar
Simon Budig committed
861 862
  gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
                    // GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
863 864
  gtk_widget_show (label);

865 866 867 868
  yadj = gtk_adjustment_new (ratio_y,
                             (gdouble) GIMP_MIN_IMAGE_SIZE / (gdouble) svg_height,
                             (gdouble) GIMP_MAX_IMAGE_SIZE / (gdouble) svg_height,
                             0.01, 0.1, 0);
869 870
  spinbutton = gtk_spin_button_new (yadj, 0.01, 4);
  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
871
  gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
Simon Budig's avatar
Simon Budig committed
872
  gtk_grid_attach (GTK_GRID (grid2), spinbutton, 0, 1, 1, 1);
873 874
  gtk_widget_show (spinbutton);

875
  g_signal_connect (yadj, "value-changed",
876 877
                    G_CALLBACK (load_dialog_ratio_callback),
                    NULL);
878

879
  label = gtk_label_new_with_mnemonic (_("_Y ratio:"));
880
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
881
  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
Simon Budig's avatar
Simon Budig committed
882 883
  gtk_grid_attach (GTK_GRID (grid), label, 0, 3, 1, 1);
                    // GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
884 885
  gtk_widget_show (label);

886 887 888
  /*  the constrain ratio chainbutton  */
  constrain = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
  gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (constrain), TRUE);
Simon Budig's avatar
Simon Budig committed
889
  gtk_grid_attach (GTK_GRID (grid2), constrain, 1, 0, 1, 2);
890 891
  gtk_widget_show (constrain);

892
  gimp_help_set_help_data (gimp_chain_button_get_button (GIMP_CHAIN_BUTTON (constrain)),
893 894
                           _("Constrain aspect ratio"), NULL);

Simon Budig's avatar
Simon Budig committed
895
  gtk_widget_show (grid2);
896 897 898

  /*  Resolution   */
  label = gtk_label_new (_("Resolution:"));
899
  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
Simon Budig's avatar
Simon Budig committed
900 901
  gtk_grid_attach (GTK_GRID (grid), label, 0, 4, 1, 1);
                    // GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
902 903
  gtk_widget_show (label);

904 905 906 907
  res = gimp_size_entry_new (1, GIMP_UNIT_INCH, _("pixels/%a"),
                             FALSE, FALSE, FALSE, 10,
                             GIMP_SIZE_ENTRY_UPDATE_RESOLUTION);

Simon Budig's avatar
Simon Budig committed
908 909
  gtk_grid_attach (GTK_GRID (grid), res, 1, 4, 1, 1);
                    // GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
910 911
  gtk_widget_show (res);

912 913
  /* don't let the resolution become too small, librsvg tends to
     crash with very small resolutions */
914
  gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (res), 0,
915
                                         5.0, GIMP_MAX_RESOLUTION);
916 917 918
  gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (res), 0, load_vals.resolution);

  g_signal_connect (res, "value-changed",
919 920
                    G_CALLBACK (load_dialog_resolution_callback),
                    (gpointer) filename);
921

922
  /*  Path Import  */
923
  toggle = gtk_check_button_new_with_mnemonic (_("Import _paths"));
924
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), load_vals.import);
Simon Budig's avatar
Simon Budig committed
925 926
  gtk_grid_attach (GTK_GRID (grid), toggle, 0, 5, 2, 1);
                    // GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
927 928
  gtk_widget_show (toggle);

929
  gimp_help_set_help_data (toggle,
930 931
                           _("Import path elements of the SVG so they "
                             "can be used with the GIMP path tool"),
932 933
                           NULL);

934 935 936 937
  g_signal_connect (toggle, "toggled",
                    G_CALLBACK (gimp_toggle_button_update),
                    &load_vals.import);

938
  toggle2 = gtk_check_button_new_with_mnemonic (_("Merge imported paths"));
939
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle2), load_vals.merge);
Simon Budig's avatar
Simon Budig committed
940 941
  gtk_grid_attach (GTK_GRID (grid), toggle2, 0, 6, 2, 1);
                    // GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
942 943 944 945 946 947
  gtk_widget_show (toggle2);

  g_signal_connect (toggle2, "toggled",
                    G_CALLBACK (gimp_toggle_button_update),
                    &load_vals.merge);

948 949 950
  g_object_bind_property (toggle,  "active",
                          toggle2, "sensitive",
                          G_BINDING_SYNC_CREATE);
951

952 953
  gtk_widget_show (dialog);

954
  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
955

956 957 958 959 960 961 962 963
  if (run)
    {
      load_vals.width  = ROUND (gimp_size_entry_get_refval (size, 0));
      load_vals.height = ROUND (gimp_size_entry_get_refval (size, 1));
    }

  gtk_widget_destroy (dialog);

964
  return run ? GIMP_PDB_SUCCESS : GIMP_PDB_CANCEL;
965
}