1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 * PostScript file plugin
4 * PostScript writing and GhostScript interfacing code
5 * Copyright (C) 1997-98 Peter Kirchgessner
6 * (email: peter@kirchgessner.net, WWW: http://www.kirchgessner.net)
7 *
8 * Added controls for TextAlphaBits and GraphicsAlphaBits
9 * George White <aa056@chebucto.ns.ca>
10 *
11 * Added Ascii85 encoding
12 * Austin Donnelly <austin@gimp.org>
13 *
14 * This program is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <https://www.gnu.org/licenses/>.
26 *
27 */
28
29 /* Event history:
30 * V 0.90, PK, 28-Mar-97: Creation.
31 * V 0.91, PK, 03-Apr-97: Clip everything outside BoundingBox.
32 * 24-Apr-97: Multi page read support.
33 * V 1.00, PK, 30-Apr-97: PDF support.
34 * V 1.01, PK, 05-Oct-97: Parse rc-file.
35 * V 1.02, GW, 09-Oct-97: Antialiasing support.
36 * PK, 11-Oct-97: No progress bars when running non-interactive.
37 * New procedure file_ps_load_setargs to set
38 * load-arguments non-interactively.
39 * If GS_OPTIONS are not set, use at least "-dSAFER"
40 * V 1.03, nn, 20-Dec-97: Initialize some variables
41 * V 1.04, PK, 20-Dec-97: Add Encapsulated PostScript output and preview
42 * V 1.05, PK, 21-Sep-98: Write b/w-images (indexed) using image-operator
43 * V 1.06, PK, 22-Dec-98: Fix problem with writing color PS files.
44 * Ghostview may hang when displaying the files.
45 * V 1.07, PK, 14-Sep-99: Add resolution to image
46 * V 1.08, PK, 16-Jan-2000: Add PostScript-Level 2 by Austin Donnelly
47 * V 1.09, PK, 15-Feb-2000: Force showpage on EPS-files
48 * Add "RunLength" compression
49 * Fix problem with "Level 2" toggle
50 * V 1.10, PK, 15-Mar-2000: For load EPSF, allow negative Bounding Box Values
51 * Save PS: don't start lines of image data with %%
52 * to prevent problems with stupid PostScript
53 * analyzer programs (Stanislav Brabec)
54 * Add BeginData/EndData comments
55 * Save PS: Set default rotation to 0
56 * V 1.11, PK, 20-Aug-2000: Fix problem with BoundingBox recognition
57 * for Mac files.
58 * Fix problem with loop when reading not all
59 * images of a multi page file.
60 * PK, 31-Aug-2000: Load PS: Add checks for space in filename.
61 * V 1.12 PK, 19-Jun-2001: Fix problem with command line switch --
62 * (reported by Ferenc Wagner)
63 * V 1.13 PK, 07-Apr-2002: Fix problem with DOS binary EPS files
64 * V 1.14 PK, 14-May-2002: Workaround EPS files of Adb. Ill. 8.0
65 * V 1.15 PK, 04-Oct-2002: Be more accurate with using BoundingBox
66 * V 1.16 PK, 22-Jan-2004: Don't use popen(), use g_spawn_async_with_pipes()
67 * or g_spawn_sync().
68 * V 1.17 PK, 19-Sep-2004: Fix problem with interpretation of bounding box
69 */
70
71 #include "config.h"
72
73 #include <errno.h>
74 #include <string.h>
75 #include <time.h>
76
77 #include <sys/types.h>
78
79 #ifdef HAVE_UNISTD_H
80 #include <unistd.h>
81 #endif
82
83 #include <glib/gstdio.h>
84
85 #include <libgimp/gimp.h>
86 #include <libgimp/gimpui.h>
87
88 #include "libgimp/stdplugins-intl.h"
89
90 #include <ghostscript/ierrors.h>
91 #include <ghostscript/iapi.h>
92 #include <ghostscript/gdevdsp.h>
93
94 #define VERSIO 1.17
95 static const gchar dversio[] = "v1.17 19-Sep-2004";
96
97 #define LOAD_PS_PROC "file-ps-load"
98 #define LOAD_EPS_PROC "file-eps-load"
99 #define LOAD_PS_SETARGS_PROC "file-ps-load-setargs"
100 #define LOAD_PS_THUMB_PROC "file-ps-load-thumb"
101 #define SAVE_PS_PROC "file-ps-save"
102 #define SAVE_EPS_PROC "file-eps-save"
103 #define PLUG_IN_BINARY "file-ps"
104 #define PLUG_IN_ROLE "gimp-file-ps"
105
106
107 #define STR_LENGTH 64
108 #define MIN_RESOLUTION 5
109 #define MAX_RESOLUTION 8192
110
111 /* Load info */
112 typedef struct
113 {
114 guint resolution; /* resolution (dpi) at which to run ghostscript */
115 guint width, height; /* desired size (ghostscript may ignore this) */
116 gboolean use_bbox; /* 0: use width/height, 1: try to use BoundingBox */
117 gchar pages[STR_LENGTH]; /* Pages to load (eg.: 1,3,5-7) */
118 gint pnm_type; /* 4: pbm, 5: pgm, 6: ppm, 7: automatic */
119 gint textalpha; /* antialiasing: 1,2, or 4 TextAlphaBits */
120 gint graphicsalpha; /* antialiasing: 1,2, or 4 GraphicsAlphaBits */
121 } PSLoadVals;
122
123 static PSLoadVals plvals =
124 {
125 100, /* 100 dpi */
126 826, 1170, /* default width/height (A4) */
127 TRUE, /* try to use BoundingBox */
128 "1", /* pages to load */
129 6, /* use ppm (color) */
130 1, /* don't use text antialiasing */
131 1 /* don't use graphics antialiasing */
132 };
133
134 /* Widgets for width and height of PostScript image to
135 * be loaded, so that they can be updated when desired resolution is
136 * changed
137 */
138 static GtkWidget *ps_width_spinbutton;
139 static GtkWidget *ps_height_spinbutton;
140
141 /* Save info */
142 typedef struct
143 {
144 gdouble width, height; /* Size of image */
145 gdouble x_offset, y_offset; /* Offset to image on page */
146 gboolean unit_mm; /* Unit of measure (0: inch, 1: mm) */
147 gboolean keep_ratio; /* Keep aspect ratio */
148 gint rotate; /* Rotation (0, 90, 180, 270) */
149 gint level; /* PostScript Level */
150 gboolean eps; /* Encapsulated PostScript flag */
151 gboolean preview; /* Preview Flag */
152 gint preview_size; /* Preview size */
153 } PSSaveVals;
154
155 static PSSaveVals psvals =
156 {
157 287.0, 200.0, /* Image size (A4) */
158 5.0, 5.0, /* Offset */
159 TRUE, /* Unit is mm */
160 TRUE, /* Keep edge ratio */
161 0, /* Rotate */
162 2, /* PostScript Level */
163 FALSE, /* Encapsulated PostScript flag */
164 FALSE, /* Preview flag */
165 256 /* Preview size */
166 };
167
168 static const char hex[] = "0123456789abcdef";
169
170
171 /* Declare some local functions.
172 */
173 static void query (void);
174 static void run (const gchar *name,
175 gint nparams,
176 const GimpParam *param,
177 gint *nreturn_vals,
178 GimpParam **return_vals);
179
180 static gint32 load_image (const gchar *filename,
181 GError **error);
182 static gboolean save_image (GFile *file,
183 gint32 image_ID,
184 gint32 drawable_ID,
185 GError **error);
186
187 static gboolean save_ps_header (GOutputStream *output,
188 GFile *file,
189 GError **error);
190 static gboolean save_ps_setup (GOutputStream *output,
191 gint32 drawable_ID,
192 gint width,
193 gint height,
194 gint bpp,
195 GError **error);
196 static gboolean save_ps_trailer (GOutputStream *output,
197 GError **error);
198
199 static gboolean save_ps_preview (GOutputStream *output,
200 gint32 drawable_ID,
201 GError **error);
202
203 static gboolean save_gray (GOutputStream *output,
204 gint32 image_ID,
205 gint32 drawable_ID,
206 GError **error);
207 static gboolean save_bw (GOutputStream *output,
208 gint32 image_ID,
209 gint32 drawable_ID,
210 GError **error);
211 static gboolean save_index (GOutputStream *output,
212 gint32 image_ID,
213 gint32 drawable_ID,
214 GError **error);
215 static gboolean save_rgb (GOutputStream *output,
216 gint32 image_ID,
217 gint32 drawable_ID,
218 GError **error);
219
220 static gboolean print (GOutputStream *output,
221 GError **error,
222 const gchar *format,
223 ...) G_GNUC_PRINTF (3, 4);
224
225 static gint32 create_new_image (const gchar *filename,
226 guint pagenum,
227 guint width,
228 guint height,
229 GimpImageBaseType type,
230 gint32 *layer_ID);
231
232 static void check_load_vals (void);
233 static void check_save_vals (void);
234
235 static gint page_in_list (gchar *list,
236 guint pagenum);
237
238 static gint get_bbox (const gchar *filename,
239 gint *x0,
240 gint *y0,
241 gint *x1,
242 gint *y1);
243
244 static FILE * ps_open (const gchar *filename,
245 const PSLoadVals *loadopt,
246 gint *llx,
247 gint *lly,
248 gint *urx,
249 gint *ury,
250 gboolean *is_epsf);
251
252 static void ps_close (FILE *ifp);
253
254 static gboolean skip_ps (FILE *ifp);
255
256 static gint32 load_ps (const gchar *filename,
257 guint pagenum,
258 FILE *ifp,
259 gint llx,
260 gint lly,
261 gint urx,
262 gint ury);
263
264 static void dither_grey (const guchar *grey,
265 guchar *bw,
266 gint npix,
267 gint linecount);
268
269
270 /* Dialog-handling */
271
272 static gint32 count_ps_pages (const gchar *filename);
273 static gboolean load_dialog (const gchar *filename);
274 static void load_pages_entry_callback (GtkWidget *widget,
275 gpointer data);
276
277 static gboolean resolution_change_callback (GtkAdjustment *adjustment,
278 gpointer data);
279
280 typedef struct
281 {
282 GtkAdjustment *adjustment[4];
283 gint level;
284 } SaveDialogVals;
285
286 static gboolean save_dialog (void);
287 static void save_unit_toggle_update (GtkWidget *widget,
288 gpointer data);
289
290 const GimpPlugInInfo PLUG_IN_INFO =
291 {
292 NULL, /* init_proc */
293 NULL, /* quit_proc */
294 query, /* query_proc */
295 run, /* run_proc */
296 };
297
298
299 /* The run mode */
300 static GimpRunMode l_run_mode;
301
302 static void compress_packbits (int nin,
303 unsigned char *src,
304 int *nout,
305 unsigned char *dst);
306
307
308 static guint32 ascii85_buf = 0;
309 static gint ascii85_len = 0;
310 static gint ascii85_linewidth = 0;
311
312 static GimpPageSelectorTarget ps_pagemode = GIMP_PAGE_SELECTOR_TARGET_LAYERS;
313
314 static void
ascii85_init(void)315 ascii85_init (void)
316 {
317 ascii85_len = 0;
318 ascii85_linewidth = 0;
319 }
320
321 static gboolean
ascii85_flush(GOutputStream * output,GError ** error)322 ascii85_flush (GOutputStream *output,
323 GError **error)
324 {
325 gchar c[5];
326 gint i;
327 gboolean zero_case = (ascii85_buf == 0);
328 GString *string = g_string_new (NULL);
329
330 static gint max_linewidth = 75;
331
332 for (i = 4; i >= 0; i--)
333 {
334 c[i] = (ascii85_buf % 85) + '!';
335 ascii85_buf /= 85;
336 }
337
338 /* check for special case: "!!!!!" becomes "z", but only if not
339 * at end of data. */
340 if (zero_case && (ascii85_len == 4))
341 {
342 if (ascii85_linewidth >= max_linewidth)
343 {
344 g_string_append_c (string, '\n');
345
346 ascii85_linewidth = 0;
347 }
348
349 g_string_append_c (string, 'z');
350
351 ascii85_linewidth++;
352 }
353 else
354 {
355 for (i = 0; i < ascii85_len + 1; i++)
356 {
357 if ((ascii85_linewidth >= max_linewidth) && (c[i] != '%'))
358 {
359 g_string_append_c (string, '\n');
360
361 ascii85_linewidth = 0;
362 }
363
364 g_string_append_c (string, c[i]);
365
366 ascii85_linewidth++;
367 }
368 }
369
370 ascii85_len = 0;
371 ascii85_buf = 0;
372
373 if (string->len > 0 &&
374 ! g_output_stream_write_all (output,
375 string->str, string->len, NULL,
376 NULL, error))
377 {
378 g_string_free (string, TRUE);
379
380 return FALSE;
381 }
382
383 g_string_free (string, TRUE);
384
385 return TRUE;
386 }
387
388 static inline gboolean
ascii85_out(GOutputStream * output,guchar byte,GError ** error)389 ascii85_out (GOutputStream *output,
390 guchar byte,
391 GError **error)
392 {
393 if (ascii85_len == 4)
394 if (! ascii85_flush (output, error))
395 return FALSE;
396
397 ascii85_buf <<= 8;
398 ascii85_buf |= byte;
399 ascii85_len++;
400
401 return TRUE;
402 }
403
404 static gboolean
ascii85_nout(GOutputStream * output,gint n,guchar * uptr,GError ** error)405 ascii85_nout (GOutputStream *output,
406 gint n,
407 guchar *uptr,
408 GError **error)
409 {
410 while (n-- > 0)
411 {
412 if (! ascii85_out (output, *uptr, error))
413 return FALSE;
414
415 uptr++;
416 }
417
418 return TRUE;
419 }
420
421 static gboolean
ascii85_done(GOutputStream * output,GError ** error)422 ascii85_done (GOutputStream *output,
423 GError **error)
424 {
425 if (ascii85_len)
426 {
427 /* zero any unfilled buffer portion, then flush */
428 ascii85_buf <<= (8 * (4 - ascii85_len));
429
430 if (! ascii85_flush (output, error))
431 return FALSE;
432 }
433
434 if (! print (output, error, "~>\n"))
435 return FALSE;
436
437 return TRUE;
438 }
439
440
441 static void
compress_packbits(int nin,unsigned char * src,int * nout,unsigned char * dst)442 compress_packbits (int nin,
443 unsigned char *src,
444 int *nout,
445 unsigned char *dst)
446
447 {
448 unsigned char c;
449 int nrepeat, nliteral;
450 unsigned char *run_start;
451 unsigned char *start_dst = dst;
452 unsigned char *last_literal = NULL;
453
454 for (;;)
455 {
456 if (nin <= 0) break;
457
458 run_start = src;
459 c = *run_start;
460
461 /* Search repeat bytes */
462 if ((nin > 1) && (c == src[1]))
463 {
464 nrepeat = 1;
465 nin -= 2;
466 src += 2;
467 while ((nin > 0) && (c == *src))
468 {
469 nrepeat++;
470 src++;
471 nin--;
472 if (nrepeat == 127) break; /* Maximum repeat */
473 }
474
475 /* Add two-byte repeat to last literal run ? */
476 if ( (nrepeat == 1)
477 && (last_literal != NULL) && (((*last_literal)+1)+2 <= 128))
478 {
479 *last_literal += 2;
480 *(dst++) = c;
481 *(dst++) = c;
482 continue;
483 }
484
485 /* Add repeat run */
486 *(dst++) = (unsigned char)((-nrepeat) & 0xff);
487 *(dst++) = c;
488 last_literal = NULL;
489 continue;
490 }
491 /* Search literal bytes */
492 nliteral = 1;
493 nin--;
494 src++;
495
496 for (;;)
497 {
498 if (nin <= 0) break;
499
500 if ((nin >= 2) && (src[0] == src[1])) /* A two byte repeat ? */
501 break;
502
503 nliteral++;
504 nin--;
505 src++;
506 if (nliteral == 128) break; /* Maximum literal run */
507 }
508
509 /* Could be added to last literal run ? */
510 if ((last_literal != NULL) && (((*last_literal)+1)+nliteral <= 128))
511 {
512 *last_literal += nliteral;
513 }
514 else
515 {
516 last_literal = dst;
517 *(dst++) = (unsigned char)(nliteral-1);
518 }
519 while (nliteral-- > 0) *(dst++) = *(run_start++);
520 }
521 *nout = dst - start_dst;
522 }
523
524
525 typedef struct
526 {
527 goffset eol;
528 goffset begin_data;
529 } PS_DATA_POS;
530
531 static PS_DATA_POS ps_data_pos = { 0, 0 };
532
533 static gboolean
ps_begin_data(GOutputStream * output,GError ** error)534 ps_begin_data (GOutputStream *output,
535 GError **error)
536 {
537 /* %%BeginData: 123456789012 ASCII Bytes */
538 if (! print (output, error, "%s", "%%BeginData: "))
539 return FALSE;
540
541 ps_data_pos.eol = g_seekable_tell (G_SEEKABLE (output));
542
543 if (! print (output, error, "\n"))
544 return FALSE;
545
546 ps_data_pos.begin_data = g_seekable_tell (G_SEEKABLE (output));
547
548 return TRUE;
549 }
550
551 static gboolean
ps_end_data(GOutputStream * output,GError ** error)552 ps_end_data (GOutputStream *output,
553 GError **error)
554 {
555 goffset end_data;
556 gchar s[64];
557
558 if ((ps_data_pos.begin_data > 0) && (ps_data_pos.eol > 0))
559 {
560 end_data = g_seekable_tell (G_SEEKABLE (output));
561
562 if (end_data > 0)
563 {
564 g_snprintf (s, sizeof (s),
565 "%"G_GOFFSET_FORMAT" ASCII Bytes", end_data - ps_data_pos.begin_data);
566
567 if (! g_seekable_seek (G_SEEKABLE (output),
568 ps_data_pos.eol - strlen (s), G_SEEK_SET,
569 NULL, error))
570 return FALSE;
571
572 if (! print (output, error, "%s", s))
573 return FALSE;
574
575 if (! g_seekable_seek (G_SEEKABLE (output),
576 end_data, G_SEEK_SET,
577 NULL, error))
578 return FALSE;
579 }
580 }
581
582 if (! print (output, error, "%s\n", "%%EndData"))
583 return FALSE;
584
585 return TRUE;
586 }
587
588
MAIN()589 MAIN ()
590
591 static void
592 query (void)
593 {
594 static const GimpParamDef load_args[] =
595 {
596 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
597 { GIMP_PDB_STRING, "filename", "The name of the file to load" },
598 { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
599 };
600 static const GimpParamDef load_return_vals[] =
601 {
602 { GIMP_PDB_IMAGE, "image", "Output image" }
603 };
604
605 static const GimpParamDef set_load_args[] =
606 {
607 { GIMP_PDB_INT32, "resolution", "Resolution to interpret image (dpi)" },
608 { GIMP_PDB_INT32, "width", "Desired width" },
609 { GIMP_PDB_INT32, "height", "Desired height" },
610 { GIMP_PDB_INT32, "check-bbox", "0: Use width/height, 1: Use BoundingBox" },
611 { GIMP_PDB_STRING, "pages", "Pages to load (e.g.: 1,3,5-7)" },
612 { GIMP_PDB_INT32, "coloring", "4: b/w, 5: grey, 6: color image, 7: automatic" },
613 { GIMP_PDB_INT32, "text-alpha-bits", "1, 2, or 4" },
614 { GIMP_PDB_INT32, "graphic-alpha-bits", "1, 2, or 4" }
615 };
616
617 static const GimpParamDef thumb_args[] =
618 {
619 { GIMP_PDB_STRING, "filename", "The name of the file to load" },
620 { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
621 };
622 static const GimpParamDef thumb_return_vals[] =
623 {
624 { GIMP_PDB_IMAGE, "image", "Output image" }
625 };
626
627 static const GimpParamDef save_args[] =
628 {
629 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
630 { GIMP_PDB_IMAGE, "image", "Input image" },
631 { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
632 { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
633 { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" },
634 { GIMP_PDB_FLOAT, "width", "Width of the image in PostScript file (0: use input image size)" },
635 { GIMP_PDB_FLOAT, "height", "Height of image in PostScript file (0: use input image size)" },
636 { GIMP_PDB_FLOAT, "x-offset", "X-offset to image from lower left corner" },
637 { GIMP_PDB_FLOAT, "y-offset", "Y-offset to image from lower left corner" },
638 { GIMP_PDB_INT32, "unit", "Unit for width/height/offset. 0: inches, 1: millimeters" },
639 { GIMP_PDB_INT32, "keep-ratio", "0: use width/height, 1: keep aspect ratio" },
640 { GIMP_PDB_INT32, "rotation", "0, 90, 180, 270" },
641 { GIMP_PDB_INT32, "eps-flag", "0: PostScript, 1: Encapsulated PostScript" },
642 { GIMP_PDB_INT32, "preview", "0: no preview, >0: max. size of preview" },
643 { GIMP_PDB_INT32, "level", "1: PostScript Level 1, 2: PostScript Level 2" }
644 };
645
646 gimp_install_procedure (LOAD_PS_PROC,
647 "load PostScript documents",
648 "load PostScript documents",
649 "Peter Kirchgessner <peter@kirchgessner.net>",
650 "Peter Kirchgessner",
651 dversio,
652 N_("PostScript document"),
653 NULL,
654 GIMP_PLUGIN,
655 G_N_ELEMENTS (load_args),
656 G_N_ELEMENTS (load_return_vals),
657 load_args, load_return_vals);
658
659 gimp_register_file_handler_mime (LOAD_PS_PROC, "application/postscript");
660 gimp_register_magic_load_handler (LOAD_PS_PROC,
661 "ps",
662 "",
663 "0,string,%!,0,long,0xc5d0d3c6");
664
665 gimp_install_procedure (LOAD_EPS_PROC,
666 "load Encapsulated PostScript images",
667 "load Encapsulated PostScript images",
668 "Peter Kirchgessner <peter@kirchgessner.net>",
669 "Peter Kirchgessner",
670 dversio,
671 N_("Encapsulated PostScript image"),
672 NULL,
673 GIMP_PLUGIN,
674 G_N_ELEMENTS (load_args),
675 G_N_ELEMENTS (load_return_vals),
676 load_args, load_return_vals);
677
678 gimp_register_file_handler_mime (LOAD_EPS_PROC, "image/x-eps");
679 gimp_register_magic_load_handler (LOAD_EPS_PROC,
680 "eps",
681 "",
682 "0,string,%!,0,long,0xc5d0d3c6");
683
684 gimp_install_procedure (LOAD_PS_SETARGS_PROC,
685 "set additional parameters for procedure file-ps-load",
686 "set additional parameters for procedure file-ps-load",
687 "Peter Kirchgessner <peter@kirchgessner.net>",
688 "Peter Kirchgessner",
689 dversio,
690 NULL,
691 NULL,
692 GIMP_PLUGIN,
693 G_N_ELEMENTS (set_load_args), 0,
694 set_load_args, NULL);
695
696 gimp_install_procedure (LOAD_PS_THUMB_PROC,
697 "Loads a small preview from a PostScript or PDF document",
698 "",
699 "Peter Kirchgessner <peter@kirchgessner.net>",
700 "Peter Kirchgessner",
701 dversio,
702 NULL,
703 NULL,
704 GIMP_PLUGIN,
705 G_N_ELEMENTS (thumb_args),
706 G_N_ELEMENTS (thumb_return_vals),
707 thumb_args, thumb_return_vals);
708
709 gimp_register_thumbnail_loader (LOAD_PS_PROC, LOAD_PS_THUMB_PROC);
710 gimp_register_thumbnail_loader (LOAD_EPS_PROC, LOAD_PS_THUMB_PROC);
711
712 gimp_install_procedure (SAVE_PS_PROC,
713 "export image as PostScript document",
714 "PostScript exporting handles all image types except "
715 "those with alpha channels.",
716 "Peter Kirchgessner <peter@kirchgessner.net>",
717 "Peter Kirchgessner",
718 dversio,
719 N_("PostScript document"),
720 "RGB, GRAY, INDEXED",
721 GIMP_PLUGIN,
722 G_N_ELEMENTS (save_args), 0,
723 save_args, NULL);
724
725 gimp_register_file_handler_mime (SAVE_PS_PROC, "application/postscript");
726 gimp_register_file_handler_uri (SAVE_PS_PROC);
727 gimp_register_save_handler (SAVE_PS_PROC, "ps", "");
728
729 gimp_install_procedure (SAVE_EPS_PROC,
730 "export image as Encapsulated PostScript image",
731 "PostScript exporting handles all image types except "
732 "those with alpha channels.",
733 "Peter Kirchgessner <peter@kirchgessner.net>",
734 "Peter Kirchgessner",
735 dversio,
736 N_("Encapsulated PostScript image"),
737 "RGB, GRAY, INDEXED",
738 GIMP_PLUGIN,
739 G_N_ELEMENTS (save_args), 0,
740 save_args, NULL);
741
742 gimp_register_file_handler_mime (SAVE_EPS_PROC, "application/x-eps");
743 gimp_register_file_handler_uri (SAVE_EPS_PROC);
744 gimp_register_save_handler (SAVE_EPS_PROC, "eps", "");
745 }
746
747 static void
ps_set_save_size(PSSaveVals * vals,gint32 image_ID)748 ps_set_save_size (PSSaveVals *vals,
749 gint32 image_ID)
750 {
751 gdouble xres, yres, factor, iw, ih;
752 guint width, height;
753 GimpUnit unit;
754
755 gimp_image_get_resolution (image_ID, &xres, &yres);
756
757 if ((xres < 1e-5) || (yres < 1e-5))
758 xres = yres = 72.0;
759
760 /* Calculate size of image in inches */
761 width = gimp_image_width (image_ID);
762 height = gimp_image_height (image_ID);
763 iw = width / xres;
764 ih = height / yres;
765
766 unit = gimp_image_get_unit (image_ID);
767 factor = gimp_unit_get_factor (unit);
768
769 if (factor == 0.0254 ||
770 factor == 0.254 ||
771 factor == 2.54 ||
772 factor == 25.4)
773 {
774 vals->unit_mm = TRUE;
775 }
776
777 if (vals->unit_mm)
778 {
779 iw *= 25.4;
780 ih *= 25.4;
781 }
782
783 vals->width = iw;
784 vals->height = ih;
785 }
786
787 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)788 run (const gchar *name,
789 gint nparams,
790 const GimpParam *param,
791 gint *nreturn_vals,
792 GimpParam **return_vals)
793 {
794 static GimpParam values[2];
795 GimpRunMode run_mode;
796 GimpPDBStatusType status = GIMP_PDB_SUCCESS;
797 gint32 image_ID = -1;
798 gint32 drawable_ID = -1;
799 gint32 orig_image_ID = -1;
800 GimpExportReturn export = GIMP_EXPORT_CANCEL;
801 GError *error = NULL;
802
803 l_run_mode = run_mode = param[0].data.d_int32;
804
805 INIT_I18N ();
806 gegl_init (NULL, NULL);
807
808 *nreturn_vals = 1;
809 *return_vals = values;
810
811 values[0].type = GIMP_PDB_STATUS;
812 values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
813
814 if (strcmp (name, LOAD_PS_PROC) == 0 ||
815 strcmp (name, LOAD_EPS_PROC) == 0)
816 {
817 switch (run_mode)
818 {
819 case GIMP_RUN_INTERACTIVE:
820 /* Possibly retrieve data */
821 gimp_get_data (LOAD_PS_PROC, &plvals);
822
823 if (! load_dialog (param[1].data.d_string))
824 status = GIMP_PDB_CANCEL;
825 break;
826
827 case GIMP_RUN_NONINTERACTIVE:
828 /* Make sure all the arguments are there! */
829 if (nparams != 3)
830 status = GIMP_PDB_CALLING_ERROR;
831 else /* Get additional interpretation arguments */
832 gimp_get_data (LOAD_PS_PROC, &plvals);
833 break;
834
835 case GIMP_RUN_WITH_LAST_VALS:
836 /* Possibly retrieve data */
837 gimp_get_data (LOAD_PS_PROC, &plvals);
838 break;
839
840 default:
841 break;
842 }
843
844 if (status == GIMP_PDB_SUCCESS)
845 {
846 check_load_vals ();
847 image_ID = load_image (param[1].data.d_string, &error);
848
849 if (image_ID != -1)
850 {
851 *nreturn_vals = 2;
852 values[1].type = GIMP_PDB_IMAGE;
853 values[1].data.d_image = image_ID;
854 }
855 else
856 {
857 status = GIMP_PDB_EXECUTION_ERROR;
858 }
859 }
860
861 /* Store plvals data */
862 if (status == GIMP_PDB_SUCCESS)
863 gimp_set_data (LOAD_PS_PROC, &plvals, sizeof (PSLoadVals));
864 }
865 else if (strcmp (name, LOAD_PS_THUMB_PROC) == 0)
866 {
867 if (nparams < 2)
868 {
869 status = GIMP_PDB_CALLING_ERROR;
870 }
871 else
872 {
873 gint size = param[1].data.d_int32;
874
875 /* We should look for an embedded preview but for now we
876 * just load the document at a small resolution and the
877 * first page only.
878 */
879
880 plvals.resolution = size / 4;
881 plvals.width = size;
882 plvals.height = size;
883 strncpy (plvals.pages, "1", sizeof (plvals.pages) - 1);
884
885 check_load_vals ();
886 image_ID = load_image (param[0].data.d_string, &error);
887
888 if (image_ID != -1)
889 {
890 *nreturn_vals = 2;
891 values[1].type = GIMP_PDB_IMAGE;
892 values[1].data.d_image = image_ID;
893 }
894 else
895 {
896 status = GIMP_PDB_EXECUTION_ERROR;
897 }
898 }
899 }
900 else if (strcmp (name, SAVE_PS_PROC) == 0 ||
901 strcmp (name, SAVE_EPS_PROC) == 0)
902 {
903 psvals.eps = strcmp (name, SAVE_PS_PROC);
904
905 image_ID = orig_image_ID = param[1].data.d_int32;
906 drawable_ID = param[2].data.d_int32;
907
908 /* eventually export the image */
909 switch (run_mode)
910 {
911 case GIMP_RUN_INTERACTIVE:
912 case GIMP_RUN_WITH_LAST_VALS:
913 gimp_ui_init (PLUG_IN_BINARY, FALSE);
914
915 export = gimp_export_image (&image_ID, &drawable_ID,
916 psvals.eps ? "EPS" : "PostScript",
917 GIMP_EXPORT_CAN_HANDLE_RGB |
918 GIMP_EXPORT_CAN_HANDLE_GRAY |
919 GIMP_EXPORT_CAN_HANDLE_INDEXED);
920
921 if (export == GIMP_EXPORT_CANCEL)
922 {
923 values[0].data.d_status = GIMP_PDB_CANCEL;
924 return;
925 }
926 break;
927 default:
928 break;
929 }
930
931 switch (run_mode)
932 {
933 case GIMP_RUN_INTERACTIVE:
934 /* Possibly retrieve data */
935 gimp_get_data (name, &psvals);
936
937 ps_set_save_size (&psvals, orig_image_ID);
938
939 /* First acquire information with a dialog */
940 if (! save_dialog ())
941 status = GIMP_PDB_CANCEL;
942 break;
943
944 case GIMP_RUN_NONINTERACTIVE:
945 /* Make sure all the arguments are there! */
946 if (nparams != 15)
947 {
948 status = GIMP_PDB_CALLING_ERROR;
949 }
950 else
951 {
952 psvals.width = param[5].data.d_float;
953 psvals.height = param[6].data.d_float;
954 psvals.x_offset = param[7].data.d_float;
955 psvals.y_offset = param[8].data.d_float;
956 psvals.unit_mm = (param[9].data.d_int32 != 0);
957 psvals.keep_ratio = (param[10].data.d_int32 != 0);
958 psvals.rotate = param[11].data.d_int32;
959 psvals.eps = (param[12].data.d_int32 != 0);
960 psvals.preview = (param[13].data.d_int32 != 0);
961 psvals.preview_size = param[13].data.d_int32;
962 psvals.level = param[14].data.d_int32;
963 }
964 break;
965
966 case GIMP_RUN_WITH_LAST_VALS:
967 /* Possibly retrieve data */
968 gimp_get_data (name, &psvals);
969 break;
970
971 default:
972 break;
973 }
974
975 if (status == GIMP_PDB_SUCCESS)
976 {
977 if ((psvals.width == 0.0) || (psvals.height == 0.0))
978 ps_set_save_size (&psvals, orig_image_ID);
979
980 check_save_vals ();
981
982 if (save_image (g_file_new_for_uri (param[3].data.d_string),
983 image_ID, drawable_ID,
984 &error))
985 {
986 /* Store psvals data */
987 gimp_set_data (name, &psvals, sizeof (PSSaveVals));
988 }
989 else
990 {
991 status = GIMP_PDB_EXECUTION_ERROR;
992 }
993 }
994
995 if (export == GIMP_EXPORT_EXPORT)
996 gimp_image_delete (image_ID);
997 }
998 else if (strcmp (name, LOAD_PS_SETARGS_PROC) == 0)
999 {
1000 /* Make sure all the arguments are there! */
1001 if (nparams != 8)
1002 {
1003 status = GIMP_PDB_CALLING_ERROR;
1004 }
1005 else
1006 {
1007 plvals.resolution = param[0].data.d_int32;
1008 plvals.width = param[1].data.d_int32;
1009 plvals.height = param[2].data.d_int32;
1010 plvals.use_bbox = param[3].data.d_int32;
1011 if (param[4].data.d_string != NULL)
1012 strncpy (plvals.pages, param[4].data.d_string,
1013 sizeof (plvals.pages));
1014 else
1015 plvals.pages[0] = '\0';
1016 plvals.pages[sizeof (plvals.pages) - 1] = '\0';
1017 plvals.pnm_type = param[5].data.d_int32;
1018 plvals.textalpha = param[6].data.d_int32;
1019 plvals.graphicsalpha = param[7].data.d_int32;
1020 check_load_vals ();
1021
1022 gimp_set_data (LOAD_PS_PROC, &plvals, sizeof (PSLoadVals));
1023 }
1024 }
1025 else
1026 {
1027 status = GIMP_PDB_CALLING_ERROR;
1028 }
1029
1030 if (status != GIMP_PDB_SUCCESS && error)
1031 {
1032 *nreturn_vals = 2;
1033 values[1].type = GIMP_PDB_STRING;
1034 values[1].data.d_string = error->message;
1035 }
1036
1037 values[0].data.d_status = status;
1038 }
1039
1040
1041 static gint32
load_image(const gchar * filename,GError ** error)1042 load_image (const gchar *filename,
1043 GError **error)
1044 {
1045 gint32 image_ID = 0;
1046 gint32 *image_list, *nl;
1047 guint page_count;
1048 FILE *ifp;
1049 gchar *temp;
1050 gint llx, lly, urx, ury;
1051 gint k, n_images, max_images, max_pagenum;
1052 gboolean is_epsf;
1053
1054 #ifdef PS_DEBUG
1055 g_print ("load_image:\n resolution = %d\n", plvals.resolution);
1056 g_print (" %dx%d pixels\n", plvals.width, plvals.height);
1057 g_print (" BoundingBox: %d\n", plvals.use_bbox);
1058 g_print (" Coloring: %d\n", plvals.pnm_type);
1059 g_print (" TextAlphaBits: %d\n", plvals.textalpha);
1060 g_print (" GraphicsAlphaBits: %d\n", plvals.graphicsalpha);
1061 #endif
1062
1063 gimp_progress_init_printf (_("Opening '%s'"),
1064 gimp_filename_to_utf8 (filename));
1065
1066 /* Try to see if PostScript file is available */
1067 ifp = g_fopen (filename, "r");
1068 if (ifp == NULL)
1069 {
1070 g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
1071 _("Could not open '%s' for reading: %s"),
1072 gimp_filename_to_utf8 (filename), g_strerror (errno));
1073 return -1;
1074 }
1075 fclose (ifp);
1076
1077 ifp = ps_open (filename, &plvals, &llx, &lly, &urx, &ury, &is_epsf);
1078 if (!ifp)
1079 {
1080 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR,
1081 _("Could not interpret PostScript file '%s'"),
1082 gimp_filename_to_utf8 (filename));
1083 return -1;
1084 }
1085
1086 image_list = g_new (gint32, 10);
1087 n_images = 0;
1088 max_images = 10;
1089
1090 max_pagenum = 9999; /* Try to get the maximum pagenumber to read */
1091 if (is_epsf)
1092 max_pagenum = 1;
1093
1094 if (!page_in_list (plvals.pages, max_pagenum)) /* Is there a limit in list ? */
1095 {
1096 max_pagenum = -1;
1097 for (temp = plvals.pages; *temp != '\0'; temp++)
1098 {
1099 if ((*temp < '0') || (*temp > '9'))
1100 continue; /* Search next digit */
1101 sscanf (temp, "%d", &k);
1102 if (k > max_pagenum)
1103 max_pagenum = k;
1104 while ((*temp >= '0') && (*temp <= '9'))
1105 temp++;
1106 temp--;
1107 }
1108
1109 if (max_pagenum < 1)
1110 max_pagenum = 9999;
1111 }
1112
1113 /* Load all images */
1114 for (page_count = 1; page_count <= max_pagenum; page_count++)
1115 {
1116 if (page_in_list (plvals.pages, page_count))
1117 {
1118 image_ID = load_ps (filename, page_count, ifp, llx, lly, urx, ury);
1119 if (image_ID == -1)
1120 break;
1121
1122 gimp_image_set_resolution (image_ID,
1123 (gdouble) plvals.resolution,
1124 (gdouble) plvals.resolution);
1125
1126 if (n_images == max_images)
1127 {
1128 nl = (gint32 *) g_realloc (image_list,
1129 (max_images+10)*sizeof (gint32));
1130 if (nl == NULL) break;
1131 image_list = nl;
1132 max_images += 10;
1133 }
1134 image_list[n_images++] = image_ID;
1135 }
1136 else /* Skip an image */
1137 {
1138 image_ID = -1;
1139 if (! skip_ps (ifp))
1140 break;
1141 }
1142 }
1143
1144 ps_close (ifp);
1145
1146 if (ps_pagemode == GIMP_PAGE_SELECTOR_TARGET_LAYERS)
1147 {
1148 for (k = 0; k < n_images; k++)
1149 {
1150 gchar *name;
1151
1152 if (k == 0)
1153 {
1154 image_ID = image_list[0];
1155
1156 name = g_strdup_printf (_("%s-pages"), filename);
1157 gimp_image_set_filename (image_ID, name);
1158 g_free (name);
1159 }
1160 else
1161 {
1162 gint32 current_layer;
1163 gint32 tmp_ID;
1164
1165 tmp_ID = gimp_image_get_active_drawable (image_list[k]);
1166
1167 name = gimp_item_get_name (tmp_ID);
1168
1169 current_layer = gimp_layer_new_from_drawable (tmp_ID, image_ID);
1170 gimp_item_set_name (current_layer, name);
1171 gimp_image_insert_layer (image_ID, current_layer, -1, -1);
1172 gimp_image_delete (image_list[k]);
1173
1174 g_free (name);
1175 }
1176 }
1177
1178 gimp_image_undo_enable (image_ID);
1179 }
1180 else
1181 {
1182 /* Display images in reverse order.
1183 * The last will be displayed by GIMP itself
1184 */
1185 for (k = n_images - 1; k >= 0; k--)
1186 {
1187 gimp_image_undo_enable (image_list[k]);
1188 gimp_image_clean_all (image_list[k]);
1189
1190 if (l_run_mode != GIMP_RUN_NONINTERACTIVE && k > 0)
1191 gimp_display_new (image_list[k]);
1192 }
1193
1194 image_ID = (n_images > 0) ? image_list[0] : -1;
1195 }
1196
1197 g_free (image_list);
1198
1199 return image_ID;
1200 }
1201
1202
1203 static gboolean
save_image(GFile * file,gint32 image_ID,gint32 drawable_ID,GError ** error)1204 save_image (GFile *file,
1205 gint32 image_ID,
1206 gint32 drawable_ID,
1207 GError **error)
1208 {
1209 GOutputStream *output;
1210 GCancellable *cancellable;
1211 GimpImageType drawable_type;
1212
1213 drawable_type = gimp_drawable_type (drawable_ID);
1214
1215 /* Make sure we're not exporting an image with an alpha channel */
1216 if (gimp_drawable_has_alpha (drawable_ID))
1217 {
1218 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
1219 _("PostScript export cannot handle images with alpha channels"));
1220 return FALSE;
1221 }
1222
1223 switch (drawable_type)
1224 {
1225 case GIMP_INDEXED_IMAGE:
1226 case GIMP_GRAY_IMAGE:
1227 case GIMP_RGB_IMAGE:
1228 break;
1229
1230 default:
1231 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
1232 _("Cannot operate on unknown image types."));
1233 return FALSE;
1234 break;
1235 }
1236
1237 gimp_progress_init_printf (_("Exporting '%s'"),
1238 gimp_file_get_utf8_name (file));
1239
1240 output = G_OUTPUT_STREAM (g_file_replace (file,
1241 NULL, FALSE, G_FILE_CREATE_NONE,
1242 NULL, error));
1243 if (output)
1244 {
1245 GOutputStream *buffered;
1246
1247 buffered = g_buffered_output_stream_new (output);
1248 g_object_unref (output);
1249
1250 output = buffered;
1251 }
1252 else
1253 {
1254 return FALSE;
1255 }
1256
1257 if (! save_ps_header (output, file, error))
1258 goto fail;
1259
1260 switch (drawable_type)
1261 {
1262 case GIMP_INDEXED_IMAGE:
1263 if (! save_index (output, image_ID, drawable_ID, error))
1264 goto fail;
1265 break;
1266
1267 case GIMP_GRAY_IMAGE:
1268 if (! save_gray (output, image_ID, drawable_ID, error))
1269 goto fail;
1270 break;
1271
1272 case GIMP_RGB_IMAGE:
1273 if (! save_rgb (output, image_ID, drawable_ID, error))
1274 goto fail;
1275 break;
1276
1277 default:
1278 g_return_val_if_reached (FALSE);
1279 }
1280
1281 if (! save_ps_trailer (output, error))
1282 goto fail;
1283
1284 if (! g_output_stream_close (output, NULL, error))
1285 goto fail;
1286
1287 g_object_unref (output);
1288
1289 return TRUE;
1290
1291 fail:
1292
1293 cancellable = g_cancellable_new ();
1294 g_cancellable_cancel (cancellable);
1295 g_output_stream_close (output, cancellable, NULL);
1296
1297 g_object_unref (output);
1298 g_object_unref (cancellable);
1299
1300 return FALSE;
1301 }
1302
1303
1304 /* Check (and correct) the load values plvals */
1305 static void
check_load_vals(void)1306 check_load_vals (void)
1307 {
1308 if (plvals.resolution < MIN_RESOLUTION)
1309 plvals.resolution = MIN_RESOLUTION;
1310 else if (plvals.resolution > MAX_RESOLUTION)
1311 plvals.resolution = MAX_RESOLUTION;
1312
1313 if (plvals.width < 2)
1314 plvals.width = 2;
1315 if (plvals.height < 2)
1316 plvals.height = 2;
1317 plvals.use_bbox = (plvals.use_bbox != 0);
1318 if (plvals.pages[0] == '\0')
1319 strncpy (plvals.pages, "1-99", sizeof (plvals.pages) - 1);
1320 if ((plvals.pnm_type < 4) || (plvals.pnm_type > 7))
1321 plvals.pnm_type = 6;
1322 if ( (plvals.textalpha != 1) && (plvals.textalpha != 2)
1323 && (plvals.textalpha != 4))
1324 plvals.textalpha = 1;
1325 if ( (plvals.graphicsalpha != 1) && (plvals.graphicsalpha != 2)
1326 && (plvals.graphicsalpha != 4))
1327 plvals.graphicsalpha = 1;
1328 }
1329
1330
1331 /* Check (and correct) the save values psvals */
1332 static void
check_save_vals(void)1333 check_save_vals (void)
1334 {
1335 int i;
1336
1337 i = psvals.rotate;
1338 if ((i != 0) && (i != 90) && (i != 180) && (i != 270))
1339 psvals.rotate = 90;
1340 if (psvals.preview_size <= 0)
1341 psvals.preview = FALSE;
1342 }
1343
1344
1345 /* Check if a page is in a given list */
1346 static gint
page_in_list(gchar * list,guint page_num)1347 page_in_list (gchar *list,
1348 guint page_num)
1349 {
1350 char tmplist[STR_LENGTH], *c0, *c1;
1351 int state, start_num, end_num;
1352 #define READ_STARTNUM 0
1353 #define READ_ENDNUM 1
1354 #define CHK_LIST(a,b,c) {int low=(a),high=(b),swp; \
1355 if ((low>0) && (high>0)) { \
1356 if (low>high) {swp=low; low=high; high=swp;} \
1357 if ((low<=(c))&&(high>=(c))) return (1); } }
1358
1359 if ((list == NULL) || (*list == '\0'))
1360 return 1;
1361
1362 strncpy (tmplist, list, STR_LENGTH);
1363 tmplist[STR_LENGTH-1] = '\0';
1364
1365 c0 = c1 = tmplist;
1366 while (*c1) /* Remove all whitespace and break on unsupported characters */
1367 {
1368 if ((*c1 >= '0') && (*c1 <= '9'))
1369 {
1370 *(c0++) = *c1;
1371 }
1372 else if ((*c1 == '-') || (*c1 == ','))
1373 { /* Try to remove double occurrences of these characters */
1374 if (c0 == tmplist)
1375 {
1376 *(c0++) = *c1;
1377 }
1378 else
1379 {
1380 if (*(c0-1) != *c1)
1381 *(c0++) = *c1;
1382 }
1383 }
1384 else
1385 break;
1386
1387 c1++;
1388 }
1389
1390 if (c0 == tmplist)
1391 return 1;
1392
1393 *c0 = '\0';
1394
1395 /* Now we have a comma separated list like 1-4-1,-3,1- */
1396
1397 start_num = end_num = -1;
1398 state = READ_STARTNUM;
1399 for (c0 = tmplist; *c0 != '\0'; c0++)
1400 {
1401 switch (state)
1402 {
1403 case READ_STARTNUM:
1404 if (*c0 == ',')
1405 {
1406 if ((start_num > 0) && (start_num == (int)page_num))
1407 return -1;
1408 start_num = -1;
1409 }
1410 else if (*c0 == '-')
1411 {
1412 if (start_num < 0) start_num = 1;
1413 state = READ_ENDNUM;
1414 }
1415 else /* '0' - '9' */
1416 {
1417 if (start_num < 0) start_num = 0;
1418 start_num *= 10;
1419 start_num += *c0 - '0';
1420 }
1421 break;
1422
1423 case READ_ENDNUM:
1424 if (*c0 == ',')
1425 {
1426 if (end_num < 0) end_num = 9999;
1427 CHK_LIST (start_num, end_num, (int)page_num);
1428 start_num = end_num = -1;
1429 state = READ_STARTNUM;
1430 }
1431 else if (*c0 == '-')
1432 {
1433 CHK_LIST (start_num, end_num, (int)page_num);
1434 start_num = end_num;
1435 end_num = -1;
1436 }
1437 else /* '0' - '9' */
1438 {
1439 if (end_num < 0) end_num = 0;
1440 end_num *= 10;
1441 end_num += *c0 - '0';
1442 }
1443 break;
1444 }
1445 }
1446 if (state == READ_STARTNUM)
1447 {
1448 if (start_num > 0)
1449 return (start_num == (int) page_num);
1450 }
1451 else
1452 {
1453 if (end_num < 0) end_num = 9999;
1454 CHK_LIST (start_num, end_num, (int)page_num);
1455 }
1456
1457 return 0;
1458 #undef CHK_LIST
1459 }
1460
1461
1462 /* A function like fgets, but treats single CR-character as line break. */
1463 /* As a line break the newline-character is returned. */
psfgets(char * s,int size,FILE * stream)1464 static char *psfgets (char *s, int size, FILE *stream)
1465
1466 {
1467 int c;
1468 char *sptr = s;
1469
1470 if (size <= 0)
1471 return NULL;
1472
1473 if (size == 1)
1474 {
1475 *s = '\0';
1476 return NULL;
1477 }
1478
1479 c = getc (stream);
1480 if (c == EOF)
1481 return NULL;
1482
1483 for (;;)
1484 {
1485 /* At this point we have space in sptr for at least two characters */
1486 if (c == '\n') /* Got end of line (UNIX line end) ? */
1487 {
1488 *(sptr++) = '\n';
1489 break;
1490 }
1491 else if (c == '\r') /* Got a carriage return. Check next character */
1492 {
1493 c = getc (stream);
1494 if ((c == EOF) || (c == '\n')) /* EOF or DOS line end ? */
1495 {
1496 *(sptr++) = '\n'; /* Return UNIX line end */
1497 break;
1498 }
1499 else /* Single carriage return. Return UNIX line end. */
1500 {
1501 ungetc (c, stream); /* Save the extra character */
1502 *(sptr++) = '\n';
1503 break;
1504 }
1505 }
1506 else /* no line end character */
1507 {
1508 *(sptr++) = (char)c;
1509 size--;
1510 }
1511 if (size == 1)
1512 break; /* Only space for the nul-character ? */
1513
1514 c = getc (stream);
1515 if (c == EOF)
1516 break;
1517 }
1518
1519 *sptr = '\0';
1520
1521 return s;
1522 }
1523
1524
1525 /* Get the BoundingBox of a PostScript file. On success, 0 is returned. */
1526 /* On failure, -1 is returned. */
1527 static gint
get_bbox(const gchar * filename,gint * x0,gint * y0,gint * x1,gint * y1)1528 get_bbox (const gchar *filename,
1529 gint *x0,
1530 gint *y0,
1531 gint *x1,
1532 gint *y1)
1533 {
1534 char line[1024], *src;
1535 FILE *ifp;
1536 int retval = -1;
1537
1538 ifp = g_fopen (filename, "rb");
1539 if (ifp == NULL)
1540 return -1;
1541
1542 for (;;)
1543 {
1544 if (psfgets (line, sizeof (line)-1, ifp) == NULL) break;
1545 if ((line[0] != '%') || (line[1] != '%')) continue;
1546 src = &(line[2]);
1547 while ((*src == ' ') || (*src == '\t')) src++;
1548 if (strncmp (src, "BoundingBox", 11) != 0) continue;
1549 src += 11;
1550 while ((*src == ' ') || (*src == '\t') || (*src == ':')) src++;
1551 if (strncmp (src, "(atend)", 7) == 0) continue;
1552 if (sscanf (src, "%d%d%d%d", x0, y0, x1, y1) == 4)
1553 retval = 0;
1554 break;
1555 }
1556 fclose (ifp);
1557
1558 return retval;
1559 }
1560
1561 static gchar *pnmfile;
1562
1563 /* Open the PostScript file. On failure, NULL is returned. */
1564 /* The filepointer returned will give a PNM-file generated */
1565 /* by the PostScript-interpreter. */
1566 static FILE *
ps_open(const gchar * filename,const PSLoadVals * loadopt,gint * llx,gint * lly,gint * urx,gint * ury,gboolean * is_epsf)1567 ps_open (const gchar *filename,
1568 const PSLoadVals *loadopt,
1569 gint *llx,
1570 gint *lly,
1571 gint *urx,
1572 gint *ury,
1573 gboolean *is_epsf)
1574 {
1575 const gchar *driver;
1576 GPtrArray *cmdA;
1577 gchar **pcmdA;
1578 FILE *fd_popen = NULL;
1579 FILE *eps_file;
1580 gint width, height;
1581 gint resolution;
1582 gint x0, y0, x1, y1;
1583 gint offx = 0;
1584 gint offy = 0;
1585 gboolean is_pdf;
1586 gboolean maybe_epsf = FALSE;
1587 int code;
1588 void *instance = NULL;
1589
1590 resolution = loadopt->resolution;
1591 *llx = *lly = 0;
1592 width = loadopt->width;
1593 height = loadopt->height;
1594 *urx = width - 1;
1595 *ury = height - 1;
1596
1597 /* Check if the file is a PDF. For PDF, we can't set geometry */
1598 is_pdf = FALSE;
1599
1600 /* Check if it is a EPS-file */
1601 *is_epsf = FALSE;
1602
1603 eps_file = g_fopen (filename, "rb");
1604
1605 if (eps_file != NULL)
1606 {
1607 gchar hdr[512];
1608
1609 fread (hdr, 1, sizeof(hdr), eps_file);
1610 is_pdf = (strncmp (hdr, "%PDF", 4) == 0);
1611
1612 if (!is_pdf) /* Check for EPSF */
1613 {
1614 char *adobe, *epsf;
1615 int ds = 0;
1616 static unsigned char doseps[5] = { 0xc5, 0xd0, 0xd3, 0xc6, 0 };
1617
1618 hdr[sizeof(hdr)-1] = '\0';
1619 adobe = strstr (hdr, "PS-Adobe-");
1620 epsf = strstr (hdr, "EPSF-");
1621
1622 if ((adobe != NULL) && (epsf != NULL))
1623 ds = epsf - adobe;
1624
1625 *is_epsf = ((ds >= 11) && (ds <= 15));
1626
1627 /* Illustrator uses negative values in BoundingBox without marking */
1628 /* files as EPSF. Try to handle that. */
1629 maybe_epsf =
1630 (strstr (hdr, "%%Creator: Adobe Illustrator(R) 8.0") != 0);
1631
1632 /* Check DOS EPS binary file */
1633 if ((!*is_epsf) && (strncmp (hdr, (char *)doseps, 4) == 0))
1634 *is_epsf = 1;
1635 }
1636
1637 fclose (eps_file);
1638 }
1639
1640 if ((!is_pdf) && (loadopt->use_bbox)) /* Try the BoundingBox ? */
1641 {
1642 if (get_bbox (filename, &x0, &y0, &x1, &y1) == 0)
1643 {
1644 if (maybe_epsf && ((x0 < 0) || (y0 < 0)))
1645 *is_epsf = 1;
1646
1647 if (*is_epsf) /* Handle negative BoundingBox for EPSF */
1648 {
1649 offx = -x0; x1 += offx; x0 += offx;
1650 offy = -y0; y1 += offy; y0 += offy;
1651 }
1652 if ((x0 >= 0) && (y0 >= 0) && (x1 > x0) && (y1 > y0))
1653 {
1654 *llx = (int)((x0/72.0) * resolution + 0.0001);
1655 *lly = (int)((y0/72.0) * resolution + 0.0001);
1656 /* Use upper bbox values as image size */
1657 width = (int)((x1/72.0) * resolution + 0.5);
1658 height = (int)((y1/72.0) * resolution + 0.5);
1659 /* Pixel coordinates must be one less */
1660 *urx = width - 1;
1661 *ury = height - 1;
1662 if (*urx < *llx) *urx = *llx;
1663 if (*ury < *lly) *ury = *lly;
1664 }
1665 }
1666 }
1667
1668 switch (loadopt->pnm_type)
1669 {
1670 case 4:
1671 driver = "pbmraw";
1672 break;
1673 case 5:
1674 driver = "pgmraw";
1675 break;
1676 case 7:
1677 driver = "pnmraw";
1678 break;
1679 default:
1680 driver = "ppmraw";
1681 break;
1682 }
1683
1684 /* For instance, the Win32 port of ghostscript doesn't work correctly when
1685 * using standard output as output file.
1686 * Thus, use a real output file.
1687 */
1688 pnmfile = gimp_temp_name ("pnm");
1689
1690 /* Build command array */
1691 cmdA = g_ptr_array_new ();
1692
1693 g_ptr_array_add (cmdA, g_strdup (g_get_prgname ()));
1694 g_ptr_array_add (cmdA, g_strdup_printf ("-sDEVICE=%s", driver));
1695 g_ptr_array_add (cmdA, g_strdup_printf ("-r%d", resolution));
1696
1697 if (is_pdf)
1698 {
1699 /* Acrobat Reader honors CropBox over MediaBox, so let's match that
1700 * behavior.
1701 */
1702 g_ptr_array_add (cmdA, g_strdup ("-dUseCropBox"));
1703 }
1704 else
1705 {
1706 /* For PDF, we can't set geometry */
1707 g_ptr_array_add (cmdA, g_strdup_printf ("-g%dx%d", width, height));
1708 }
1709
1710 /* Antialiasing not available for PBM-device */
1711 if ((loadopt->pnm_type != 4) && (loadopt->textalpha != 1))
1712 g_ptr_array_add (cmdA, g_strdup_printf ("-dTextAlphaBits=%d",
1713 loadopt->textalpha));
1714 if ((loadopt->pnm_type != 4) && (loadopt->graphicsalpha != 1))
1715 g_ptr_array_add (cmdA, g_strdup_printf ("-dGraphicsAlphaBits=%d",
1716 loadopt->graphicsalpha));
1717 g_ptr_array_add (cmdA, g_strdup ("-q"));
1718 g_ptr_array_add (cmdA, g_strdup ("-dBATCH"));
1719 g_ptr_array_add (cmdA, g_strdup ("-dNOPAUSE"));
1720
1721 /* If no additional options specified, use at least -dSAFER */
1722 if (g_getenv ("GS_OPTIONS") == NULL)
1723 g_ptr_array_add (cmdA, g_strdup ("-dSAFER"));
1724
1725 /* Output file name */
1726 g_ptr_array_add (cmdA, g_strdup_printf ("-sOutputFile=%s", pnmfile));
1727
1728 /* Offset command for gs to get image part with negative x/y-coord. */
1729 if ((offx != 0) || (offy != 0))
1730 {
1731 g_ptr_array_add (cmdA, g_strdup ("-c"));
1732 g_ptr_array_add (cmdA, g_strdup_printf ("%d", offx));
1733 g_ptr_array_add (cmdA, g_strdup_printf ("%d", offy));
1734 g_ptr_array_add (cmdA, g_strdup ("translate"));
1735 }
1736
1737 /* input file name */
1738 g_ptr_array_add (cmdA, g_strdup ("-f"));
1739 g_ptr_array_add (cmdA, g_strdup (filename));
1740
1741 if (*is_epsf)
1742 {
1743 g_ptr_array_add (cmdA, g_strdup ("-c"));
1744 g_ptr_array_add (cmdA, g_strdup ("showpage"));
1745 }
1746
1747 g_ptr_array_add (cmdA, g_strdup ("-c"));
1748 g_ptr_array_add (cmdA, g_strdup ("quit"));
1749 g_ptr_array_add (cmdA, NULL);
1750
1751 pcmdA = (gchar **) cmdA->pdata;
1752
1753 #ifdef PS_DEBUG
1754 {
1755 gchar **p = pcmdA;
1756 g_print ("Passing args (argc=%d):\n", cmdA->len - 1);
1757
1758 while (*p)
1759 {
1760 g_print ("%s\n", *p);
1761 p++;
1762 }
1763 }
1764 #endif
1765
1766 code = gsapi_new_instance (&instance, NULL);
1767 if (code == 0) {
1768 code = gsapi_set_arg_encoding(instance, GS_ARG_ENCODING_UTF8);
1769 code = gsapi_init_with_args (instance, cmdA->len - 1, pcmdA);
1770 code = gsapi_exit (instance);
1771 gsapi_delete_instance (instance);
1772 }
1773
1774 /* Don't care about exit status of ghostscript. */
1775 /* Just try to read what it wrote. */
1776
1777 fd_popen = g_fopen (pnmfile, "rb");
1778
1779 g_ptr_array_free (cmdA, FALSE);
1780 g_strfreev (pcmdA);
1781
1782 return fd_popen;
1783 }
1784
1785
1786 /* Close the PNM-File of the PostScript interpreter */
1787 static void
ps_close(FILE * ifp)1788 ps_close (FILE *ifp)
1789 {
1790 /* If a real outputfile was used, close the file and remove it. */
1791 fclose (ifp);
1792 g_unlink (pnmfile);
1793 }
1794
1795
1796 /* Read the header of a raw PNM-file and return type (4-6) or -1 on failure */
1797 static gint
read_pnmraw_type(FILE * ifp,gint * width,gint * height,gint * maxval)1798 read_pnmraw_type (FILE *ifp,
1799 gint *width,
1800 gint *height,
1801 gint *maxval)
1802 {
1803 int frst, scnd, thrd;
1804 gint pnmtype;
1805 gchar line[1024];
1806
1807 /* GhostScript may write some informational messages infront of the header. */
1808 /* We are just looking at a Px\n in the input stream. */
1809 frst = getc (ifp);
1810 scnd = getc (ifp);
1811 thrd = getc (ifp);
1812 for (;;)
1813 {
1814 if (thrd == EOF) return -1;
1815 #ifdef G_OS_WIN32
1816 if (thrd == '\r') thrd = getc (ifp);
1817 #endif
1818 if ((thrd == '\n') && (frst == 'P') && (scnd >= '1') && (scnd <= '6'))
1819 break;
1820 frst = scnd;
1821 scnd = thrd;
1822 thrd = getc (ifp);
1823 }
1824 pnmtype = scnd - '0';
1825 /* We don't use the ASCII-versions */
1826 if ((pnmtype >= 1) && (pnmtype <= 3))
1827 return -1;
1828
1829 /* Read width/height */
1830 for (;;)
1831 {
1832 if (fgets (line, sizeof (line)-1, ifp) == NULL)
1833 return -1;
1834 if (line[0] != '#')
1835 break;
1836 }
1837 if (sscanf (line, "%d%d", width, height) != 2)
1838 return -1;
1839
1840 *maxval = 255;
1841
1842 if (pnmtype != 4) /* Read maxval */
1843 {
1844 for (;;)
1845 {
1846 if (fgets (line, sizeof (line)-1, ifp) == NULL)
1847 return -1;
1848 if (line[0] != '#')
1849 break;
1850 }
1851 if (sscanf (line, "%d", maxval) != 1)
1852 return -1;
1853 }
1854
1855 return pnmtype;
1856 }
1857
1858
1859 /* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
1860 static gint32
create_new_image(const gchar * filename,guint pagenum,guint width,guint height,GimpImageBaseType type,gint32 * layer_ID)1861 create_new_image (const gchar *filename,
1862 guint pagenum,
1863 guint width,
1864 guint height,
1865 GimpImageBaseType type,
1866 gint32 *layer_ID)
1867 {
1868 gint32 image_ID;
1869 GimpImageType gdtype;
1870 gchar *tmp;
1871
1872 switch (type)
1873 {
1874 case GIMP_GRAY:
1875 gdtype = GIMP_GRAY_IMAGE;
1876 break;
1877 case GIMP_INDEXED:
1878 gdtype = GIMP_INDEXED_IMAGE;
1879 break;
1880 case GIMP_RGB:
1881 default:
1882 gdtype = GIMP_RGB_IMAGE;
1883 }
1884
1885 image_ID = gimp_image_new_with_precision (width, height, type,
1886 GIMP_PRECISION_U8_GAMMA);
1887 gimp_image_undo_disable (image_ID);
1888
1889 tmp = g_strdup_printf ("%s-%d", filename, pagenum);
1890 gimp_image_set_filename (image_ID, tmp);
1891 g_free (tmp);
1892
1893 tmp = g_strdup_printf (_("Page %d"), pagenum);
1894 *layer_ID = gimp_layer_new (image_ID, tmp, width, height,
1895 gdtype,
1896 100,
1897 gimp_image_get_default_new_layer_mode (image_ID));
1898 g_free (tmp);
1899
1900 gimp_image_insert_layer (image_ID, *layer_ID, -1, 0);
1901
1902 return image_ID;
1903 }
1904
1905
1906 /* Skip PNM image generated from PostScript file. */
1907 /* Return TRUE on success, FALSE otherwise. */
1908 static gboolean
skip_ps(FILE * ifp)1909 skip_ps (FILE *ifp)
1910 {
1911 guchar buf[8192];
1912 gsize len;
1913 gint pnmtype, width, height, maxval, bpl;
1914
1915 pnmtype = read_pnmraw_type (ifp, &width, &height, &maxval);
1916
1917 if (pnmtype == 4) /* Portable bitmap */
1918 bpl = (width + 7) / 8;
1919 else if (pnmtype == 5)
1920 bpl = width;
1921 else if (pnmtype == 6)
1922 bpl = width * 3;
1923 else
1924 return FALSE;
1925
1926 len = bpl * height;
1927 while (len)
1928 {
1929 gsize bytes = fread (buf, 1, MIN (len, sizeof (buf)), ifp);
1930
1931 if (bytes < MIN (len, sizeof (buf)))
1932 return FALSE;
1933
1934 len -= bytes;
1935 }
1936
1937 return TRUE;
1938 }
1939
1940
1941 /* Load PNM image generated from PostScript file */
1942 static gint32
load_ps(const gchar * filename,guint pagenum,FILE * ifp,gint llx,gint lly,gint urx,gint ury)1943 load_ps (const gchar *filename,
1944 guint pagenum,
1945 FILE *ifp,
1946 gint llx,
1947 gint lly,
1948 gint urx,
1949 gint ury)
1950 {
1951 guchar *dest;
1952 guchar *data, *bitline = NULL, *byteline = NULL, *byteptr, *temp;
1953 guchar bit2byte[256*8];
1954 int width, height, tile_height, scan_lines, total_scan_lines;
1955 int image_width, image_height;
1956 int skip_left, skip_bottom;
1957 int i, j, pnmtype, maxval, bpp, nread;
1958 GimpImageBaseType imagetype;
1959 gint32 layer_ID, image_ID;
1960 GeglBuffer *buffer = NULL;
1961 int err = 0, e;
1962
1963 pnmtype = read_pnmraw_type (ifp, &width, &height, &maxval);
1964
1965 if ((width == urx+1) && (height == ury+1)) /* gs respected BoundingBox ? */
1966 {
1967 skip_left = llx; skip_bottom = lly;
1968 image_width = width - skip_left;
1969 image_height = height - skip_bottom;
1970 }
1971 else
1972 {
1973 skip_left = skip_bottom = 0;
1974 image_width = width;
1975 image_height = height;
1976 }
1977 if (pnmtype == 4) /* Portable Bitmap */
1978 {
1979 imagetype = GIMP_INDEXED;
1980 nread = (width+7)/8;
1981 bpp = 1;
1982 bitline = g_new (guchar, nread);
1983 byteline = g_new (guchar, nread * 8);
1984
1985 /* Get an array for mapping 8 bits in a byte to 8 bytes */
1986 temp = bit2byte;
1987 for (j = 0; j < 256; j++)
1988 for (i = 7; i >= 0; i--)
1989 *(temp++) = ((j & (1 << i)) != 0);
1990 }
1991 else if (pnmtype == 5) /* Portable Greymap */
1992 {
1993 imagetype = GIMP_GRAY;
1994 nread = width;
1995 bpp = 1;
1996 byteline = g_new (guchar, nread);
1997 }
1998 else if (pnmtype == 6) /* Portable Pixmap */
1999 {
2000 imagetype = GIMP_RGB;
2001 nread = width * 3;
2002 bpp = 3;
2003 byteline = g_new (guchar, nread);
2004 }
2005 else
2006 return -1;
2007
2008 image_ID = create_new_image (filename, pagenum,
2009 image_width, image_height, imagetype,
2010 &layer_ID);
2011 buffer = gimp_drawable_get_buffer (layer_ID);
2012
2013 tile_height = gimp_tile_height ();
2014 data = g_malloc (tile_height * image_width * bpp);
2015
2016 dest = data;
2017 total_scan_lines = scan_lines = 0;
2018
2019 if (pnmtype == 4) /* Read bitimage ? Must be mapped to indexed */
2020 {
2021 const guchar BWColorMap[2*3] = { 255, 255, 255, 0, 0, 0 };
2022
2023 gimp_image_set_colormap (image_ID, BWColorMap, 2);
2024
2025 for (i = 0; i < height; i++)
2026 {
2027 e = (fread (bitline, 1, nread, ifp) != nread);
2028 if (total_scan_lines >= image_height)
2029 continue;
2030 err |= e;
2031 if (err)
2032 break;
2033
2034 j = width; /* Map 1 byte of bitimage to 8 bytes of indexed image */
2035 temp = bitline;
2036 byteptr = byteline;
2037 while (j >= 8)
2038 {
2039 memcpy (byteptr, bit2byte + *(temp++)*8, 8);
2040 byteptr += 8;
2041 j -= 8;
2042 }
2043 if (j > 0)
2044 memcpy (byteptr, bit2byte + *temp*8, j);
2045
2046 memcpy (dest, byteline+skip_left, image_width);
2047 dest += image_width;
2048 scan_lines++;
2049 total_scan_lines++;
2050
2051 if ((scan_lines == tile_height) || ((i + 1) == image_height))
2052 {
2053 gegl_buffer_set (buffer,
2054 GEGL_RECTANGLE (0, i-scan_lines+1,
2055 image_width, scan_lines),
2056 0,
2057 NULL,
2058 data,
2059 GEGL_AUTO_ROWSTRIDE);
2060 scan_lines = 0;
2061 dest = data;
2062 }
2063
2064 if ((i % 20) == 0)
2065 gimp_progress_update ((gdouble) (i + 1) / (gdouble) image_height);
2066
2067 if (err)
2068 break;
2069 }
2070 }
2071 else /* Read gray/rgb-image */
2072 {
2073 for (i = 0; i < height; i++)
2074 {
2075 e = (fread (byteline, bpp, width, ifp) != width);
2076 if (total_scan_lines >= image_height)
2077 continue;
2078 err |= e;
2079 if (err)
2080 break;
2081
2082 memcpy (dest, byteline+skip_left*bpp, image_width*bpp);
2083 dest += image_width*bpp;
2084 scan_lines++;
2085 total_scan_lines++;
2086
2087 if ((scan_lines == tile_height) || ((i + 1) == image_height))
2088 {
2089 gegl_buffer_set (buffer,
2090 GEGL_RECTANGLE (0, i-scan_lines+1,
2091 image_width, scan_lines),
2092 0,
2093 NULL,
2094 data,
2095 GEGL_AUTO_ROWSTRIDE);
2096 scan_lines = 0;
2097 dest = data;
2098 }
2099
2100 if ((i % 20) == 0)
2101 gimp_progress_update ((gdouble) (i + 1) / (gdouble) image_height);
2102
2103 if (err)
2104 break;
2105 }
2106 }
2107 gimp_progress_update (1.0);
2108
2109 g_free (data);
2110 g_free (byteline);
2111 g_free (bitline);
2112
2113 if (err)
2114 g_message ("EOF encountered on reading");
2115
2116 g_object_unref (buffer);
2117
2118 return (err ? -1 : image_ID);
2119 }
2120
2121
2122 /* Write out the PostScript file header */
2123 static gboolean
save_ps_header(GOutputStream * output,GFile * file,GError ** error)2124 save_ps_header (GOutputStream *output,
2125 GFile *file,
2126 GError **error)
2127 {
2128 gchar *basename = g_path_get_basename (gimp_file_get_utf8_name (file));
2129 time_t cutime = time (NULL);
2130
2131 if (! print (output, error,
2132 "%%!PS-Adobe-3.0%s\n", psvals.eps ? " EPSF-3.0" : ""))
2133 goto fail;
2134
2135 if (! print (output, error,
2136 "%%%%Creator: GIMP PostScript file plug-in V %4.2f "
2137 "by Peter Kirchgessner\n", VERSIO))
2138 goto fail;
2139
2140 if (! print (output, error,
2141 "%%%%Title: %s\n"
2142 "%%%%CreationDate: %s"
2143 "%%%%DocumentData: Clean7Bit\n",
2144 basename, ctime (&cutime)))
2145 goto fail;
2146
2147 if (psvals.eps || (psvals.level > 1))
2148 if (! print (output, error,"%%%%LanguageLevel: 2\n"))
2149 goto fail;
2150
2151 if (! print (output, error, "%%%%Pages: 1\n"))
2152 goto fail;
2153
2154 g_free (basename);
2155
2156 return TRUE;
2157
2158 fail:
2159
2160 g_free (basename);
2161
2162 return FALSE;
2163 }
2164
2165
2166 /* Write out transformation for image */
2167 static gboolean
save_ps_setup(GOutputStream * output,gint32 drawable_ID,gint width,gint height,gint bpp,GError ** error)2168 save_ps_setup (GOutputStream *output,
2169 gint32 drawable_ID,
2170 gint width,
2171 gint height,
2172 gint bpp,
2173 GError **error)
2174 {
2175 gdouble x_offset, y_offset, x_size, y_size;
2176 gdouble urx, ury;
2177 gdouble width_inch, height_inch;
2178 gdouble f1, f2, dx, dy;
2179 gint xtrans, ytrans;
2180 gint i_urx, i_ury;
2181 gchar tmpbuf1[G_ASCII_DTOSTR_BUF_SIZE];
2182 gchar tmpbuf2[G_ASCII_DTOSTR_BUF_SIZE];
2183
2184 /* initialize */
2185
2186 dx = 0.0;
2187 dy = 0.0;
2188
2189 x_offset = psvals.x_offset;
2190 y_offset = psvals.y_offset;
2191 width_inch = fabs (psvals.width);
2192 height_inch = fabs (psvals.height);
2193
2194 if (psvals.unit_mm)
2195 {
2196 x_offset /= 25.4; y_offset /= 25.4;
2197 width_inch /= 25.4; height_inch /= 25.4;
2198 }
2199
2200 if (psvals.keep_ratio) /* Proportions to keep ? */
2201 { /* Fit the image into the allowed size */
2202 f1 = width_inch / width;
2203 f2 = height_inch / height;
2204 if (f1 < f2)
2205 height_inch = width_inch * (gdouble) height / (gdouble) width;
2206 else
2207 width_inch = fabs (height_inch) * (gdouble) width / (gdouble) height;
2208 }
2209
2210 if ((psvals.rotate == 0) || (psvals.rotate == 180))
2211 {
2212 x_size = width_inch;
2213 y_size = height_inch;
2214 }
2215 else
2216 {
2217 y_size = width_inch;
2218 x_size = height_inch;
2219 }
2220
2221 /* Round up upper right corner only for non-integer values */
2222 urx = (x_offset + x_size) * 72.0;
2223 ury = (y_offset + y_size) * 72.0;
2224 i_urx = (gint) urx;
2225 i_ury = (gint) ury;
2226 if (urx != (gdouble) i_urx) i_urx++; /* Check for non-integer value */
2227 if (ury != (gdouble) i_ury) i_ury++;
2228
2229 if (! print (output, error,
2230 "%%%%BoundingBox: %d %d %d %d\n"
2231 "%%%%EndComments\n",
2232 (gint) (x_offset * 72.0), (gint) (y_offset * 72.0), i_urx, i_ury))
2233 return FALSE;
2234
2235 if (psvals.preview && (psvals.preview_size > 0))
2236 {
2237 if (! save_ps_preview (output, drawable_ID, error))
2238 return FALSE;
2239 }
2240
2241 if (! print (output, error,
2242 "%%%%BeginProlog\n"
2243 "%% Use own dictionary to avoid conflicts\n"
2244 "10 dict begin\n"
2245 "%%%%EndProlog\n"
2246 "%%%%Page: 1 1\n"
2247 "%% Translate for offset\n"
2248 "%s %s translate\n",
2249 g_ascii_dtostr (tmpbuf1, sizeof (tmpbuf1), x_offset * 72.0),
2250 g_ascii_dtostr (tmpbuf2, sizeof (tmpbuf2), y_offset * 72.0)))
2251 return FALSE;
2252
2253 /* Calculate translation to startpoint of first scanline */
2254 switch (psvals.rotate)
2255 {
2256 case 0: dx = 0.0; dy = y_size * 72.0;
2257 break;
2258 case 90: dx = dy = 0.0;
2259 break;
2260 case 180: dx = x_size * 72.0; dy = 0.0;
2261 break;
2262 case 270: dx = x_size * 72.0; dy = y_size * 72.0;
2263 break;
2264 }
2265
2266 if ((dx != 0.0) || (dy != 0.0))
2267 {
2268 if (! print (output, error,
2269 "%% Translate to begin of first scanline\n"
2270 "%s %s translate\n",
2271 g_ascii_dtostr (tmpbuf1, sizeof (tmpbuf1), dx),
2272 g_ascii_dtostr (tmpbuf2, sizeof (tmpbuf2), dy)))
2273 return FALSE;
2274 }
2275
2276 if (psvals.rotate)
2277 if (! print (output, error, "%d rotate\n", (gint) psvals.rotate))
2278 return FALSE;
2279
2280 if (! print (output, error,
2281 "%s %s scale\n",
2282 g_ascii_dtostr (tmpbuf1, sizeof (tmpbuf1), 72.0 * width_inch),
2283 g_ascii_dtostr (tmpbuf2, sizeof (tmpbuf2), -72.0 * height_inch)))
2284 return FALSE;
2285
2286 /* Write the PostScript procedures to read the image */
2287 if (psvals.level <= 1)
2288 {
2289 if (! print (output, error,
2290 "%% Variable to keep one line of raster data\n"))
2291 return FALSE;
2292
2293 if (bpp == 1)
2294 {
2295 if (! print (output, error,
2296 "/scanline %d string def\n", (width + 7) / 8))
2297 return FALSE;
2298 }
2299 else
2300 {
2301 if (! print (output, error,
2302 "/scanline %d %d mul string def\n", width, bpp / 8))
2303 return FALSE;
2304 }
2305 }
2306
2307 if (! print (output, error,
2308 "%% Image geometry\n%d %d %d\n"
2309 "%% Transformation matrix\n",
2310 width, height, (bpp == 1) ? 1 : 8))
2311 return FALSE;
2312
2313 xtrans = ytrans = 0;
2314 if (psvals.width < 0.0) { width = -width; xtrans = -width; }
2315 if (psvals.height < 0.0) { height = -height; ytrans = -height; }
2316
2317 if (! print (output, error,
2318 "[ %d 0 0 %d %d %d ]\n", width, height, xtrans, ytrans))
2319 return FALSE;
2320
2321 return TRUE;
2322 }
2323
2324 static gboolean
save_ps_trailer(GOutputStream * output,GError ** error)2325 save_ps_trailer (GOutputStream *output,
2326 GError **error)
2327 {
2328 return print (output, error,
2329 "%%%%Trailer\n"
2330 "end\n%%%%EOF\n");
2331 }
2332
2333 /* Do a Floyd-Steinberg dithering on a grayscale scanline. */
2334 /* linecount must keep the counter for the actual scanline (0, 1, 2, ...). */
2335 /* If linecount is less than zero, all used memory is freed. */
2336
2337 static void
dither_grey(const guchar * grey,guchar * bw,gint npix,gint linecount)2338 dither_grey (const guchar *grey,
2339 guchar *bw,
2340 gint npix,
2341 gint linecount)
2342 {
2343 static gboolean do_init_arrays = TRUE;
2344 static gint *fs_error = NULL;
2345 static gint limit[1278];
2346 static gint east_error[256];
2347 static gint seast_error[256];
2348 static gint south_error[256];
2349 static gint swest_error[256];
2350
2351 const guchar *greyptr;
2352 guchar *bwptr, mask;
2353 gint *fse;
2354 gint x, greyval, fse_inline;
2355
2356 if (linecount <= 0)
2357 {
2358 g_free (fs_error);
2359
2360 if (linecount < 0)
2361 return;
2362
2363 fs_error = g_new0 (gint, npix + 2);
2364
2365 /* Initialize some arrays that speed up dithering */
2366 if (do_init_arrays)
2367 {
2368 gint i;
2369
2370 do_init_arrays = FALSE;
2371
2372 for (i = 0, x = -511; x <= 766; i++, x++)
2373 limit[i] = (x < 0) ? 0 : ((x > 255) ? 255 : x);
2374
2375 for (greyval = 0; greyval < 256; greyval++)
2376 {
2377 east_error[greyval] = (greyval < 128) ?
2378 ((greyval * 79) >> 8) : (((greyval - 255) * 79) >> 8);
2379 seast_error[greyval] = (greyval < 128) ?
2380 ((greyval * 34) >> 8) : (((greyval - 255) * 34) >> 8);
2381 south_error[greyval] = (greyval < 128) ?
2382 ((greyval * 56) >> 8) : (((greyval - 255) * 56) >> 8);
2383 swest_error[greyval] = (greyval < 128) ?
2384 ((greyval * 12) >> 8) : (((greyval - 255) * 12) >> 8);
2385 }
2386 }
2387 }
2388
2389 g_return_if_fail (fs_error != NULL);
2390
2391 memset (bw, 0, (npix + 7) / 8); /* Initialize with white */
2392
2393 greyptr = grey;
2394 bwptr = bw;
2395 mask = 0x80;
2396
2397 fse_inline = fs_error[1];
2398
2399 for (x = 0, fse = fs_error + 1; x < npix; x++, fse++)
2400 {
2401 greyval =
2402 limit[*(greyptr++) + fse_inline + 512]; /* 0 <= greyval <= 255 */
2403
2404 if (greyval < 128)
2405 *bwptr |= mask; /* Set a black pixel */
2406
2407 /* Error distribution */
2408 fse_inline = east_error[greyval] + fse[1];
2409 fse[1] = seast_error[greyval];
2410 fse[0] += south_error[greyval];
2411 fse[-1] += swest_error[greyval];
2412
2413 mask >>= 1; /* Get mask for next b/w-pixel */
2414
2415 if (!mask)
2416 {
2417 mask = 0x80;
2418 bwptr++;
2419 }
2420 }
2421 }
2422
2423 /* Write a device independent screen preview */
2424 static gboolean
save_ps_preview(GOutputStream * output,gint32 drawable_ID,GError ** error)2425 save_ps_preview (GOutputStream *output,
2426 gint32 drawable_ID,
2427 GError **error)
2428 {
2429 GimpImageType drawable_type;
2430 GeglBuffer *buffer = NULL;
2431 const Babl *format;
2432 gint bpp;
2433 guchar *bwptr, *greyptr;
2434 gint width, height, x, y, nbsl, out_count;
2435 gint nchar_pl = 72, src_y;
2436 gdouble f1, f2;
2437 guchar *grey, *bw, *src_row, *src_ptr;
2438 guchar *cmap;
2439 gint ncols, cind;
2440
2441 if (psvals.preview_size <= 0)
2442 return TRUE;
2443
2444 buffer = gimp_drawable_get_buffer (drawable_ID);
2445 cmap = NULL;
2446
2447 drawable_type = gimp_drawable_type (drawable_ID);
2448 switch (drawable_type)
2449 {
2450 case GIMP_GRAY_IMAGE:
2451 format = babl_format ("Y' u8");
2452 break;
2453
2454 case GIMP_INDEXED_IMAGE:
2455 cmap = gimp_image_get_colormap (gimp_item_get_image (drawable_ID),
2456 &ncols);
2457 format = gimp_drawable_get_format (drawable_ID);
2458 break;
2459
2460 case GIMP_RGB_IMAGE:
2461 default:
2462 format = babl_format ("R'G'B' u8");
2463 break;
2464 }
2465
2466 bpp = babl_format_get_bytes_per_pixel (format);
2467
2468 width = gegl_buffer_get_width (buffer);
2469 height = gegl_buffer_get_height (buffer);
2470
2471 /* Calculate size of preview */
2472 if ((width > psvals.preview_size) ||
2473 (height > psvals.preview_size))
2474 {
2475 f1 = (gdouble) psvals.preview_size / (gdouble) width;
2476 f2 = (gdouble) psvals.preview_size / (gdouble) height;
2477
2478 if (f1 < f2)
2479 {
2480 width = psvals.preview_size;
2481 height *= f1;
2482 if (height <= 0)
2483 height = 1;
2484 }
2485 else
2486 {
2487 height = psvals.preview_size;
2488 width *= f1;
2489 if (width <= 0)
2490 width = 1;
2491 }
2492 }
2493
2494 nbsl = (width + 7) / 8; /* Number of bytes per scanline in bitmap */
2495
2496 grey = g_new (guchar, width);
2497 bw = g_new (guchar, nbsl);
2498 src_row = g_new (guchar, gegl_buffer_get_width (buffer) * bpp);
2499
2500 if (! print (output, error,
2501 "%%%%BeginPreview: %d %d 1 %d\n",
2502 width, height,
2503 ((nbsl * 2 + nchar_pl - 1) / nchar_pl) * height))
2504 goto fail;
2505
2506 for (y = 0; y < height; y++)
2507 {
2508 /* Get a scanline from the input image and scale it to the desired
2509 width */
2510 src_y = (y * gegl_buffer_get_height (buffer)) / height;
2511 gegl_buffer_get (buffer,
2512 GEGL_RECTANGLE (0, src_y,
2513 gegl_buffer_get_width (buffer), 1),
2514 1.0, format, src_row,
2515 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
2516
2517 greyptr = grey;
2518 if (bpp == 3) /* RGB-image */
2519 {
2520 for (x = 0; x < width; x++)
2521 { /* Convert to grey */
2522 src_ptr = src_row + ((x * gegl_buffer_get_width (buffer)) / width) * 3;
2523 *(greyptr++) = (3*src_ptr[0] + 6*src_ptr[1] + src_ptr[2]) / 10;
2524 }
2525 }
2526 else if (cmap) /* Indexed image */
2527 {
2528 for (x = 0; x < width; x++)
2529 {
2530 src_ptr = src_row + ((x * gegl_buffer_get_width (buffer)) / width);
2531 cind = *src_ptr; /* Get color index and convert to grey */
2532 src_ptr = (cind >= ncols) ? cmap : (cmap + 3*cind);
2533 *(greyptr++) = (3*src_ptr[0] + 6*src_ptr[1] + src_ptr[2]) / 10;
2534 }
2535 }
2536 else /* Grey image */
2537 {
2538 for (x = 0; x < width; x++)
2539 *(greyptr++) = *(src_row + ((x * gegl_buffer_get_width (buffer)) / width));
2540 }
2541
2542 /* Now we have a grayscale line for the desired width. */
2543 /* Dither it to b/w */
2544 dither_grey (grey, bw, width, y);
2545
2546 /* Write out the b/w line */
2547 out_count = 0;
2548 bwptr = bw;
2549 for (x = 0; x < nbsl; x++)
2550 {
2551 if (out_count == 0)
2552 if (! print (output, error, "%% "))
2553 goto fail;
2554
2555 if (! print (output, error, "%02x", *(bwptr++)))
2556 goto fail;
2557
2558 out_count += 2;
2559 if (out_count >= nchar_pl)
2560 {
2561 if (! print (output, error, "\n"))
2562 goto fail;
2563
2564 out_count = 0;
2565 }
2566 }
2567
2568 if (! print (output, error, "\n"))
2569 goto fail;
2570
2571 if ((y % 20) == 0)
2572 gimp_progress_update ((gdouble) y / (gdouble) height);
2573 }
2574
2575 gimp_progress_update (1.0);
2576
2577 if (! print (output, error, "%%%%EndPreview\n"))
2578 goto fail;
2579
2580 dither_grey (grey, bw, width, -1);
2581 g_free (src_row);
2582 g_free (bw);
2583 g_free (grey);
2584
2585 g_object_unref (buffer);
2586
2587 return TRUE;
2588
2589 fail:
2590
2591 g_free (src_row);
2592 g_free (bw);
2593 g_free (grey);
2594
2595 g_object_unref (buffer);
2596
2597 return FALSE;
2598 }
2599
2600 static gboolean
save_gray(GOutputStream * output,gint32 image_ID,gint32 drawable_ID,GError ** error)2601 save_gray (GOutputStream *output,
2602 gint32 image_ID,
2603 gint32 drawable_ID,
2604 GError **error)
2605 {
2606 GeglBuffer *buffer = NULL;
2607 const Babl *format;
2608 gint bpp;
2609 gint height, width, i, j;
2610 gint tile_height;
2611 guchar *data;
2612 guchar *src;
2613 guchar *packb = NULL;
2614 gboolean level2 = (psvals.level > 1);
2615
2616 buffer = gimp_drawable_get_buffer (drawable_ID);
2617 format = babl_format ("Y' u8");
2618 bpp = babl_format_get_bytes_per_pixel (format);
2619 width = gegl_buffer_get_width (buffer);
2620 height = gegl_buffer_get_height (buffer);
2621
2622 tile_height = gimp_tile_height ();
2623
2624 /* allocate a buffer for retrieving information from the pixel region */
2625 src = data = (guchar *) g_malloc (tile_height * width * bpp);
2626
2627 /* Set up transformation in PostScript */
2628 if (! save_ps_setup (output, drawable_ID, width, height, 1 * 8, error))
2629 goto fail;
2630
2631 /* Write read image procedure */
2632 if (! level2)
2633 {
2634 if (! print (output, error,
2635 "{ currentfile scanline readhexstring pop }\n"))
2636 goto fail;
2637 }
2638 else
2639 {
2640 if (! print (output, error,
2641 "currentfile /ASCII85Decode filter /RunLengthDecode filter\n"))
2642 goto fail;
2643
2644 ascii85_init ();
2645
2646 /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
2647 packb = (guchar *) g_malloc ((width * 105) / 100 + 2);
2648 }
2649
2650 if (! ps_begin_data (output, error))
2651 goto fail;
2652
2653 if (! print (output, error, "image\n"))
2654 goto fail;
2655
2656 #define GET_GRAY_TILE(begin) \
2657 { gint scan_lines; \
2658 scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
2659 gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), \
2660 1.0, format, begin, \
2661 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
2662 src = begin; }
2663
2664 for (i = 0; i < height; i++)
2665 {
2666 if ((i % tile_height) == 0)
2667 GET_GRAY_TILE (data); /* Get more data */
2668
2669 if (! level2)
2670 {
2671 for (j = 0; j < width; j++)
2672 {
2673 if (! print (output, error, "%c", hex[(*src) >> 4]))
2674 goto fail;
2675
2676 if (! print (output, error, "%c", hex[(*(src++)) & 0x0f]))
2677 goto fail;
2678
2679 if (((j + 1) % 39) == 0)
2680 if (! print (output, error, "\n"))
2681 goto fail;
2682 }
2683
2684 if (! print (output, error, "\n"))
2685 goto fail;
2686 }
2687 else
2688 {
2689 gint nout;
2690
2691 compress_packbits (width, src, &nout, packb);
2692
2693 if (! ascii85_nout (output, nout, packb, error))
2694 goto fail;
2695
2696 src += width;
2697 }
2698
2699 if ((i % 20) == 0)
2700 gimp_progress_update ((gdouble) i / (gdouble) height);
2701 }
2702
2703 gimp_progress_update (1.0);
2704
2705 if (level2)
2706 {
2707 /* Write EOD of RunLengthDecode filter */
2708 if (! ascii85_out (output, 128, error))
2709 goto fail;
2710
2711 if (! ascii85_done (output, error))
2712 goto fail;
2713 }
2714
2715 if (! ps_end_data (output, error))
2716 return FALSE;
2717
2718 if (! print (output, error, "showpage\n"))
2719 goto fail;
2720
2721 g_free (data);
2722 g_free (packb);
2723
2724 g_object_unref (buffer);
2725
2726 return TRUE;
2727
2728 fail:
2729
2730 g_free (data);
2731 g_free (packb);
2732
2733 g_object_unref (buffer);
2734
2735 return FALSE;
2736
2737 #undef GET_GRAY_TILE
2738 }
2739
2740 static gboolean
save_bw(GOutputStream * output,gint32 image_ID,gint32 drawable_ID,GError ** error)2741 save_bw (GOutputStream *output,
2742 gint32 image_ID,
2743 gint32 drawable_ID,
2744 GError **error)
2745 {
2746 GeglBuffer *buffer = NULL;
2747 const Babl *format;
2748 gint bpp;
2749 gint height, width, i, j;
2750 gint ncols, nbsl, nwrite;
2751 gint tile_height;
2752 guchar *cmap, *ct;
2753 guchar *data, *src;
2754 guchar *packb = NULL;
2755 guchar *scanline, *dst, mask;
2756 guchar *hex_scanline;
2757 gboolean level2 = (psvals.level > 1);
2758
2759 cmap = gimp_image_get_colormap (image_ID, &ncols);
2760
2761 buffer = gimp_drawable_get_buffer (drawable_ID);
2762 format = gimp_drawable_get_format (drawable_ID);
2763 bpp = babl_format_get_bytes_per_pixel (format);
2764 width = gegl_buffer_get_width (buffer);
2765 height = gegl_buffer_get_height (buffer);
2766
2767 tile_height = gimp_tile_height ();
2768
2769 /* allocate a buffer for retrieving information from the pixel region */
2770 src = data = g_new (guchar, tile_height * width * bpp);
2771
2772 nbsl = (width + 7) / 8;
2773
2774 scanline = g_new (guchar, nbsl + 1);
2775 hex_scanline = g_new (guchar, (nbsl + 1) * 2);
2776
2777 /* Set up transformation in PostScript */
2778 if (! save_ps_setup (output, drawable_ID, width, height, 1, error))
2779 goto fail;
2780
2781 /* Write read image procedure */
2782 if (! level2)
2783 {
2784 if (! print (output, error,
2785 "{ currentfile scanline readhexstring pop }\n"))
2786 goto fail;
2787 }
2788 else
2789 {
2790 if (! print (output, error,
2791 "currentfile /ASCII85Decode filter /RunLengthDecode filter\n"))
2792 goto fail;
2793
2794 ascii85_init ();
2795
2796 /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
2797 packb = g_new (guchar, ((nbsl+1) * 105) / 100 + 2);
2798 }
2799
2800 if (! ps_begin_data (output, error))
2801 goto fail;
2802
2803 if (! print (output, error, "image\n"))
2804 goto fail;
2805
2806 #define GET_BW_TILE(begin) \
2807 { gint scan_lines; \
2808 scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
2809 gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), \
2810 1.0, format, begin, \
2811 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
2812 src = begin; }
2813
2814 for (i = 0; i < height; i++)
2815 {
2816 if ((i % tile_height) == 0)
2817 GET_BW_TILE (data); /* Get more data */
2818
2819 dst = scanline;
2820 memset (dst, 0, nbsl);
2821 mask = 0x80;
2822
2823 /* Build a bitmap for a scanline */
2824 for (j = 0; j < width; j++)
2825 {
2826 ct = cmap + *(src++)*3;
2827 if (ct[0] || ct[1] || ct[2])
2828 *dst |= mask;
2829
2830 if (mask == 0x01)
2831 {
2832 mask = 0x80; dst++;
2833 }
2834 else
2835 {
2836 mask >>= 1;
2837 }
2838 }
2839
2840 if (! level2)
2841 {
2842 /* Convert to hexstring */
2843 for (j = 0; j < nbsl; j++)
2844 {
2845 hex_scanline[j * 2] = (guchar) hex[scanline[j] >> 4];
2846 hex_scanline[j * 2 + 1] = (guchar) hex[scanline[j] & 0x0f];
2847 }
2848
2849 /* Write out hexstring */
2850 j = nbsl * 2;
2851 dst = hex_scanline;
2852
2853 while (j > 0)
2854 {
2855 nwrite = (j > 78) ? 78 : j;
2856
2857 if (! g_output_stream_write_all (output,
2858 dst, nwrite, NULL,
2859 NULL, error))
2860 goto fail;
2861
2862 if (! print (output, error, "\n"))
2863 goto fail;
2864
2865 j -= nwrite;
2866 dst += nwrite;
2867 }
2868 }
2869 else
2870 {
2871 gint nout;
2872
2873 compress_packbits (nbsl, scanline, &nout, packb);
2874
2875 if (! ascii85_nout (output, nout, packb, error))
2876 goto fail;
2877 }
2878
2879 if ((i % 20) == 0)
2880 gimp_progress_update ((gdouble) i / (gdouble) height);
2881 }
2882
2883 gimp_progress_update (1.0);
2884
2885 if (level2)
2886 {
2887 /* Write EOD of RunLengthDecode filter */
2888 if (! ascii85_out (output, 128, error))
2889 goto fail;
2890
2891 if (! ascii85_done (output, error))
2892 goto fail;
2893 }
2894
2895 if (! ps_end_data (output, error))
2896 goto fail;
2897
2898 if (! print (output, error, "showpage\n"))
2899 goto fail;
2900
2901 g_free (hex_scanline);
2902 g_free (scanline);
2903 g_free (data);
2904 g_free (packb);
2905
2906 g_object_unref (buffer);
2907
2908 return TRUE;
2909
2910 fail:
2911
2912 g_free (hex_scanline);
2913 g_free (scanline);
2914 g_free (data);
2915 g_free (packb);
2916
2917 g_object_unref (buffer);
2918
2919 return FALSE;
2920
2921 #undef GET_BW_TILE
2922 }
2923
2924 static gboolean
save_index(GOutputStream * output,gint32 image_ID,gint32 drawable_ID,GError ** error)2925 save_index (GOutputStream *output,
2926 gint32 image_ID,
2927 gint32 drawable_ID,
2928 GError **error)
2929 {
2930 GeglBuffer *buffer = NULL;
2931 const Babl *format;
2932 gint bpp;
2933 gint height, width, i, j;
2934 gint ncols, bw;
2935 gint tile_height;
2936 guchar *cmap, *cmap_start;
2937 guchar *data, *src;
2938 guchar *packb = NULL;
2939 guchar *plane = NULL;
2940 gchar coltab[256 * 6], *ct;
2941 gboolean level2 = (psvals.level > 1);
2942
2943 cmap = cmap_start = gimp_image_get_colormap (image_ID, &ncols);
2944
2945 ct = coltab;
2946 bw = 1;
2947 for (j = 0; j < 256; j++)
2948 {
2949 if (j >= ncols)
2950 {
2951 memset (ct, 0, 6);
2952 ct += 6;
2953 }
2954 else
2955 {
2956 bw &= ((cmap[0] == 0) && (cmap[1] == 0) && (cmap[2] == 0)) ||
2957 ((cmap[0] == 255) && (cmap[1] == 255) && (cmap[2] == 255));
2958
2959 *(ct++) = (guchar) hex[(*cmap) >> 4];
2960 *(ct++) = (guchar) hex[(*(cmap++)) & 0x0f];
2961 *(ct++) = (guchar) hex[(*cmap) >> 4];
2962 *(ct++) = (guchar) hex[(*(cmap++)) & 0x0f];
2963 *(ct++) = (guchar) hex[(*cmap) >> 4];
2964 *(ct++) = (guchar) hex[(*(cmap++)) & 0x0f];
2965 }
2966 }
2967
2968 if (bw)
2969 return save_bw (output, image_ID, drawable_ID, error);
2970
2971 buffer = gimp_drawable_get_buffer (drawable_ID);
2972 format = gimp_drawable_get_format (drawable_ID);
2973 bpp = babl_format_get_bytes_per_pixel (format);
2974 width = gegl_buffer_get_width (buffer);
2975 height = gegl_buffer_get_height (buffer);
2976
2977 tile_height = gimp_tile_height ();
2978
2979 /* allocate a buffer for retrieving information from the pixel region */
2980 src = data = (guchar *) g_malloc (tile_height * width * bpp);
2981
2982 /* Set up transformation in PostScript */
2983 if (! save_ps_setup (output, drawable_ID, width, height, 3 * 8, error))
2984 goto fail;
2985
2986 /* Write read image procedure */
2987 if (! level2)
2988 {
2989 if (! print (output, error,
2990 "{ currentfile scanline readhexstring pop } false 3\n"))
2991 goto fail;
2992 }
2993 else
2994 {
2995 if (! print (output, error,
2996 "%% Strings to hold RGB-samples per scanline\n"
2997 "/rstr %d string def\n"
2998 "/gstr %d string def\n"
2999 "/bstr %d string def\n",
3000 width, width, width))
3001 goto fail;
3002
3003 if (! print (output, error,
3004 "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
3005 rstr readstring pop}\n"
3006 "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
3007 gstr readstring pop}\n"
3008 "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
3009 bstr readstring pop}\n"
3010 "true 3\n"))
3011 goto fail;
3012
3013 /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
3014 packb = (guchar *) g_malloc ((width * 105) / 100 + 2);
3015 plane = (guchar *) g_malloc (width);
3016 }
3017
3018 if (! ps_begin_data (output, error))
3019 goto fail;
3020
3021 if (! print (output, error, "colorimage\n"))
3022 goto fail;
3023
3024 #define GET_INDEX_TILE(begin) \
3025 { gint scan_lines; \
3026 scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
3027 gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), \
3028 1.0, format, begin, \
3029 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
3030 src = begin; }
3031
3032 for (i = 0; i < height; i++)
3033 {
3034 if ((i % tile_height) == 0)
3035 GET_INDEX_TILE (data); /* Get more data */
3036
3037 if (! level2)
3038 {
3039 for (j = 0; j < width; j++)
3040 {
3041 if (! g_output_stream_write_all (output,
3042 coltab + (*(src++)) * 6, 6, NULL,
3043 NULL, error))
3044 goto fail;
3045
3046 if (((j + 1) % 13) == 0)
3047 if (! print (output, error, "\n"))
3048 goto fail;
3049 }
3050
3051 if (! print (output, error, "\n"))
3052 goto fail;
3053 }
3054 else
3055 {
3056 gint rgb;
3057
3058 for (rgb = 0; rgb < 3; rgb++)
3059 {
3060 guchar *src_ptr = src;
3061 guchar *plane_ptr = plane;
3062 gint nout;
3063
3064 for (j = 0; j < width; j++)
3065 *(plane_ptr++) = cmap_start[3 * *(src_ptr++) + rgb];
3066
3067 compress_packbits (width, plane, &nout, packb);
3068
3069 ascii85_init ();
3070
3071 if (! ascii85_nout (output, nout, packb, error))
3072 goto fail;
3073
3074 /* Write EOD of RunLengthDecode filter */
3075 if (! ascii85_out (output, 128, error))
3076 goto fail;
3077
3078 if (! ascii85_done (output, error))
3079 goto fail;
3080 }
3081
3082 src += width;
3083 }
3084
3085 if ((i % 20) == 0)
3086 gimp_progress_update ((gdouble) i / (gdouble) height);
3087 }
3088
3089 gimp_progress_update (1.0);
3090
3091 if (! ps_end_data (output, error))
3092 goto fail;
3093
3094 if (! print (output, error, "showpage\n"))
3095 goto fail;
3096
3097 g_free (data);
3098 g_free (packb);
3099 g_free (plane);
3100
3101 g_object_unref (buffer);
3102
3103 return TRUE;
3104
3105 fail:
3106
3107 g_free (data);
3108 g_free (packb);
3109 g_free (plane);
3110
3111 g_object_unref (buffer);
3112
3113 return FALSE;
3114
3115 #undef GET_INDEX_TILE
3116 }
3117
3118 static gboolean
save_rgb(GOutputStream * output,gint32 image_ID,gint32 drawable_ID,GError ** error)3119 save_rgb (GOutputStream *output,
3120 gint32 image_ID,
3121 gint32 drawable_ID,
3122 GError **error)
3123 {
3124 GeglBuffer *buffer = NULL;
3125 const Babl *format;
3126 gint bpp;
3127 gint height, width, tile_height;
3128 gint i, j;
3129 guchar *data, *src;
3130 guchar *packb = NULL;
3131 guchar *plane = NULL;
3132 gboolean level2 = (psvals.level > 1);
3133
3134 buffer = gimp_drawable_get_buffer (drawable_ID);
3135 format = babl_format ("R'G'B' u8");
3136 bpp = babl_format_get_bytes_per_pixel (format);
3137 width = gegl_buffer_get_width (buffer);
3138 height = gegl_buffer_get_height (buffer);
3139
3140 tile_height = gimp_tile_height ();
3141
3142 /* allocate a buffer for retrieving information from the pixel region */
3143 src = data = g_new (guchar, tile_height * width * bpp);
3144
3145 /* Set up transformation in PostScript */
3146 if (! save_ps_setup (output, drawable_ID, width, height, 3 * 8, error))
3147 goto fail;
3148
3149 /* Write read image procedure */
3150 if (! level2)
3151 {
3152 if (! print (output, error,
3153 "{ currentfile scanline readhexstring pop } false 3\n"))
3154 goto fail;
3155 }
3156 else
3157 {
3158 if (! print (output, error,
3159 "%% Strings to hold RGB-samples per scanline\n"
3160 "/rstr %d string def\n"
3161 "/gstr %d string def\n"
3162 "/bstr %d string def\n",
3163 width, width, width))
3164 goto fail;
3165
3166 if (! print (output, error,
3167 "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
3168 rstr readstring pop}\n"
3169 "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
3170 gstr readstring pop}\n"
3171 "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
3172 bstr readstring pop}\n"
3173 "true 3\n"))
3174 goto fail;
3175
3176 /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
3177 packb = g_new (guchar, (width * 105) / 100 + 2);
3178 plane = g_new (guchar, width);
3179 }
3180
3181 if (! ps_begin_data (output, error))
3182 goto fail;
3183
3184 if (! print (output, error, "colorimage\n"))
3185 goto fail;
3186
3187 #define GET_RGB_TILE(begin) \
3188 { gint scan_lines; \
3189 scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
3190 gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), \
3191 1.0, format, begin, \
3192 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
3193 src = begin; }
3194
3195 for (i = 0; i < height; i++)
3196 {
3197 if ((i % tile_height) == 0)
3198 GET_RGB_TILE (data); /* Get more data */
3199
3200 if (! level2)
3201 {
3202 for (j = 0; j < width; j++)
3203 {
3204 if (! print (output, error, "%c",
3205 hex[(*src) >> 4])) /* Red */
3206 goto fail;
3207
3208 if (! print (output, error, "%c",
3209 hex[(*(src++)) & 0x0f]))
3210 goto fail;
3211
3212 if (! print (output, error, "%c",
3213 hex[(*src) >> 4])) /* Green */
3214 goto fail;
3215
3216 if (! print (output, error, "%c",
3217 hex[(*(src++)) & 0x0f]))
3218 goto fail;
3219
3220 if (! print (output, error, "%c",
3221 hex[(*src) >> 4])) /* Blue */
3222 goto fail;
3223
3224 if (! print (output, error, "%c",
3225 hex[(*(src++)) & 0x0f]))
3226 goto fail;
3227
3228 if (((j+1) % 13) == 0)
3229 if (! print (output, error, "\n"))
3230 goto fail;
3231 }
3232
3233 if (! print (output, error, "\n"))
3234 goto fail;
3235 }
3236 else
3237 {
3238 gint rgb;
3239
3240 for (rgb = 0; rgb < 3; rgb++)
3241 {
3242 guchar *src_ptr = src + rgb;
3243 guchar *plane_ptr = plane;
3244 gint nout;
3245
3246 for (j = 0; j < width; j++)
3247 {
3248 *(plane_ptr++) = *src_ptr;
3249 src_ptr += 3;
3250 }
3251
3252 compress_packbits (width, plane, &nout, packb);
3253
3254 ascii85_init ();
3255
3256 if (! ascii85_nout (output, nout, packb, error))
3257 goto fail;
3258
3259 /* Write EOD of RunLengthDecode filter */
3260 if (! ascii85_out (output, 128, error))
3261 goto fail;
3262
3263 if (! ascii85_done (output, error))
3264 goto fail;
3265 }
3266
3267 src += 3 * width;
3268 }
3269
3270 if ((i % 20) == 0)
3271 gimp_progress_update ((gdouble) i / (gdouble) height);
3272 }
3273
3274 gimp_progress_update (1.0);
3275
3276 if (! ps_end_data (output, error))
3277 goto fail;
3278
3279 if (! print (output, error, "showpage\n"))
3280 goto fail;
3281
3282 g_free (data);
3283 g_free (packb);
3284 g_free (plane);
3285
3286 g_object_unref (buffer);
3287
3288 return TRUE;
3289
3290 fail:
3291
3292 g_free (data);
3293 g_free (packb);
3294 g_free (plane);
3295
3296 g_object_unref (buffer);
3297
3298 return FALSE;
3299
3300 #undef GET_RGB_TILE
3301 }
3302
3303 static gboolean
print(GOutputStream * output,GError ** error,const gchar * format,...)3304 print (GOutputStream *output,
3305 GError **error,
3306 const gchar *format,
3307 ...)
3308 {
3309 va_list args;
3310 gboolean success;
3311
3312 va_start (args, format);
3313 success = g_output_stream_vprintf (output, NULL, NULL,
3314 error, format, args);
3315 va_end (args);
3316
3317 return success;
3318 }
3319
3320 /* Load interface functions */
3321
3322 static gint32
count_ps_pages(const gchar * filename)3323 count_ps_pages (const gchar *filename)
3324 {
3325 FILE *psfile;
3326 gchar *extension;
3327 gchar buf[1024];
3328 gint32 num_pages = 0;
3329 gint32 showpage_count = 0;
3330
3331 extension = strrchr (filename, '.');
3332 if (extension)
3333 {
3334 extension = g_ascii_strdown (extension + 1, -1);
3335
3336 if (strcmp (extension, "eps") == 0)
3337 {
3338 g_free (extension);
3339 return 1;
3340 }
3341
3342 g_free (extension);
3343 }
3344
3345 psfile = g_fopen (filename, "r");
3346
3347 if (psfile == NULL)
3348 {
3349 g_message (_("Could not open '%s' for reading: %s"),
3350 gimp_filename_to_utf8 (filename), g_strerror (errno));
3351 return 0;
3352 }
3353
3354 while (num_pages == 0 && !feof (psfile))
3355 {
3356 fgets (buf, sizeof (buf), psfile);
3357
3358 if (strncmp (buf + 2, "Pages:", 6) == 0)
3359 sscanf (buf + strlen ("%%Pages:"), "%d", &num_pages);
3360 else if (strncmp (buf, "showpage", 8) == 0)
3361 showpage_count++;
3362 }
3363
3364 if (feof (psfile) && num_pages < 1 && showpage_count > 0)
3365 num_pages = showpage_count;
3366
3367 fclose (psfile);
3368
3369 return num_pages;
3370 }
3371
3372 static gboolean
load_dialog(const gchar * filename)3373 load_dialog (const gchar *filename)
3374 {
3375 GtkWidget *dialog;
3376 GtkWidget *main_vbox;
3377 GtkWidget *hbox;
3378 GtkWidget *frame;
3379 GtkWidget *vbox;
3380 GtkWidget *table;
3381 GtkWidget *spinbutton;
3382 GtkAdjustment *adj;
3383 GtkWidget *entry = NULL;
3384 GtkWidget *target = NULL;
3385 GtkWidget *toggle;
3386 GtkWidget *selector = NULL;
3387 gint32 page_count;
3388 gchar *range = NULL;
3389 gboolean run;
3390
3391 page_count = count_ps_pages (filename);
3392
3393 gimp_ui_init (PLUG_IN_BINARY, FALSE);
3394
3395 dialog = gimp_dialog_new (_("Import from PostScript"), PLUG_IN_ROLE,
3396 NULL, 0,
3397 gimp_standard_help_func, LOAD_PS_PROC,
3398
3399 _("_Cancel"), GTK_RESPONSE_CANCEL,
3400 _("_Import"), GTK_RESPONSE_OK,
3401
3402 NULL);
3403
3404 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
3405 GTK_RESPONSE_OK,
3406 GTK_RESPONSE_CANCEL,
3407 -1);
3408
3409 gimp_window_set_transient (GTK_WINDOW (dialog));
3410
3411 main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
3412 gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
3413 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
3414 main_vbox, TRUE, TRUE, 0);
3415 gtk_widget_show (main_vbox);
3416
3417 if (page_count > 1)
3418 {
3419 selector = gimp_page_selector_new ();
3420 gtk_box_pack_start (GTK_BOX (main_vbox), selector, TRUE, TRUE, 0);
3421 gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (selector),
3422 page_count);
3423 gimp_page_selector_set_target (GIMP_PAGE_SELECTOR (selector),
3424 ps_pagemode);
3425
3426 gtk_widget_show (selector);
3427
3428 g_signal_connect_swapped (selector, "activate",
3429 G_CALLBACK (gtk_window_activate_default),
3430 dialog);
3431 }
3432
3433 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
3434 gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
3435 gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
3436 gtk_widget_show (hbox);
3437
3438 /* Rendering */
3439 frame = gimp_frame_new (_("Rendering"));
3440 gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
3441
3442 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
3443 gtk_container_add (GTK_CONTAINER (frame), vbox);
3444
3445 /* Resolution/Width/Height/Pages labels */
3446 table = gtk_table_new (4, 2, FALSE);
3447 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
3448 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
3449 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
3450 gtk_widget_show (table);
3451
3452 adj = (GtkAdjustment *) gtk_adjustment_new (plvals.resolution,
3453 MIN_RESOLUTION, MAX_RESOLUTION,
3454 1, 10, 0);
3455 spinbutton = gimp_spin_button_new (adj, 1.0, 0);
3456 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
3457 gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
3458 _("Resolution:"), 0.0, 0.5,
3459 spinbutton, 1, FALSE);
3460
3461 g_signal_connect (adj, "value-changed",
3462 G_CALLBACK (resolution_change_callback),
3463 &plvals.resolution);
3464 g_signal_connect (adj, "value-changed",
3465 G_CALLBACK (gimp_int_adjustment_update),
3466 &plvals.resolution);
3467
3468 adj = (GtkAdjustment *) gtk_adjustment_new (plvals.width,
3469 1, GIMP_MAX_IMAGE_SIZE,
3470 1, 10, 0);
3471 ps_width_spinbutton = gimp_spin_button_new (adj, 1.0, 0);
3472 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
3473 gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
3474 _("_Width:"), 0.0, 0.5,
3475 ps_width_spinbutton, 1, FALSE);
3476
3477 g_signal_connect (adj, "value-changed",
3478 G_CALLBACK (gimp_int_adjustment_update),
3479 &plvals.width);
3480
3481 adj = (GtkAdjustment *) gtk_adjustment_new (plvals.height,
3482 1, GIMP_MAX_IMAGE_SIZE,
3483 1, 10, 0);
3484 ps_height_spinbutton = gimp_spin_button_new (adj, 1.0, 0);
3485 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
3486 gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
3487 _("_Height:"), 0.0, 0.5,
3488 ps_height_spinbutton, 1, FALSE);
3489
3490 g_signal_connect (adj, "value-changed",
3491 G_CALLBACK (gimp_int_adjustment_update),
3492 &plvals.height);
3493
3494 if (page_count == 0)
3495 {
3496 entry = gtk_entry_new ();
3497 gtk_widget_set_size_request (entry, 80, -1);
3498 gtk_entry_set_text (GTK_ENTRY (entry), plvals.pages);
3499 gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
3500 _("Pages:"), 0.0, 0.5,
3501 entry, 1, FALSE);
3502
3503 g_signal_connect (entry, "changed",
3504 G_CALLBACK (load_pages_entry_callback),
3505 NULL);
3506 gimp_help_set_help_data (GTK_WIDGET (entry),
3507 _("Pages to load (e.g.: 1-4 or 1,3,5-7)"), NULL);
3508
3509 target = gtk_combo_box_text_new ();
3510 gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (target),
3511 GIMP_PAGE_SELECTOR_TARGET_LAYERS,
3512 _("Layers"));
3513 gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (target),
3514 GIMP_PAGE_SELECTOR_TARGET_IMAGES,
3515 _("Images"));
3516 gtk_combo_box_set_active (GTK_COMBO_BOX (target), (int) ps_pagemode);
3517 gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
3518 _("Open as"), 0.0, 0.5,
3519 target, 1, FALSE);
3520 }
3521
3522 toggle = gtk_check_button_new_with_label (_("Try Bounding Box"));
3523 gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
3524 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), plvals.use_bbox);
3525 gtk_widget_show (toggle);
3526
3527 g_signal_connect (toggle, "toggled",
3528 G_CALLBACK (gimp_toggle_button_update),
3529 &plvals.use_bbox);
3530
3531 gtk_widget_show (vbox);
3532 gtk_widget_show (frame);
3533
3534 /* Coloring */
3535 frame = gimp_int_radio_group_new (TRUE, _("Coloring"),
3536 G_CALLBACK (gimp_radio_button_update),
3537 &plvals.pnm_type, plvals.pnm_type,
3538
3539 _("B/W"), 4, NULL,
3540 _("Gray"), 5, NULL,
3541 _("Color"), 6, NULL,
3542 _("Automatic"), 7, NULL,
3543
3544 NULL);
3545 gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
3546 gtk_widget_show (frame);
3547
3548 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
3549 gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
3550 gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
3551 gtk_widget_show (hbox);
3552
3553 frame = gimp_int_radio_group_new (TRUE, _("Text antialiasing"),
3554 G_CALLBACK (gimp_radio_button_update),
3555 &plvals.textalpha, plvals.textalpha,
3556
3557 C_("antialiasing", "None"), 1, NULL,
3558 _("Weak"), 2, NULL,
3559 _("Strong"), 4, NULL,
3560
3561 NULL);
3562 gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
3563 gtk_widget_show (frame);
3564
3565 frame = gimp_int_radio_group_new (TRUE, _("Graphic antialiasing"),
3566 G_CALLBACK (gimp_radio_button_update),
3567 &plvals.graphicsalpha, plvals.graphicsalpha,
3568
3569 C_("antialiasing", "None"), 1, NULL,
3570 _("Weak"), 2, NULL,
3571 _("Strong"), 4, NULL,
3572
3573 NULL);
3574 gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
3575 gtk_widget_show (frame);
3576
3577 gtk_widget_show (dialog);
3578
3579 run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
3580
3581 if (selector)
3582 {
3583 range = gimp_page_selector_get_selected_range (GIMP_PAGE_SELECTOR (selector));
3584
3585 if (strlen (range) < 1)
3586 {
3587 gimp_page_selector_select_all (GIMP_PAGE_SELECTOR (selector));
3588 range = gimp_page_selector_get_selected_range (GIMP_PAGE_SELECTOR (selector));
3589 }
3590
3591 strncpy (plvals.pages, range, sizeof (plvals.pages));
3592 plvals.pages[strlen (range)] = '\0';
3593
3594 ps_pagemode = gimp_page_selector_get_target (GIMP_PAGE_SELECTOR (selector));
3595 }
3596 else if (page_count == 0)
3597 {
3598 ps_pagemode = gtk_combo_box_get_active (GTK_COMBO_BOX (target));
3599 }
3600 else
3601 {
3602 strncpy (plvals.pages, "1", 1);
3603 plvals.pages[1] = '\0';
3604 ps_pagemode = GIMP_PAGE_SELECTOR_TARGET_IMAGES;
3605 }
3606
3607 gtk_widget_destroy (dialog);
3608
3609 return run;
3610 }
3611
3612 static void
load_pages_entry_callback(GtkWidget * widget,gpointer data)3613 load_pages_entry_callback (GtkWidget *widget,
3614 gpointer data)
3615 {
3616 gsize nelem = sizeof (plvals.pages);
3617
3618 strncpy (plvals.pages, gtk_entry_get_text (GTK_ENTRY (widget)), nelem);
3619 plvals.pages[nelem-1] = '\0';
3620 }
3621
3622
3623 /* Save interface functions */
3624
3625 static gboolean
save_dialog(void)3626 save_dialog (void)
3627 {
3628 SaveDialogVals *vals;
3629 GtkWidget *dialog;
3630 GtkWidget *toggle;
3631 GtkWidget *frame, *uframe;
3632 GtkWidget *hbox, *vbox;
3633 GtkWidget *main_vbox[2];
3634 GtkWidget *table;
3635 GtkWidget *spinbutton;
3636 GtkAdjustment *adj;
3637 gint j;
3638 gboolean run;
3639
3640 vals = g_new (SaveDialogVals, 1);
3641 vals->level = (psvals.level > 1);
3642
3643 dialog = gimp_export_dialog_new (_("PostScript"), PLUG_IN_BINARY, SAVE_PS_PROC);
3644
3645 /* Main hbox */
3646 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
3647 gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
3648 gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
3649 hbox, FALSE, FALSE, 0);
3650 main_vbox[0] = main_vbox[1] = NULL;
3651
3652 for (j = 0; j < G_N_ELEMENTS (main_vbox); j++)
3653 {
3654 main_vbox[j] = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
3655 gtk_box_pack_start (GTK_BOX (hbox), main_vbox[j], FALSE, TRUE, 0);
3656 gtk_widget_show (main_vbox[j]);
3657 }
3658
3659 /* Image Size */
3660 frame = gimp_frame_new (_("Image Size"));
3661 gtk_box_pack_start (GTK_BOX (main_vbox[0]), frame, FALSE, TRUE, 0);
3662
3663 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
3664 gtk_container_add (GTK_CONTAINER (frame), vbox);
3665
3666 /* Width/Height/X-/Y-offset labels */
3667 table = gtk_table_new (4, 2, FALSE);
3668 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
3669 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
3670 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
3671 gtk_widget_show (table);
3672
3673 vals->adjustment[0] = (GtkAdjustment *)
3674 gtk_adjustment_new (psvals.width,
3675 1e-5, GIMP_MAX_IMAGE_SIZE, 1, 10, 0);
3676 spinbutton = gimp_spin_button_new (vals->adjustment[0], 1.0, 2);
3677 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
3678 gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
3679 _("_Width:"), 0.0, 0.5,
3680 spinbutton, 1, FALSE);
3681 g_signal_connect (vals->adjustment[0], "value-changed",
3682 G_CALLBACK (gimp_double_adjustment_update),
3683 &psvals.width);
3684
3685 vals->adjustment[1] = (GtkAdjustment *)
3686 gtk_adjustment_new (psvals.height,
3687 1e-5, GIMP_MAX_IMAGE_SIZE, 1, 10, 0);
3688 spinbutton = gimp_spin_button_new (vals->adjustment[1], 1.0, 2);
3689 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
3690 gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
3691 _("_Height:"), 0.0, 0.5,
3692 spinbutton, 1, FALSE);
3693 g_signal_connect (vals->adjustment[1], "value-changed",
3694 G_CALLBACK (gimp_double_adjustment_update),
3695 &psvals.height);
3696
3697 vals->adjustment[2] = (GtkAdjustment *)
3698 gtk_adjustment_new (psvals.x_offset,
3699 0.0, GIMP_MAX_IMAGE_SIZE, 1, 10, 0);
3700 spinbutton = gimp_spin_button_new (vals->adjustment[2], 1.0, 2);
3701 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
3702 gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
3703 _("_X offset:"), 0.0, 0.5,
3704 spinbutton, 1, FALSE);
3705 g_signal_connect (vals->adjustment[2], "value-changed",
3706 G_CALLBACK (gimp_double_adjustment_update),
3707 &psvals.x_offset);
3708
3709 vals->adjustment[3] = (GtkAdjustment *)
3710 gtk_adjustment_new (psvals.y_offset,
3711 0.0, GIMP_MAX_IMAGE_SIZE, 1, 10, 0);
3712 spinbutton = gimp_spin_button_new (vals->adjustment[3], 1.0, 2);
3713 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
3714 gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
3715 _("_Y offset:"), 0.0, 0.5,
3716 spinbutton, 1, FALSE);
3717 g_signal_connect (vals->adjustment[3], "value-changed",
3718 G_CALLBACK (gimp_double_adjustment_update),
3719 &psvals.y_offset);
3720
3721 toggle = gtk_check_button_new_with_mnemonic (_("_Keep aspect ratio"));
3722 gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
3723 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), psvals.keep_ratio);
3724 gtk_widget_show (toggle);
3725
3726 gimp_help_set_help_data (toggle,
3727 _("When toggled, the resulting image will be "
3728 "scaled to fit into the given size without "
3729 "changing the aspect ratio."),
3730 "#keep_aspect_ratio"),
3731
3732 g_signal_connect (toggle, "toggled",
3733 G_CALLBACK (gimp_toggle_button_update),
3734 &psvals.keep_ratio);
3735
3736 /* Unit */
3737 uframe = gimp_int_radio_group_new (TRUE, _("Unit"),
3738 G_CALLBACK (save_unit_toggle_update),
3739 vals, psvals.unit_mm,
3740
3741 _("_Inch"), FALSE, NULL,
3742 _("_Millimeter"), TRUE, NULL,
3743
3744 NULL);
3745
3746 gtk_box_pack_start (GTK_BOX (main_vbox[0]), uframe, TRUE, TRUE, 0);
3747 gtk_widget_show (uframe);
3748
3749 gtk_widget_show (vbox);
3750 gtk_widget_show (frame);
3751
3752 /* Rotation */
3753 frame = gimp_int_radio_group_new (TRUE, _("Rotation"),
3754 G_CALLBACK (gimp_radio_button_update),
3755 &psvals.rotate, psvals.rotate,
3756
3757 "_0", 0, NULL,
3758 "_90", 90, NULL,
3759 "_180", 180, NULL,
3760 "_270", 270, NULL,
3761
3762 NULL);
3763
3764 gtk_box_pack_start (GTK_BOX (main_vbox[1]), frame, TRUE, TRUE, 0);
3765 gtk_widget_show (frame);
3766
3767 /* Format */
3768 frame = gimp_frame_new (_("Output"));
3769 gtk_box_pack_start (GTK_BOX (main_vbox[1]), frame, TRUE, TRUE, 0);
3770
3771 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
3772 gtk_container_add (GTK_CONTAINER (frame), vbox);
3773
3774 toggle = gtk_check_button_new_with_mnemonic (_("_PostScript level 2"));
3775 gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
3776 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), vals->level);
3777 gtk_widget_show (toggle);
3778
3779 g_signal_connect (toggle, "toggled",
3780 G_CALLBACK (gimp_toggle_button_update),
3781 &vals->level);
3782
3783 toggle = gtk_check_button_new_with_mnemonic (_("_Encapsulated PostScript"));
3784 gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
3785 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), psvals.eps);
3786 gtk_widget_show (toggle);
3787
3788 g_signal_connect (toggle, "toggled",
3789 G_CALLBACK (gimp_toggle_button_update),
3790 &psvals.eps);
3791
3792 toggle = gtk_check_button_new_with_mnemonic (_("P_review"));
3793 gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
3794 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), psvals.preview);
3795 gtk_widget_show (toggle);
3796
3797 g_signal_connect (toggle, "toggled",
3798 G_CALLBACK (gimp_toggle_button_update),
3799 &psvals.preview);
3800
3801 /* Preview size label/entry */
3802 table = gtk_table_new (1, 2, FALSE);
3803 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
3804 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
3805 gtk_widget_show (table);
3806
3807 g_object_bind_property (toggle, "active",
3808 table, "sensitive",
3809 G_BINDING_SYNC_CREATE);
3810
3811 adj = (GtkAdjustment *) gtk_adjustment_new (psvals.preview_size,
3812 0, 1024, 1, 10, 0);
3813 spinbutton = gimp_spin_button_new (adj, 1.0, 0);
3814 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
3815 gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
3816 _("Preview _size:"), 1.0, 0.5,
3817 spinbutton, 1, FALSE);
3818 gtk_widget_show (spinbutton);
3819
3820 g_signal_connect (adj, "value-changed",
3821 G_CALLBACK (gimp_int_adjustment_update),
3822 &psvals.preview_size);
3823
3824 gtk_widget_show (vbox);
3825 gtk_widget_show (frame);
3826
3827 gtk_widget_show (hbox);
3828 gtk_widget_show (dialog);
3829
3830 run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
3831
3832 gtk_widget_destroy (dialog);
3833
3834 psvals.level = (vals->level) ? 2 : 1;
3835
3836 g_free (vals);
3837
3838 return run;
3839 }
3840
3841 static void
save_unit_toggle_update(GtkWidget * widget,gpointer data)3842 save_unit_toggle_update (GtkWidget *widget,
3843 gpointer data)
3844 {
3845 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
3846 {
3847 SaveDialogVals *vals = (SaveDialogVals *) data;
3848 gdouble factor;
3849 gdouble value;
3850 gint unit_mm;
3851 gint i;
3852
3853 unit_mm = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
3854 "gimp-item-data"));
3855
3856 psvals.unit_mm = unit_mm;
3857
3858 if (unit_mm)
3859 factor = 25.4;
3860 else
3861 factor = 1.0 / 25.4;
3862
3863 for (i = 0; i < 4; i++)
3864 {
3865 value = gtk_adjustment_get_value (vals->adjustment[i]) * factor;
3866
3867 gtk_adjustment_set_value (vals->adjustment[i], value);
3868 }
3869 }
3870 }
3871
3872 static gboolean
resolution_change_callback(GtkAdjustment * adjustment,gpointer data)3873 resolution_change_callback (GtkAdjustment *adjustment,
3874 gpointer data)
3875 {
3876 guint *old_resolution = (guint *) data;
3877 gdouble ratio;
3878
3879 if (*old_resolution)
3880 ratio = (gdouble) gtk_adjustment_get_value (adjustment) / *old_resolution;
3881 else
3882 ratio = 1.0;
3883
3884 gtk_spin_button_set_value (GTK_SPIN_BUTTON (ps_width_spinbutton),
3885 gtk_spin_button_get_value (GTK_SPIN_BUTTON (ps_width_spinbutton)) * ratio);
3886
3887 gtk_spin_button_set_value (GTK_SPIN_BUTTON (ps_height_spinbutton),
3888 gtk_spin_button_get_value (GTK_SPIN_BUTTON (ps_height_spinbutton)) * ratio);
3889
3890 return TRUE;
3891
3892 }
3893