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 /* WMF loading file filter for GIMP
19  * -Dom Lachowicz <cinamod@hotmail.com> 2003
20  * -Francis James Franklin <fjf@alinameridon.com>
21  */
22 
23 #include "config.h"
24 
25 #include <libwmf/api.h>
26 #include <libwmf/gd.h>
27 
28 #include <libgimp/gimp.h>
29 #include <libgimp/gimpui.h>
30 #include <libgimpmath/gimpmath.h>
31 
32 #include "libgimp/stdplugins-intl.h"
33 
34 
35 #define LOAD_PROC               "file-wmf-load"
36 #define LOAD_THUMB_PROC         "file-wmf-load-thumb"
37 #define PLUG_IN_BINARY          "file-wmf"
38 #define PLUG_IN_ROLE            "gimp-file-wmf"
39 
40 #define WMF_DEFAULT_RESOLUTION  90.0
41 #define WMF_DEFAULT_SIZE        500
42 #define WMF_PREVIEW_SIZE        128
43 
44 typedef struct
45 {
46   gdouble    resolution;
47   gint       width;
48   gint       height;
49 } WmfLoadVals;
50 
51 static WmfLoadVals load_vals =
52 {
53   WMF_DEFAULT_RESOLUTION,
54   0,
55   0,
56 };
57 
58 
59 static void      query          (void);
60 static void      run            (const gchar       *name,
61                                  gint               nparams,
62                                  const GimpParam   *param,
63                                  gint              *nreturn_vals,
64                                  GimpParam        **return_vals);
65 static gint32    load_image     (const gchar       *filename,
66                                  GError           **error);
67 static gboolean  load_wmf_size  (const gchar       *filename,
68                                  WmfLoadVals       *vals);
69 static gboolean  load_dialog    (const gchar       *filename);
70 static guchar   *wmf_get_pixbuf (const gchar       *filename,
71                                  gint              *width,
72                                  gint              *height);
73 static guchar   *wmf_load_file  (const gchar       *filename,
74                                  guint             *width,
75                                  guint             *height,
76                                  GError           **error);
77 
78 
79 const GimpPlugInInfo PLUG_IN_INFO =
80 {
81   NULL,   /* init_proc  */
82   NULL,   /* quit_proc  */
83   query,  /* query_proc */
84   run     /* run_proc   */
85 };
86 
MAIN()87 MAIN ()
88 
89 
90 /*
91  * 'query()' - Respond to a plug-in query...
92  */
93 static void
94 query (void)
95 {
96   static const GimpParamDef load_args[] =
97   {
98     { GIMP_PDB_INT32,  "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
99     { GIMP_PDB_STRING, "filename",     "The name of the file to load" },
100     { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" },
101     { GIMP_PDB_FLOAT,  "resolution",   "Resolution to use for rendering the WMF (defaults to 72 dpi"     },
102     { GIMP_PDB_INT32,  "width",        "Width (in pixels) to load the WMF in, 0 for original width"      },
103     { GIMP_PDB_INT32,  "height",       "Height (in pixels) to load the WMF in, 0 for original height"    }
104   };
105 
106   static const GimpParamDef load_return_vals[] =
107   {
108     { GIMP_PDB_IMAGE,   "image",         "Output image"               }
109   };
110 
111   static const GimpParamDef thumb_args[] =
112   {
113     { GIMP_PDB_STRING, "filename",     "The name of the file to load"   },
114     { GIMP_PDB_INT32,  "thumb-size",   "Preferred thumbnail size"       }
115   };
116   static const GimpParamDef thumb_return_vals[] =
117   {
118     { GIMP_PDB_IMAGE,  "image",        "Thumbnail image"                },
119     { GIMP_PDB_INT32,  "image-width",  "Width of full-sized image"      },
120     { GIMP_PDB_INT32,  "image-height", "Height of full-sized image"     }
121   };
122 
123   gimp_install_procedure (LOAD_PROC,
124                           "Loads files in the WMF file format",
125                           "Loads files in the WMF file format",
126                           "Dom Lachowicz <cinamod@hotmail.com>",
127                           "Dom Lachowicz <cinamod@hotmail.com>",
128                           "(c) 2003 - Version 0.3.0",
129                           N_("Microsoft WMF file"),
130                           NULL,
131                           GIMP_PLUGIN,
132                           G_N_ELEMENTS (load_args),
133                           G_N_ELEMENTS (load_return_vals),
134                           load_args, load_return_vals);
135 
136   gimp_register_file_handler_mime (LOAD_PROC, "image/x-wmf");
137   gimp_register_magic_load_handler (LOAD_PROC,
138                                     "wmf,apm", "",
139                                     "0,string,\\327\\315\\306\\232,0,string,\\1\\0\\11\\0");
140 
141   gimp_install_procedure (LOAD_THUMB_PROC,
142                           "Loads a small preview from a WMF image",
143                           "",
144                           "Dom Lachowicz <cinamod@hotmail.com>",
145                           "Dom Lachowicz <cinamod@hotmail.com>",
146                           "(c) 2003 - Version 0.3.0",
147                           NULL,
148                           NULL,
149                           GIMP_PLUGIN,
150                           G_N_ELEMENTS (thumb_args),
151                           G_N_ELEMENTS (thumb_return_vals),
152                           thumb_args, thumb_return_vals);
153 
154   gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
155 }
156 
157 /*
158  * 'run()' - Run the plug-in...
159  */
160 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)161 run (const gchar      *name,
162      gint              nparams,
163      const GimpParam  *param,
164      gint             *nreturn_vals,
165      GimpParam       **return_vals)
166 {
167   static GimpParam   values[4];
168   GimpRunMode        run_mode;
169   GimpPDBStatusType  status   = GIMP_PDB_SUCCESS;
170   const gchar       *filename = NULL;
171   GError            *error    = NULL;
172   gint32             image_ID = -1;
173   gint               width    = 0;
174   gint               height   = 0;
175 
176   INIT_I18N ();
177   gegl_init (NULL, NULL);
178 
179   run_mode = param[0].data.d_int32;
180 
181   *nreturn_vals = 1;
182   *return_vals  = values;
183 
184   values[0].type          = GIMP_PDB_STATUS;
185   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
186 
187   if (strcmp (name, LOAD_PROC) == 0)
188     {
189       filename = param[1].data.d_string;
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           break;
200 
201         case GIMP_RUN_INTERACTIVE:
202           if (!load_dialog (param[1].data.d_string))
203             status = GIMP_PDB_CANCEL;
204           break;
205 
206         case GIMP_RUN_WITH_LAST_VALS:
207           break;
208         }
209     }
210   else if (strcmp (name, LOAD_THUMB_PROC) == 0)
211     {
212       gint size = param[1].data.d_int32;
213 
214       filename = param[0].data.d_string;
215 
216       if (size > 0                             &&
217           load_wmf_size (filename, &load_vals) &&
218           load_vals.width  > 0                 &&
219           load_vals.height > 0)
220         {
221           width  = load_vals.width;
222           height = load_vals.height;
223 
224           if ((gdouble) load_vals.width > (gdouble) load_vals.height)
225             {
226               load_vals.width   = size;
227               load_vals.height *= size / (gdouble) load_vals.width;
228             }
229           else
230             {
231               load_vals.width  *= size / (gdouble) load_vals.height;
232               load_vals.height  = size;
233             }
234         }
235       else
236         {
237           status = GIMP_PDB_EXECUTION_ERROR;
238         }
239     }
240   else
241     {
242       status = GIMP_PDB_CALLING_ERROR;
243     }
244 
245   if (status == GIMP_PDB_SUCCESS)
246     {
247       if (load_vals.resolution < GIMP_MIN_RESOLUTION ||
248           load_vals.resolution > GIMP_MAX_RESOLUTION)
249         {
250           load_vals.resolution = WMF_DEFAULT_RESOLUTION;
251         }
252 
253       image_ID = load_image (filename, &error);
254 
255       if (image_ID != -1)
256         {
257           *nreturn_vals = 2;
258           values[1].type         = GIMP_PDB_IMAGE;
259           values[1].data.d_image = image_ID;
260         }
261       else
262         {
263           status = GIMP_PDB_EXECUTION_ERROR;
264         }
265     }
266 
267   if (status == GIMP_PDB_SUCCESS)
268     {
269       if (strcmp (name, LOAD_THUMB_PROC) == 0)
270         {
271           *nreturn_vals = 4;
272           values[2].type         = GIMP_PDB_INT32;
273           values[2].data.d_int32 = width;
274           values[3].type         = GIMP_PDB_INT32;
275           values[3].data.d_int32 = height;
276         }
277       else
278         {
279           gimp_set_data (LOAD_PROC, &load_vals, sizeof (load_vals));
280         }
281     }
282 
283   if (status != GIMP_PDB_SUCCESS && error)
284     {
285       *nreturn_vals = 2;
286       values[1].type          = GIMP_PDB_STRING;
287       values[1].data.d_string = error->message;
288     }
289 
290   values[0].data.d_status = status;
291 }
292 
293 
294 static GtkWidget *size_label = NULL;
295 
296 
297 /*  This function retrieves the pixel size from a WMF file. */
298 static gboolean
load_wmf_size(const gchar * filename,WmfLoadVals * vals)299 load_wmf_size (const gchar *filename,
300                WmfLoadVals *vals)
301 {
302   GMappedFile    *file;
303   /* the bits we need to decode the WMF via libwmf2's GD layer  */
304   wmf_error_t     err;
305   gulong          flags;
306   wmf_gd_t       *ddata = NULL;
307   wmfAPI         *API   = NULL;
308   wmfAPI_Options  api_options;
309   wmfD_Rect       bbox;
310   guint           width   = -1;
311   guint           height  = -1;
312   gboolean        success = TRUE;
313   char*           wmffontdirs[2] = { NULL, NULL };
314 
315   file = g_mapped_file_new (filename, FALSE, NULL);
316   if (! file)
317     return FALSE;
318 
319   flags = WMF_OPT_IGNORE_NONFATAL | WMF_OPT_FUNCTION;
320   api_options.function = wmf_gd_function;
321 
322 #ifdef ENABLE_RELOCATABLE_RESOURCES
323   wmffontdirs[0] = g_build_filename (gimp_installation_directory (),
324                                      "share/libwmf/fonts", NULL);
325   flags |= WMF_OPT_FONTDIRS;
326   api_options.fontdirs = wmffontdirs;
327 #endif
328 
329   err = wmf_api_create (&API, flags, &api_options);
330   if (wmffontdirs[0])
331     g_free (wmffontdirs[0]);
332   if (err != wmf_E_None)
333     success = FALSE;
334 
335   ddata = WMF_GD_GetData (API);
336   ddata->type = wmf_gd_image;
337 
338   err = wmf_mem_open (API,
339                       (guchar *) g_mapped_file_get_contents (file),
340                       g_mapped_file_get_length (file));
341   if (err != wmf_E_None)
342     success = FALSE;
343 
344   err = wmf_scan (API, 0, &bbox);
345   if (err != wmf_E_None)
346     success = FALSE;
347 
348   err = wmf_display_size (API, &width, &height,
349                           vals->resolution, vals->resolution);
350   if (err != wmf_E_None || width <= 0 || height <= 0)
351     success = FALSE;
352 
353   wmf_mem_close (API);
354   g_mapped_file_unref (file);
355 
356   if (width < 1 || height < 1)
357     {
358       width  = WMF_DEFAULT_SIZE;
359       height = WMF_DEFAULT_SIZE;
360 
361       if (size_label)
362         gtk_label_set_text (GTK_LABEL (size_label),
363                             _("WMF file does not\nspecify a size!"));
364     }
365   else
366     {
367       if (size_label)
368         {
369           gchar *text = g_strdup_printf (_("%d × %d"), width, height);
370 
371           gtk_label_set_text (GTK_LABEL (size_label), text);
372           g_free (text);
373         }
374     }
375 
376   vals->width  = width;
377   vals->height = height;
378 
379   return success;
380 }
381 
382 
383 /*  User interface  */
384 
385 static GimpSizeEntry *size       = NULL;
386 static GtkAdjustment *xadj       = NULL;
387 static GtkAdjustment *yadj       = NULL;
388 static GtkWidget     *constrain  = NULL;
389 static gdouble        ratio_x    = 1.0;
390 static gdouble        ratio_y    = 1.0;
391 static gint           wmf_width  = 0;
392 static gint           wmf_height = 0;
393 
394 static void  load_dialog_set_ratio (gdouble x,
395                                     gdouble y);
396 
397 
398 static void
load_dialog_size_callback(GtkWidget * widget,gpointer data)399 load_dialog_size_callback (GtkWidget *widget,
400                            gpointer   data)
401 {
402   if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (constrain)))
403     {
404       gdouble x = gimp_size_entry_get_refval (size, 0) / (gdouble) wmf_width;
405       gdouble y = gimp_size_entry_get_refval (size, 1) / (gdouble) wmf_height;
406 
407       if (x != ratio_x)
408         {
409           load_dialog_set_ratio (x, x);
410         }
411       else if (y != ratio_y)
412         {
413           load_dialog_set_ratio (y, y);
414         }
415     }
416 }
417 
418 static void
load_dialog_ratio_callback(GtkAdjustment * adj,gpointer data)419 load_dialog_ratio_callback (GtkAdjustment *adj,
420                             gpointer       data)
421 {
422   gdouble x = gtk_adjustment_get_value (xadj);
423   gdouble y = gtk_adjustment_get_value (yadj);
424 
425   if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (constrain)))
426     {
427       if (x != ratio_x)
428         y = x;
429       else
430         x = y;
431     }
432 
433   load_dialog_set_ratio (x, y);
434 }
435 
436 static void
load_dialog_resolution_callback(GimpSizeEntry * res,const gchar * filename)437 load_dialog_resolution_callback (GimpSizeEntry *res,
438                                  const gchar   *filename)
439 {
440   WmfLoadVals  vals = { 0.0, 0, 0 };
441 
442   load_vals.resolution = vals.resolution = gimp_size_entry_get_refval (res, 0);
443 
444   if (!load_wmf_size (filename, &vals))
445     return;
446 
447   wmf_width  = vals.width;
448   wmf_height = vals.height;
449 
450   load_dialog_set_ratio (ratio_x, ratio_y);
451 }
452 
453 static void
wmf_preview_callback(GtkWidget * widget,GtkAllocation * allocation,guchar * pixels)454 wmf_preview_callback (GtkWidget     *widget,
455                       GtkAllocation *allocation,
456                       guchar        *pixels)
457 {
458   gimp_preview_area_draw (GIMP_PREVIEW_AREA (widget),
459                           0, 0, allocation->width, allocation->height,
460                           GIMP_RGBA_IMAGE,
461                           pixels, allocation->width * 4);
462 }
463 
464 static void
load_dialog_set_ratio(gdouble x,gdouble y)465 load_dialog_set_ratio (gdouble x,
466                        gdouble y)
467 {
468   ratio_x = x;
469   ratio_y = y;
470 
471   g_signal_handlers_block_by_func (size, load_dialog_size_callback, NULL);
472 
473   gimp_size_entry_set_refval (size, 0, wmf_width  * x);
474   gimp_size_entry_set_refval (size, 1, wmf_height * y);
475 
476   g_signal_handlers_unblock_by_func (size, load_dialog_size_callback, NULL);
477 
478   g_signal_handlers_block_by_func (xadj, load_dialog_ratio_callback, NULL);
479   g_signal_handlers_block_by_func (yadj, load_dialog_ratio_callback, NULL);
480 
481   gtk_adjustment_set_value (xadj, x);
482   gtk_adjustment_set_value (yadj, y);
483 
484   g_signal_handlers_unblock_by_func (xadj, load_dialog_ratio_callback, NULL);
485   g_signal_handlers_unblock_by_func (yadj, load_dialog_ratio_callback, NULL);
486 }
487 
488 static gboolean
load_dialog(const gchar * filename)489 load_dialog (const gchar *filename)
490 {
491   GtkWidget     *dialog;
492   GtkWidget     *frame;
493   GtkWidget     *hbox;
494   GtkWidget     *vbox;
495   GtkWidget     *image;
496   GtkWidget     *table;
497   GtkWidget     *table2;
498   GtkWidget     *abox;
499   GtkWidget     *res;
500   GtkWidget     *label;
501   GtkWidget     *spinbutton;
502   GtkAdjustment *adj;
503   guchar        *pixels;
504   gboolean       run = FALSE;
505 
506   WmfLoadVals  vals = { WMF_DEFAULT_RESOLUTION,
507                         - WMF_PREVIEW_SIZE, - WMF_PREVIEW_SIZE };
508 
509   gimp_ui_init (PLUG_IN_BINARY, FALSE);
510 
511   dialog = gimp_dialog_new (_("Render Windows Metafile"), PLUG_IN_ROLE,
512                             NULL, 0,
513                             gimp_standard_help_func, LOAD_PROC,
514 
515                             _("_Cancel"), GTK_RESPONSE_CANCEL,
516                             _("_OK"),     GTK_RESPONSE_OK,
517 
518                             NULL);
519 
520   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
521                                            GTK_RESPONSE_OK,
522                                            GTK_RESPONSE_CANCEL,
523                                            -1);
524 
525   gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
526 
527   gimp_window_set_transient (GTK_WINDOW (dialog));
528 
529   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
530   gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
531   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
532                       hbox, TRUE, TRUE, 0);
533   gtk_widget_show (hbox);
534 
535   /*  The WMF preview  */
536   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
537   gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
538   gtk_widget_show (vbox);
539 
540   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
541   gtk_box_pack_start (GTK_BOX (vbox), abox, FALSE, FALSE, 0);
542   gtk_widget_show (abox);
543 
544   frame = gtk_frame_new (NULL);
545   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
546   gtk_container_add (GTK_CONTAINER (abox), frame);
547   gtk_widget_show (frame);
548 
549   pixels = wmf_get_pixbuf (filename, &vals.width, &vals.height);
550   image = gimp_preview_area_new ();
551   gtk_widget_set_size_request (image, vals.width, vals.height);
552   gtk_container_add (GTK_CONTAINER (frame), image);
553   gtk_widget_show (image);
554 
555   g_signal_connect (image, "size-allocate",
556                     G_CALLBACK (wmf_preview_callback),
557                     pixels);
558 
559   size_label = gtk_label_new (NULL);
560   gtk_label_set_justify (GTK_LABEL (size_label), GTK_JUSTIFY_CENTER);
561   gtk_box_pack_start (GTK_BOX (vbox), size_label, TRUE, TRUE, 4);
562   gtk_widget_show (size_label);
563 
564   /*  query the initial size after the size label is created  */
565   vals.resolution = load_vals.resolution;
566 
567   load_wmf_size (filename, &vals);
568 
569   wmf_width  = vals.width;
570   wmf_height = vals.height;
571 
572   table = gtk_table_new (7, 3, FALSE);
573   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
574   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
575   gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
576   gtk_table_set_row_spacing (GTK_TABLE (table), 2, 2);
577   gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0);
578   gtk_widget_show (table);
579 
580   /*  Width and Height  */
581   label = gtk_label_new (_("Width:"));
582   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
583   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
584                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
585   gtk_widget_show (label);
586 
587   label = gtk_label_new (_("Height:"));
588   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
589   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
590                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
591   gtk_widget_show (label);
592 
593   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
594   gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 0, 1,
595                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
596   gtk_widget_show (hbox);
597 
598   adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0);
599   spinbutton = gimp_spin_button_new (adj, 1.0, 2);
600   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
601   gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
602   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
603   gtk_widget_show (spinbutton);
604 
605   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
606   gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 1, 2,
607                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
608   gtk_widget_show (hbox);
609 
610   size = GIMP_SIZE_ENTRY (gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a",
611                                                TRUE, FALSE, FALSE, 10,
612                                                GIMP_SIZE_ENTRY_UPDATE_SIZE));
613   gtk_table_set_col_spacing (GTK_TABLE (size), 1, 6);
614 
615   gimp_size_entry_add_field (size, GTK_SPIN_BUTTON (spinbutton), NULL);
616 
617   gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (size), FALSE, FALSE, 0);
618   gtk_widget_show (GTK_WIDGET (size));
619 
620   gimp_size_entry_set_refval_boundaries (size, 0,
621                                          GIMP_MIN_IMAGE_SIZE,
622                                          GIMP_MAX_IMAGE_SIZE);
623   gimp_size_entry_set_refval_boundaries (size, 1,
624                                          GIMP_MIN_IMAGE_SIZE,
625                                          GIMP_MAX_IMAGE_SIZE);
626 
627   gimp_size_entry_set_refval (size, 0, wmf_width);
628   gimp_size_entry_set_refval (size, 1, wmf_height);
629 
630   gimp_size_entry_set_resolution (size, 0, load_vals.resolution, FALSE);
631   gimp_size_entry_set_resolution (size, 1, load_vals.resolution, FALSE);
632 
633   g_signal_connect (size, "value-changed",
634                     G_CALLBACK (load_dialog_size_callback),
635                     NULL);
636 
637   /*  Scale ratio  */
638   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
639   gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 2, 4,
640                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
641   gtk_widget_show (hbox);
642 
643   table2 = gtk_table_new (2, 2, FALSE);
644   gtk_table_set_col_spacing (GTK_TABLE (table2), 0, 2);
645   gtk_table_set_row_spacing (GTK_TABLE (table2), 0, 4);
646   gtk_box_pack_start (GTK_BOX (hbox), table2, FALSE, FALSE, 0);
647 
648   xadj = (GtkAdjustment *)
649     gtk_adjustment_new (ratio_x,
650                         (gdouble) GIMP_MIN_IMAGE_SIZE / (gdouble) wmf_width,
651                         (gdouble) GIMP_MAX_IMAGE_SIZE / (gdouble) wmf_width,
652                         0.01, 0.1, 0);
653   spinbutton = gimp_spin_button_new (xadj, 0.01, 4);
654   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
655   gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
656   gtk_table_attach_defaults (GTK_TABLE (table2), spinbutton, 0, 1, 0, 1);
657   gtk_widget_show (spinbutton);
658 
659   g_signal_connect (xadj, "value-changed",
660                     G_CALLBACK (load_dialog_ratio_callback),
661                     NULL);
662 
663   label = gtk_label_new_with_mnemonic (_("_X ratio:"));
664   gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
665   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
666   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
667                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
668   gtk_widget_show (label);
669 
670   yadj = (GtkAdjustment *)
671     gtk_adjustment_new (ratio_y,
672                         (gdouble) GIMP_MIN_IMAGE_SIZE / (gdouble) wmf_height,
673                         (gdouble) GIMP_MAX_IMAGE_SIZE / (gdouble) wmf_height,
674                         0.01, 0.1, 0);
675   spinbutton = gimp_spin_button_new (yadj, 0.01, 4);
676   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
677   gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
678   gtk_table_attach_defaults (GTK_TABLE (table2), spinbutton, 0, 1, 1, 2);
679   gtk_widget_show (spinbutton);
680 
681   g_signal_connect (yadj, "value-changed",
682                     G_CALLBACK (load_dialog_ratio_callback),
683                     NULL);
684 
685   label = gtk_label_new_with_mnemonic (_("_Y ratio:"));
686   gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
687   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
688   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
689                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
690   gtk_widget_show (label);
691 
692   /*  the constrain ratio chainbutton  */
693   constrain = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
694   gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (constrain), TRUE);
695   gtk_table_attach_defaults (GTK_TABLE (table2), constrain, 1, 2, 0, 2);
696   gtk_widget_show (constrain);
697 
698   gimp_help_set_help_data (GIMP_CHAIN_BUTTON (constrain)->button,
699                            _("Constrain aspect ratio"), NULL);
700 
701   gtk_widget_show (table2);
702 
703   /*  Resolution   */
704   label = gtk_label_new (_("Resolution:"));
705   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
706   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5,
707                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
708   gtk_widget_show (label);
709 
710   res = gimp_size_entry_new (1, GIMP_UNIT_INCH, _("pixels/%a"),
711                              FALSE, FALSE, FALSE, 10,
712                              GIMP_SIZE_ENTRY_UPDATE_RESOLUTION);
713   gtk_table_set_col_spacing (GTK_TABLE (res), 1, 6);
714 
715   gtk_table_attach (GTK_TABLE (table), res, 1, 2, 4, 5,
716                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
717   gtk_widget_show (res);
718 
719   /* don't let the resolution become too small ? does libwmf tend to
720      crash with very small resolutions */
721   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (res), 0,
722                                          5.0, GIMP_MAX_RESOLUTION);
723   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (res), 0, load_vals.resolution);
724 
725   g_signal_connect (res, "value-changed",
726                     G_CALLBACK (load_dialog_resolution_callback),
727                     (gpointer) filename);
728 
729   gtk_widget_show (dialog);
730 
731   if (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK)
732     {
733       load_vals.width  = ROUND (gimp_size_entry_get_refval (size, 0));
734       load_vals.height = ROUND (gimp_size_entry_get_refval (size, 1));
735       run = TRUE;
736     }
737 
738   gtk_widget_destroy (GTK_WIDGET (dialog));
739 
740   return run;
741 }
742 
743 static guchar *
pixbuf_gd_convert(const gint * gd_pixels,gint width,gint height)744 pixbuf_gd_convert (const gint *gd_pixels,
745                    gint        width,
746                    gint        height)
747 {
748   const gint *gd_ptr = gd_pixels;
749   guchar     *pixels;
750   guchar     *px_ptr;
751   gint        i, j;
752 
753   pixels = (guchar *) g_try_malloc (width * height * sizeof (guchar) * 4);
754   if (! pixels)
755     return NULL;
756 
757   px_ptr = pixels;
758 
759   for (i = 0; i < height; i++)
760     for (j = 0; j < width; j++)
761       {
762         guchar r, g, b, a;
763         guint  pixel = (guint) (*gd_ptr++);
764 
765         b = (guchar) (pixel & 0xff);
766         pixel >>= 8;
767         g = (guchar) (pixel & 0xff);
768         pixel >>= 8;
769         r = (guchar) (pixel & 0xff);
770         pixel >>= 7;
771         a = (guchar) (pixel & 0xfe);
772         a ^= 0xff;
773 
774         *px_ptr++ = r;
775         *px_ptr++ = g;
776         *px_ptr++ = b;
777         *px_ptr++ = a;
778       }
779 
780   return pixels;
781 }
782 
783 static guchar *
wmf_get_pixbuf(const gchar * filename,gint * width,gint * height)784 wmf_get_pixbuf (const gchar *filename,
785                 gint        *width,
786                 gint        *height)
787 {
788   GMappedFile    *file;
789   guchar         *pixels   = NULL;
790 
791   /* the bits we need to decode the WMF via libwmf2's GD layer  */
792   wmf_error_t     err;
793   gulong          flags;
794   wmf_gd_t       *ddata = NULL;
795   wmfAPI         *API   = NULL;
796   wmfAPI_Options  api_options;
797   guint           file_width;
798   guint           file_height;
799   wmfD_Rect       bbox;
800   gint           *gd_pixels = NULL;
801   char*           wmffontdirs[2] = { NULL, NULL };
802 
803   file = g_mapped_file_new (filename, FALSE, NULL);
804   if (! file)
805     return NULL;
806 
807   flags = WMF_OPT_IGNORE_NONFATAL | WMF_OPT_FUNCTION;
808   api_options.function = wmf_gd_function;
809 
810 #ifdef ENABLE_RELOCATABLE_RESOURCES
811   wmffontdirs[0] = g_build_filename (gimp_installation_directory (),
812                                      "share/libwmf/fonts", NULL);
813   flags |= WMF_OPT_FONTDIRS;
814   api_options.fontdirs = wmffontdirs;
815 #endif
816 
817   err = wmf_api_create (&API, flags, &api_options);
818   if (wmffontdirs[0])
819     g_free (wmffontdirs[0]);
820   if (err != wmf_E_None)
821     goto _wmf_error;
822 
823   ddata = WMF_GD_GetData (API);
824   ddata->type = wmf_gd_image;
825 
826   err = wmf_mem_open (API,
827                       (guchar *) g_mapped_file_get_contents (file),
828                       g_mapped_file_get_length (file));
829   if (err != wmf_E_None)
830     goto _wmf_error;
831 
832   err = wmf_scan (API, 0, &bbox);
833   if (err != wmf_E_None)
834     goto _wmf_error;
835 
836   err = wmf_display_size (API, &file_width, &file_height,
837                           WMF_DEFAULT_RESOLUTION, WMF_DEFAULT_RESOLUTION);
838   if (err != wmf_E_None || file_width <= 0 || file_height <= 0)
839     goto _wmf_error;
840 
841   if (!*width || !*height)
842     goto _wmf_error;
843 
844   /*  either both arguments negative or none  */
845   if ((*width * *height) < 0)
846     goto _wmf_error;
847 
848   ddata->bbox   = bbox;
849 
850   if (*width > 0)
851     {
852       ddata->width  = *width;
853       ddata->height = *height;
854     }
855   else
856     {
857       gdouble w      = file_width;
858       gdouble h      = file_height;
859       gdouble aspect = ((gdouble) *width) / (gdouble) *height;
860 
861       if (aspect > (w / h))
862         {
863           ddata->height = abs (*height);
864           ddata->width  = (gdouble) abs (*width) * (w / h) + 0.5;
865         }
866       else
867         {
868           ddata->width  = abs (*width);
869           ddata->height = (gdouble) abs (*height) / (w / h) + 0.5;
870         }
871     }
872 
873   err = wmf_play (API, 0, &bbox);
874   if (err != wmf_E_None)
875     goto _wmf_error;
876 
877   if (ddata->gd_image != NULL)
878     gd_pixels = wmf_gd_image_pixels (ddata->gd_image);
879   if (gd_pixels == NULL)
880     goto _wmf_error;
881 
882   pixels = pixbuf_gd_convert (gd_pixels, ddata->width, ddata->height);
883   if (pixels == NULL)
884     goto _wmf_error;
885 
886   *width = ddata->width;
887   *height = ddata->height;
888 
889  _wmf_error:
890   if (API)
891     {
892       wmf_mem_close (API);
893       wmf_api_destroy (API);
894     }
895 
896   g_mapped_file_unref (file);
897 
898   return pixels;
899 }
900 
901 static guchar *
wmf_load_file(const gchar * filename,guint * width,guint * height,GError ** error)902 wmf_load_file (const gchar  *filename,
903                guint        *width,
904                guint        *height,
905                GError      **error)
906 {
907   GMappedFile    *file;
908   guchar         *pixels   = NULL;
909 
910   /* the bits we need to decode the WMF via libwmf2's GD layer  */
911   wmf_error_t     err;
912   gulong          flags;
913   wmf_gd_t       *ddata = NULL;
914   wmfAPI         *API   = NULL;
915   wmfAPI_Options  api_options;
916   wmfD_Rect       bbox;
917   gint           *gd_pixels = NULL;
918   char*           wmffontdirs[2] = { NULL, NULL };
919 
920   *width = *height = -1;
921 
922   file = g_mapped_file_new (filename, FALSE, error);
923   if (! file)
924     return NULL;
925 
926   flags = WMF_OPT_IGNORE_NONFATAL | WMF_OPT_FUNCTION;
927   api_options.function = wmf_gd_function;
928 
929 #ifdef ENABLE_RELOCATABLE_RESOURCES
930   wmffontdirs[0] = g_build_filename (gimp_installation_directory (),
931                                      "share/libwmf/fonts/", NULL);
932   flags |= WMF_OPT_FONTDIRS;
933   api_options.fontdirs = wmffontdirs;
934 #endif
935 
936   err = wmf_api_create (&API, flags, &api_options);
937   if (wmffontdirs[0])
938     g_free (wmffontdirs[0]);
939   if (err != wmf_E_None)
940     goto _wmf_error;
941 
942   ddata = WMF_GD_GetData (API);
943   ddata->type = wmf_gd_image;
944 
945   err = wmf_mem_open (API,
946                       (guchar *) g_mapped_file_get_contents (file),
947                       g_mapped_file_get_length (file));
948   if (err != wmf_E_None)
949     goto _wmf_error;
950 
951   err = wmf_scan (API, 0, &bbox);
952   if (err != wmf_E_None)
953     goto _wmf_error;
954 
955   err = wmf_display_size (API,
956                           width, height,
957                           load_vals.resolution, load_vals.resolution);
958   if (err != wmf_E_None || *width <= 0 || *height <= 0)
959     goto _wmf_error;
960 
961   if (load_vals.width > 0 && load_vals.height > 0)
962     {
963       *width  = load_vals.width;
964       *height = load_vals.height;
965     }
966 
967   ddata->bbox   = bbox;
968   ddata->width  = *width;
969   ddata->height = *height;
970 
971   err = wmf_play (API, 0, &bbox);
972   if (err != wmf_E_None)
973     goto _wmf_error;
974 
975   if (ddata->gd_image != NULL)
976     gd_pixels = wmf_gd_image_pixels (ddata->gd_image);
977   if (gd_pixels == NULL)
978     goto _wmf_error;
979 
980   pixels = pixbuf_gd_convert (gd_pixels, *width, *height);
981   if (pixels == NULL)
982     goto _wmf_error;
983 
984  _wmf_error:
985   if (API)
986     {
987       wmf_mem_close (API);
988       wmf_api_destroy (API);
989     }
990 
991   g_mapped_file_unref (file);
992 
993   /* FIXME: improve error message */
994   g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
995                _("Could not open '%s' for reading"),
996                gimp_filename_to_utf8 (filename));
997 
998   return pixels;
999 }
1000 
1001 /*
1002  * 'load_image()' - Load a WMF image into a new image window.
1003  */
1004 static gint32
load_image(const gchar * filename,GError ** error)1005 load_image (const gchar  *filename,
1006             GError      **error)
1007 {
1008   gint32        image;
1009   gint32        layer;
1010   GeglBuffer   *buffer;
1011   guchar       *pixels;
1012   guint         width, height;
1013 
1014   gimp_progress_init_printf (_("Opening '%s'"),
1015                              gimp_filename_to_utf8 (filename));
1016 
1017   pixels = wmf_load_file (filename, &width, &height, error);
1018 
1019   if (! pixels)
1020     return -1;
1021 
1022   image = gimp_image_new (width, height, GIMP_RGB);
1023   gimp_image_set_filename (image, filename);
1024   gimp_image_set_resolution (image,
1025                              load_vals.resolution, load_vals.resolution);
1026 
1027   layer = gimp_layer_new (image,
1028                           _("Rendered WMF"),
1029                           width, height,
1030                           GIMP_RGBA_IMAGE,
1031                           100,
1032                           gimp_image_get_default_new_layer_mode (image));
1033 
1034   buffer = gimp_drawable_get_buffer (layer);
1035 
1036   gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
1037                    babl_format ("R'G'B'A u8"),
1038                    pixels, GEGL_AUTO_ROWSTRIDE);
1039 
1040   g_object_unref (buffer);
1041 
1042   g_free (pixels);
1043 
1044   gimp_image_insert_layer (image, layer, -1, 0);
1045 
1046   gimp_progress_update (1.0);
1047 
1048   return image;
1049 }
1050