1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
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  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * 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>
20  *
21  * Largely rewritten in September 2003 by Sven Neumann <sven@gimp.org>
22  */
23 
24 #include "config.h"
25 
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include <librsvg/rsvg.h>
30 
31 #include "libgimp/gimp.h"
32 #include "libgimp/gimpui.h"
33 
34 #include "libgimp/stdplugins-intl.h"
35 
36 
37 #define LOAD_PROC               "file-svg-load"
38 #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 typedef struct
48 {
49   gdouble    resolution;
50   gint       width;
51   gint       height;
52   gboolean   import;
53   gboolean   merge;
54 } SvgLoadVals;
55 
56 static SvgLoadVals load_vals =
57 {
58   SVG_DEFAULT_RESOLUTION,
59   0,
60   0,
61   FALSE,
62   FALSE
63 };
64 
65 
66 static void  query (void);
67 static void  run   (const gchar      *name,
68                     gint              nparams,
69                     const GimpParam  *param,
70                     gint             *nreturn_vals,
71                     GimpParam       **return_vals);
72 
73 static gint32              load_image        (const gchar  *filename,
74                                               GError      **error);
75 static GdkPixbuf         * load_rsvg_pixbuf  (const gchar  *filename,
76                                               SvgLoadVals  *vals,
77                                              GError      **error);
78 static gboolean            load_rsvg_size    (const gchar  *filename,
79                                               SvgLoadVals  *vals,
80                                               GError      **error);
81 static GimpPDBStatusType   load_dialog       (const gchar  *filename,
82                                               GError      **error);
83 
84 
85 const GimpPlugInInfo PLUG_IN_INFO =
86 {
87   NULL,
88   NULL,
89   query,
90   run,
91 };
92 
MAIN()93 MAIN ()
94 
95 
96 static void
97 query (void)
98 {
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       "Height (in pixels) to load the SVG in. "
111       "(0 for original height, a negative width to specify a maximum height)"},
112     { GIMP_PDB_INT32,  "paths",
113       "Whether to not import paths (0), import paths individually (1) "
114       "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     { GIMP_PDB_INT32,  "image-width",  "Width of full-sized image"     },
130     { 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                           "Renders SVG files to raster graphics using librsvg.",
136                           "Dom Lachowicz, Sven Neumann",
137                           "Dom Lachowicz <cinamod@hotmail.com>",
138                           SVG_VERSION,
139                           N_("SVG image"),
140                           NULL,
141                           GIMP_PLUGIN,
142                           G_N_ELEMENTS (load_args),
143                           G_N_ELEMENTS (load_return_vals),
144                           load_args, load_return_vals);
145 
146   gimp_register_file_handler_mime (LOAD_PROC, "image/svg+xml");
147   gimp_register_magic_load_handler (LOAD_PROC,
148                                     "svg", "",
149                                     "0,string,<?xml,0,string,<svg");
150 
151   gimp_install_procedure (LOAD_THUMB_PROC,
152                           "Generates a thumbnail of an SVG image",
153                           "Renders a thumbnail of an SVG file using librsvg.",
154                           "Dom Lachowicz, Sven Neumann",
155                           "Dom Lachowicz <cinamod@hotmail.com>",
156                           SVG_VERSION,
157                           NULL,
158                           NULL,
159                           GIMP_PLUGIN,
160                           G_N_ELEMENTS (thumb_args),
161                           G_N_ELEMENTS (thumb_return_vals),
162                           thumb_args, thumb_return_vals);
163 
164   gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
165 }
166 
167 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)168 run (const gchar      *name,
169      gint              nparams,
170      const GimpParam  *param,
171      gint             *nreturn_vals,
172      GimpParam       **return_vals)
173 {
174   static GimpParam   values[4];
175   GimpRunMode        run_mode;
176   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
177   GError            *error  = NULL;
178 
179   INIT_I18N ();
180 
181   run_mode = param[0].data.d_int32;
182 
183   *nreturn_vals = 1;
184   *return_vals  = values;
185 
186   values[0].type          = GIMP_PDB_STATUS;
187   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
188 
189   if (strcmp (name, LOAD_PROC) == 0)
190     {
191       gimp_get_data (LOAD_PROC, &load_vals);
192 
193       switch (run_mode)
194         {
195         case GIMP_RUN_NONINTERACTIVE:
196           if (nparams > 3)  load_vals.resolution = param[3].data.d_float;
197           if (nparams > 4)  load_vals.width      = param[4].data.d_int32;
198           if (nparams > 5)  load_vals.height     = param[5].data.d_int32;
199           if (nparams > 6)
200             {
201               load_vals.import = param[6].data.d_int32 != FALSE;
202               load_vals.merge  = param[6].data.d_int32 > TRUE;
203             }
204           break;
205 
206         case GIMP_RUN_INTERACTIVE:
207           status = load_dialog (param[1].data.d_string, &error);
208           break;
209 
210         case GIMP_RUN_WITH_LAST_VALS:
211           break;
212         }
213 
214       /* Don't clamp this; insane values are probably not meant to be
215        * used as resolution anyway.
216        */
217       if (load_vals.resolution < GIMP_MIN_RESOLUTION ||
218           load_vals.resolution > GIMP_MAX_RESOLUTION)
219         {
220           load_vals.resolution = SVG_DEFAULT_RESOLUTION;
221         }
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           if (image_ID != -1)
229             {
230               if (load_vals.import)
231                 {
232                   gint32 *vectors;
233                   gint    num_vectors;
234 
235                   gimp_vectors_import_from_file (image_ID, filename,
236                                                  load_vals.merge, TRUE,
237                                                  &num_vectors, &vectors);
238                   if (num_vectors > 0)
239                     g_free (vectors);
240                 }
241 
242               *nreturn_vals = 2;
243 
244               values[1].type         = GIMP_PDB_IMAGE;
245               values[1].data.d_image = image_ID;
246             }
247           else
248             {
249               status = GIMP_PDB_EXECUTION_ERROR;
250             }
251 
252           gimp_set_data (LOAD_PROC, &load_vals, sizeof (load_vals));
253         }
254     }
255   else if (strcmp (name, LOAD_THUMB_PROC) == 0)
256     {
257       if (nparams < 2)
258         {
259           status = GIMP_PDB_CALLING_ERROR;
260         }
261       else
262         {
263           const gchar *filename = param[0].data.d_string;
264           gint         width    = 0;
265           gint         height   = 0;
266           gint32       image_ID;
267 
268           if (load_rsvg_size (filename, &load_vals, NULL))
269             {
270               width  = load_vals.width;
271               height = load_vals.height;
272             }
273 
274           load_vals.resolution = SVG_DEFAULT_RESOLUTION;
275           load_vals.width      = - param[1].data.d_int32;
276           load_vals.height     = - param[1].data.d_int32;
277 
278           image_ID = load_image (filename, &error);
279 
280           if (image_ID != -1)
281             {
282               *nreturn_vals = 4;
283               values[1].type         = GIMP_PDB_IMAGE;
284               values[1].data.d_image = image_ID;
285               values[2].type         = GIMP_PDB_INT32;
286               values[2].data.d_int32 = width;
287               values[3].type         = GIMP_PDB_INT32;
288               values[3].data.d_int32 = height;
289             }
290           else
291             {
292               status = GIMP_PDB_EXECUTION_ERROR;
293             }
294         }
295     }
296   else
297     {
298       status = GIMP_PDB_CALLING_ERROR;
299     }
300 
301   if (status != GIMP_PDB_SUCCESS && error)
302     {
303       *nreturn_vals = 2;
304       values[1].type          = GIMP_PDB_STRING;
305       values[1].data.d_string = error->message;
306     }
307 
308   values[0].data.d_status = status;
309 }
310 
311 static gint32
load_image(const gchar * filename,GError ** load_error)312 load_image (const gchar  *filename,
313             GError      **load_error)
314 {
315   gint32        image;
316   gint32        layer;
317   GdkPixbuf    *pixbuf;
318   gint          width;
319   gint          height;
320   GError       *error = NULL;
321 
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       g_set_error (load_error,
328                    error ? error->domain : 0, error ? error->code : 0,
329                    _("Could not open '%s' for reading: %s"),
330                    gimp_filename_to_utf8 (filename),
331                    error ? error->message : _("Unknown reason"));
332       g_clear_error (&error);
333 
334       return -1;
335     }
336 
337   gimp_progress_init (_("Rendering SVG"));
338 
339   width  = gdk_pixbuf_get_width (pixbuf);
340   height = gdk_pixbuf_get_height (pixbuf);
341 
342   image = gimp_image_new (width, height, GIMP_RGB);
343   gimp_image_undo_disable (image);
344 
345   gimp_image_set_filename (image, filename);
346   gimp_image_set_resolution (image,
347                              load_vals.resolution, load_vals.resolution);
348 
349   layer = gimp_layer_new_from_pixbuf (image, _("Rendered SVG"), pixbuf,
350                                       100,
351                                       gimp_image_get_default_new_layer_mode (image),
352                                       0.0, 1.0);
353   gimp_image_insert_layer (image, layer, -1, 0);
354 
355   gimp_image_undo_enable (image);
356 
357   return image;
358 }
359 
360 /*  This is the callback used from load_rsvg_pixbuf().  */
361 static void
load_set_size_callback(gint * width,gint * height,gpointer data)362 load_set_size_callback (gint     *width,
363                         gint     *height,
364                         gpointer  data)
365 {
366   SvgLoadVals *vals = data;
367 
368   if (*width < 1 || *height < 1)
369     {
370       *width  = SVG_DEFAULT_SIZE;
371       *height = SVG_DEFAULT_SIZE;
372     }
373 
374   if (!vals->width || !vals->height)
375     return;
376 
377   /*  either both arguments negative or none  */
378   if ((vals->width * vals->height) < 0)
379     return;
380 
381   if (vals->width > 0)
382     {
383       *width  = vals->width;
384       *height = vals->height;
385     }
386   else
387     {
388       gdouble w      = *width;
389       gdouble h      = *height;
390       gdouble aspect = (gdouble) vals->width / (gdouble) vals->height;
391 
392       if (aspect > (w / h))
393         {
394           *height = abs (vals->height);
395           *width  = (gdouble) abs (vals->width) * (w / h) + 0.5;
396         }
397       else
398         {
399           *width  = abs (vals->width);
400           *height = (gdouble) abs (vals->height) / (w / h) + 0.5;
401         }
402 
403       vals->width  = *width;
404       vals->height = *height;
405     }
406 }
407 
408 /*  This function renders a pixbuf from an SVG file according to vals.  */
409 static GdkPixbuf *
load_rsvg_pixbuf(const gchar * filename,SvgLoadVals * vals,GError ** error)410 load_rsvg_pixbuf (const gchar  *filename,
411                   SvgLoadVals  *vals,
412                   GError      **error)
413 {
414   GdkPixbuf  *pixbuf  = NULL;
415   RsvgHandle *handle;
416   GFile      *file;
417 
418   file = g_file_new_for_path (filename);
419 
420   handle = rsvg_handle_new_from_gfile_sync (file, RSVG_HANDLE_FLAGS_NONE, NULL, error);
421 
422   g_object_unref (file);
423 
424   if (!handle)
425     {
426       return NULL;
427     }
428 
429   rsvg_handle_set_dpi (handle, vals->resolution);
430   rsvg_handle_set_size_callback (handle, load_set_size_callback, vals, NULL);
431 
432   pixbuf = rsvg_handle_get_pixbuf (handle);
433 
434   g_object_unref (handle);
435 
436   return pixbuf;
437 }
438 
439 static GtkWidget *size_label = NULL;
440 
441 /*  This function retrieves the pixel size from an SVG file.  */
442 static gboolean
load_rsvg_size(const gchar * filename,SvgLoadVals * vals,GError ** error)443 load_rsvg_size (const gchar  *filename,
444                 SvgLoadVals  *vals,
445                 GError      **error)
446 {
447   RsvgHandle        *handle;
448   GFile             *file;
449   RsvgDimensionData  dim;
450   gboolean           has_size;
451 
452   file = g_file_new_for_path (filename);
453 
454   handle = rsvg_handle_new_from_gfile_sync (file, RSVG_HANDLE_FLAGS_NONE, NULL, error);
455 
456   g_object_unref (file);
457 
458   if (!handle)
459     {
460       return FALSE;
461     }
462 
463   rsvg_handle_set_dpi (handle, vals->resolution);
464 
465   rsvg_handle_get_dimensions (handle, &dim);
466 
467   if (dim.width > 0 && dim.height > 0)
468     {
469       vals->width  = dim.width;
470       vals->height = dim.height;
471       has_size = TRUE;
472     }
473   else
474     {
475       vals->width  = SVG_DEFAULT_SIZE;
476       vals->height = SVG_DEFAULT_SIZE;
477       has_size = FALSE;
478     }
479 
480     if (size_label)
481       {
482         if (has_size)
483           {
484             gchar *text = g_strdup_printf (_("%d × %d"),
485                                            vals->width, vals->height);
486             gtk_label_set_text (GTK_LABEL (size_label), text);
487             g_free (text);
488           }
489         else
490           {
491             gtk_label_set_text (GTK_LABEL (size_label),
492                                 _("SVG file does not\nspecify a size!"));
493           }
494       }
495 
496   g_object_unref (handle);
497 
498   if (vals->width  < 1)  vals->width  = 1;
499   if (vals->height < 1)  vals->height = 1;
500 
501   return TRUE;
502 }
503 
504 
505 /*  User interface  */
506 
507 static GimpSizeEntry *size       = NULL;
508 static GtkAdjustment *xadj       = NULL;
509 static GtkAdjustment *yadj       = NULL;
510 static GtkWidget     *constrain  = NULL;
511 static gdouble        ratio_x    = 1.0;
512 static gdouble        ratio_y    = 1.0;
513 static gint           svg_width  = 0;
514 static gint           svg_height = 0;
515 
516 static void  load_dialog_set_ratio (gdouble x,
517                                     gdouble y);
518 
519 
520 static void
load_dialog_size_callback(GtkWidget * widget,gpointer data)521 load_dialog_size_callback (GtkWidget *widget,
522                            gpointer   data)
523 {
524   if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (constrain)))
525     {
526       gdouble x = gimp_size_entry_get_refval (size, 0) / (gdouble) svg_width;
527       gdouble y = gimp_size_entry_get_refval (size, 1) / (gdouble) svg_height;
528 
529       if (x != ratio_x)
530         {
531           load_dialog_set_ratio (x, x);
532         }
533       else if (y != ratio_y)
534         {
535           load_dialog_set_ratio (y, y);
536         }
537     }
538 }
539 
540 static void
load_dialog_ratio_callback(GtkAdjustment * adj,gpointer data)541 load_dialog_ratio_callback (GtkAdjustment *adj,
542                             gpointer       data)
543 {
544   gdouble x = gtk_adjustment_get_value (xadj);
545   gdouble y = gtk_adjustment_get_value (yadj);
546 
547   if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (constrain)))
548     {
549       if (x != ratio_x)
550         y = x;
551       else
552         x = y;
553     }
554 
555   load_dialog_set_ratio (x, y);
556 }
557 
558 static void
load_dialog_resolution_callback(GimpSizeEntry * res,const gchar * filename)559 load_dialog_resolution_callback (GimpSizeEntry *res,
560                                  const gchar   *filename)
561 {
562   SvgLoadVals  vals = { 0.0, 0, 0 };
563 
564   load_vals.resolution = vals.resolution = gimp_size_entry_get_refval (res, 0);
565 
566   if (!load_rsvg_size (filename, &vals, NULL))
567     return;
568 
569   g_signal_handlers_block_by_func (size, load_dialog_size_callback, NULL);
570 
571   gimp_size_entry_set_resolution (size, 0, load_vals.resolution, FALSE);
572   gimp_size_entry_set_resolution (size, 1, load_vals.resolution, FALSE);
573 
574   g_signal_handlers_unblock_by_func (size, load_dialog_size_callback, NULL);
575 
576   if (gimp_size_entry_get_unit (size) != GIMP_UNIT_PIXEL)
577     {
578       ratio_x = gimp_size_entry_get_refval (size, 0) / vals.width;
579       ratio_y = gimp_size_entry_get_refval (size, 1) / vals.height;
580     }
581 
582   svg_width  = vals.width;
583   svg_height = vals.height;
584 
585   load_dialog_set_ratio (ratio_x, ratio_y);
586 }
587 
588 static void
load_dialog_set_ratio(gdouble x,gdouble y)589 load_dialog_set_ratio (gdouble x,
590                        gdouble y)
591 {
592   ratio_x = x;
593   ratio_y = y;
594 
595   g_signal_handlers_block_by_func (size, load_dialog_size_callback, NULL);
596 
597   gimp_size_entry_set_refval (size, 0, svg_width  * x);
598   gimp_size_entry_set_refval (size, 1, svg_height * y);
599 
600   g_signal_handlers_unblock_by_func (size, load_dialog_size_callback, NULL);
601 
602   g_signal_handlers_block_by_func (xadj, load_dialog_ratio_callback, NULL);
603   g_signal_handlers_block_by_func (yadj, load_dialog_ratio_callback, NULL);
604 
605   gtk_adjustment_set_value (xadj, x);
606   gtk_adjustment_set_value (yadj, y);
607 
608   g_signal_handlers_unblock_by_func (xadj, load_dialog_ratio_callback, NULL);
609   g_signal_handlers_unblock_by_func (yadj, load_dialog_ratio_callback, NULL);
610 }
611 
612 static GimpPDBStatusType
load_dialog(const gchar * filename,GError ** load_error)613 load_dialog (const gchar  *filename,
614              GError      **load_error)
615 {
616   GtkWidget     *dialog;
617   GtkWidget     *frame;
618   GtkWidget     *hbox;
619   GtkWidget     *vbox;
620   GtkWidget     *image;
621   GdkPixbuf     *preview;
622   GtkWidget     *table;
623   GtkWidget     *table2;
624   GtkWidget     *abox;
625   GtkWidget     *res;
626   GtkWidget     *label;
627   GtkWidget     *spinbutton;
628   GtkWidget     *toggle;
629   GtkWidget     *toggle2;
630   GtkAdjustment *adj;
631   gboolean       run;
632   GError        *error = NULL;
633   SvgLoadVals    vals  =
634   {
635     SVG_DEFAULT_RESOLUTION,
636     - SVG_PREVIEW_SIZE,
637     - SVG_PREVIEW_SIZE
638   };
639 
640   preview = load_rsvg_pixbuf (filename, &vals, &error);
641 
642   if (! preview)
643     {
644       /*  Do not rely on librsvg setting GError on failure!  */
645       g_set_error (load_error,
646                    error ? error->domain : 0, error ? error->code : 0,
647                    _("Could not open '%s' for reading: %s"),
648                    gimp_filename_to_utf8 (filename),
649                    error ? error->message : _("Unknown reason"));
650       g_clear_error (&error);
651 
652       return GIMP_PDB_EXECUTION_ERROR;
653     }
654 
655   gimp_ui_init (PLUG_IN_BINARY, FALSE);
656 
657   /* Scalable Vector Graphics is SVG, should perhaps not be translated */
658   dialog = gimp_dialog_new (_("Render Scalable Vector Graphics"),
659                             PLUG_IN_ROLE,
660                             NULL, 0,
661                             gimp_standard_help_func, LOAD_PROC,
662 
663                             _("_Cancel"), GTK_RESPONSE_CANCEL,
664                             _("_OK"),     GTK_RESPONSE_OK,
665 
666                             NULL);
667 
668   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
669                                            GTK_RESPONSE_OK,
670                                            GTK_RESPONSE_CANCEL,
671                                            -1);
672 
673   gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
674 
675   gimp_window_set_transient (GTK_WINDOW (dialog));
676 
677   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
678   gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
679   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
680                       hbox, TRUE, TRUE, 0);
681   gtk_widget_show (hbox);
682 
683   /*  The SVG preview  */
684   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
685   gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
686   gtk_widget_show (vbox);
687 
688   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
689   gtk_box_pack_start (GTK_BOX (vbox), abox, FALSE, FALSE, 0);
690   gtk_widget_show (abox);
691 
692   frame = gtk_frame_new (NULL);
693   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
694   gtk_container_add (GTK_CONTAINER (abox), frame);
695   gtk_widget_show (frame);
696 
697   image = gtk_image_new_from_pixbuf (preview);
698   gtk_container_add (GTK_CONTAINER (frame), image);
699   gtk_widget_show (image);
700 
701   size_label = gtk_label_new (NULL);
702   gtk_label_set_justify (GTK_LABEL (size_label), GTK_JUSTIFY_CENTER);
703   gtk_box_pack_start (GTK_BOX (vbox), size_label, TRUE, TRUE, 4);
704   gtk_widget_show (size_label);
705 
706   /*  query the initial size after the size label is created  */
707   vals.resolution = load_vals.resolution;
708 
709   load_rsvg_size (filename, &vals, NULL);
710 
711   svg_width  = vals.width;
712   svg_height = vals.height;
713 
714   table = gtk_table_new (7, 3, FALSE);
715   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
716   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
717   gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
718   gtk_table_set_row_spacing (GTK_TABLE (table), 2, 2);
719   gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0);
720   gtk_widget_show (table);
721 
722   /*  Width and Height  */
723   label = gtk_label_new (_("Width:"));
724   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
725   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
726                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
727   gtk_widget_show (label);
728 
729   label = gtk_label_new (_("Height:"));
730   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
731   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
732                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
733   gtk_widget_show (label);
734 
735   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
736   gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 0, 1,
737                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
738   gtk_widget_show (hbox);
739 
740   adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0);
741   spinbutton = gimp_spin_button_new (adj, 1.0, 2);
742   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
743   gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
744   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
745   gtk_widget_show (spinbutton);
746 
747   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
748   gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 1, 2,
749                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
750   gtk_widget_show (hbox);
751 
752   size = GIMP_SIZE_ENTRY (gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a",
753                                                TRUE, FALSE, FALSE, 10,
754                                                GIMP_SIZE_ENTRY_UPDATE_SIZE));
755   gtk_table_set_col_spacing (GTK_TABLE (size), 1, 6);
756 
757   gimp_size_entry_add_field (size, GTK_SPIN_BUTTON (spinbutton), NULL);
758 
759   gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (size), FALSE, FALSE, 0);
760   gtk_widget_show (GTK_WIDGET (size));
761 
762   gimp_size_entry_set_refval_boundaries (size, 0,
763                                          GIMP_MIN_IMAGE_SIZE,
764                                          GIMP_MAX_IMAGE_SIZE);
765   gimp_size_entry_set_refval_boundaries (size, 1,
766                                          GIMP_MIN_IMAGE_SIZE,
767                                          GIMP_MAX_IMAGE_SIZE);
768 
769   gimp_size_entry_set_refval (size, 0, svg_width);
770   gimp_size_entry_set_refval (size, 1, svg_height);
771 
772   gimp_size_entry_set_resolution (size, 0, load_vals.resolution, FALSE);
773   gimp_size_entry_set_resolution (size, 1, load_vals.resolution, FALSE);
774 
775   g_signal_connect (size, "value-changed",
776                     G_CALLBACK (load_dialog_size_callback),
777                     NULL);
778 
779   /*  Scale ratio  */
780   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
781   gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 2, 4,
782                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
783   gtk_widget_show (hbox);
784 
785   table2 = gtk_table_new (2, 2, FALSE);
786   gtk_table_set_col_spacing (GTK_TABLE (table2), 0, 2);
787   gtk_table_set_row_spacing (GTK_TABLE (table2), 0, 4);
788   gtk_box_pack_start (GTK_BOX (hbox), table2, FALSE, FALSE, 0);
789 
790   xadj = (GtkAdjustment *)
791     gtk_adjustment_new (ratio_x,
792                         (gdouble) GIMP_MIN_IMAGE_SIZE / (gdouble) svg_width,
793                         (gdouble) GIMP_MAX_IMAGE_SIZE / (gdouble) svg_width,
794                         0.01, 0.1, 0);
795   spinbutton = gimp_spin_button_new (xadj, 0.01, 4);
796   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
797   gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
798   gtk_table_attach_defaults (GTK_TABLE (table2), spinbutton, 0, 1, 0, 1);
799   gtk_widget_show (spinbutton);
800 
801   g_signal_connect (xadj, "value-changed",
802                     G_CALLBACK (load_dialog_ratio_callback),
803                     NULL);
804 
805   label = gtk_label_new_with_mnemonic (_("_X ratio:"));
806   gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
807   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
808   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
809                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
810   gtk_widget_show (label);
811 
812   yadj = (GtkAdjustment *)
813     gtk_adjustment_new (ratio_y,
814                         (gdouble) GIMP_MIN_IMAGE_SIZE / (gdouble) svg_height,
815                         (gdouble) GIMP_MAX_IMAGE_SIZE / (gdouble) svg_height,
816                         0.01, 0.1, 0);
817   spinbutton = gimp_spin_button_new (yadj, 0.01, 4);
818   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
819   gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
820   gtk_table_attach_defaults (GTK_TABLE (table2), spinbutton, 0, 1, 1, 2);
821   gtk_widget_show (spinbutton);
822 
823   g_signal_connect (yadj, "value-changed",
824                     G_CALLBACK (load_dialog_ratio_callback),
825                     NULL);
826 
827   label = gtk_label_new_with_mnemonic (_("_Y ratio:"));
828   gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
829   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
830   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
831                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
832   gtk_widget_show (label);
833 
834   /*  the constrain ratio chainbutton  */
835   constrain = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
836   gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (constrain), TRUE);
837   gtk_table_attach_defaults (GTK_TABLE (table2), constrain, 1, 2, 0, 2);
838   gtk_widget_show (constrain);
839 
840   gimp_help_set_help_data (GIMP_CHAIN_BUTTON (constrain)->button,
841                            _("Constrain aspect ratio"), NULL);
842 
843   gtk_widget_show (table2);
844 
845   /*  Resolution   */
846   label = gtk_label_new (_("Resolution:"));
847   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
848   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5,
849                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
850   gtk_widget_show (label);
851 
852   res = gimp_size_entry_new (1, GIMP_UNIT_INCH, _("pixels/%a"),
853                              FALSE, FALSE, FALSE, 10,
854                              GIMP_SIZE_ENTRY_UPDATE_RESOLUTION);
855   gtk_table_set_col_spacing (GTK_TABLE (res), 1, 6);
856 
857   gtk_table_attach (GTK_TABLE (table), res, 1, 2, 4, 5,
858                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
859   gtk_widget_show (res);
860 
861   /* don't let the resolution become too small, librsvg tends to
862      crash with very small resolutions */
863   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (res), 0,
864                                          5.0, GIMP_MAX_RESOLUTION);
865   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (res), 0, load_vals.resolution);
866 
867   g_signal_connect (res, "value-changed",
868                     G_CALLBACK (load_dialog_resolution_callback),
869                     (gpointer) filename);
870 
871   /*  Path Import  */
872   toggle = gtk_check_button_new_with_mnemonic (_("Import _paths"));
873   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), load_vals.import);
874   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 5, 6,
875                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
876   gtk_widget_show (toggle);
877 
878   gimp_help_set_help_data (toggle,
879                            _("Import path elements of the SVG so they "
880                              "can be used with the GIMP path tool"),
881                            NULL);
882 
883   g_signal_connect (toggle, "toggled",
884                     G_CALLBACK (gimp_toggle_button_update),
885                     &load_vals.import);
886 
887   toggle2 = gtk_check_button_new_with_mnemonic (_("Merge imported paths"));
888   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle2), load_vals.merge);
889   gtk_table_attach (GTK_TABLE (table), toggle2, 0, 2, 6, 7,
890                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
891   gtk_widget_show (toggle2);
892 
893   g_signal_connect (toggle2, "toggled",
894                     G_CALLBACK (gimp_toggle_button_update),
895                     &load_vals.merge);
896 
897   g_object_bind_property (toggle,  "active",
898                           toggle2, "sensitive",
899                           G_BINDING_SYNC_CREATE);
900 
901   gtk_widget_show (dialog);
902 
903   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
904 
905   if (run)
906     {
907       load_vals.width  = ROUND (gimp_size_entry_get_refval (size, 0));
908       load_vals.height = ROUND (gimp_size_entry_get_refval (size, 1));
909     }
910 
911   gtk_widget_destroy (dialog);
912 
913   return run ? GIMP_PDB_SUCCESS : GIMP_PDB_CANCEL;
914 }
915