1 /*
2 *
3 * Print plug-in for the GIMP.
4 *
5 * Copyright 1997-2000 Michael Sweet (mike@easysw.com) and
6 * Robert Krawitz (rlk@alum.mit.edu)
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2 of the License, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <gutenprintui2/gutenprintui.h>
27 #include "print_gimp.h"
28
29 #include <sys/types.h>
30 #include <signal.h>
31 #include <ctype.h>
32 #include <sys/wait.h>
33
34 #include <unistd.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38
39 #include "print-intl.h"
40
41 /*
42 * Local functions...
43 */
44
45 static void query (void);
46 static void run (const char *name,
47 gint nparams,
48 const GimpParam *param,
49 gint *nreturn_vals,
50 GimpParam **return_vals);
51 static int do_print_dialog (const gchar *proc_name,
52 gint32 image_ID);
53
54 /*
55 * Globals...
56 */
57
58 GimpPlugInInfo PLUG_IN_INFO = /* Plug-in information */
59 {
60 NULL, /* init_proc */
61 NULL, /* quit_proc */
62 query, /* query_proc */
63 run, /* run_proc */
64 };
65
66 static stpui_plist_t gimp_vars;
67
68 /*
69 * 'main()' - Main entry - just call gimp_main()...
70 */
71
MAIN()72 MAIN()
73
74 /*
75 * 'query()' - Respond to a plug-in query...
76 */
77
78 #pragma GCC diagnostic push
79 #pragma GCC diagnostic ignored "-Wcast-qual"
80 static void
81 query (void)
82 {
83 static GimpParamDef args[] =
84 {
85 { GIMP_PDB_INT32, (BAD_CONST_CHAR) "run_mode",(BAD_CONST_CHAR) "Interactive, non-interactive" },
86 { GIMP_PDB_IMAGE, (BAD_CONST_CHAR) "image", (BAD_CONST_CHAR) "Input image" },
87 { GIMP_PDB_DRAWABLE,(BAD_CONST_CHAR) "drawable",(BAD_CONST_CHAR) "Input drawable" },
88 { GIMP_PDB_STRING, (BAD_CONST_CHAR) "driver", (BAD_CONST_CHAR) "Printer driver short name" },
89 { GIMP_PDB_STRING, (BAD_CONST_CHAR) "printer_queue",(BAD_CONST_CHAR) "CUPS Printer Queue" },
90 { GIMP_PDB_FLOAT, (BAD_CONST_CHAR) "left", (BAD_CONST_CHAR) "Left offset (points, -1 = centered)" },
91 { GIMP_PDB_FLOAT, (BAD_CONST_CHAR) "top", (BAD_CONST_CHAR) "Top offset (points, -1 = centered)" },
92 { GIMP_PDB_INT32, (BAD_CONST_CHAR) "length_key_value_array", (BAD_CONST_CHAR) "Length of the key-value array" },
93 { GIMP_PDB_STRINGARRAY, (BAD_CONST_CHAR) "keys", (BAD_CONST_CHAR) "Key-value pairs for Gutenprint Settings" },
94 };
95
96 static const gchar *blurb = "This plug-in prints images from The GIMP using Gutenprint directly.";
97 static const gchar *help = "Prints images to many printers.";
98 static const gchar *auth = "Michael Sweet <mike@easysw.com> and Robert Krawitz <rlk@alum.mit.edu>";
99 static const gchar *copy = "Copyright 1997-2006 by Michael Sweet and Robert Krawitz";
100 static const gchar *types = "RGB*,GRAY*,INDEXED*";
101
102 gimp_plugin_domain_register (cast_safe(PACKAGE), cast_safe(PACKAGE_LOCALE_DIR));
103
104 do_gimp_install_procedure(blurb, help, auth, copy, types, G_N_ELEMENTS(args),
105 args);
106 }
107 #pragma GCC diagnostic pop
108
109 static guchar *gimp_thumbnail_data = NULL;
110
111 static guchar *
stpui_get_thumbnail_data_function(void * image_ID,gint * width,gint * height,gint * bpp,gint page)112 stpui_get_thumbnail_data_function(void *image_ID, gint *width, gint *height,
113 gint *bpp, gint page)
114 {
115 if (gimp_thumbnail_data)
116 g_free(gimp_thumbnail_data);
117 gint x = gimp_image_width(image_ID);
118 gint y = gimp_image_height(image_ID);
119 if (*width > x)
120 *width = x;
121 if (*height > y)
122 *height = y;
123 gimp_thumbnail_data =
124 gimp_image_get_thumbnail_data(p2gint(image_ID), width, height, bpp);
125 return gimp_thumbnail_data;
126 }
127
128 /*
129 * 'run()' - Run the plug-in...
130 */
131
132 volatile int SDEBUG = 1;
133
134 static void
run(const char * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)135 run (const char *name, /* I - Name of print program. */
136 gint nparams, /* I - Number of parameters passed in */
137 const GimpParam *param, /* I - Parameter values */
138 gint *nreturn_vals, /* O - Number of return values */
139 GimpParam **return_vals) /* O - Return values */
140 {
141 GimpDrawable *drawable; /* Drawable for image */
142 GimpRunMode run_mode; /* Current run mode */
143 GimpParam *values; /* Return values */
144 gint32 drawable_ID; /* drawable ID */
145 GimpExportReturn export = GIMP_EXPORT_CANCEL; /* return value of gimp_export_image() */
146 gdouble xres, yres;
147 char *image_filename;
148 stpui_image_t *image;
149 gint32 image_ID;
150 gint32 base_type;
151 stp_parameter_t desc;
152 if (getenv("STP_DEBUG_STARTUP"))
153 {
154 fprintf(stderr, "pid is %d\n", getpid());
155 while (SDEBUG)
156 ;
157 }
158
159 /*
160 * Initialise libgutenprint
161 */
162
163 stp_init();
164
165 stp_set_output_codeset("UTF-8");
166
167 #ifdef INIT_I18N_UI
168 INIT_I18N_UI();
169 #else
170 /*
171 * With GCC and glib 1.2, there will be a warning here about braces in
172 * expressions. Getting rid of it causes more problems than it solves.
173 * In particular, turning on -ansi on the command line causes a number of
174 * other useful things, such as strcasecmp, popen, and snprintf to go away
175 */
176 INIT_LOCALE (PACKAGE);
177 #endif
178
179 stpui_printer_initialize(&gimp_vars);
180 /*
181 * Initialize parameter data...
182 */
183
184 run_mode = (GimpRunMode)param[0].data.d_int32;
185
186 values = g_new (GimpParam, 1);
187
188 values[0].type = GIMP_PDB_STATUS;
189 values[0].data.d_status = GIMP_PDB_SUCCESS;
190
191 *nreturn_vals = 1;
192 *return_vals = values;
193
194 image_ID = param[1].data.d_int32;
195 drawable_ID = param[2].data.d_int32;
196
197 image_filename = gimp_image_get_filename (image_ID);
198 if (image_filename)
199 {
200 if (strchr(image_filename, '/'))
201 image_filename = strrchr(image_filename, '/') + 1;
202 stpui_set_image_filename(image_filename);
203 /* g_free(image_filename); */
204 }
205 else
206 stpui_set_image_filename("Untitled");
207
208 /* eventually export the image */
209 switch (run_mode)
210 {
211 case GIMP_RUN_INTERACTIVE:
212 case GIMP_RUN_WITH_LAST_VALS:
213 gimp_ui_init ("print", TRUE);
214 export = gimp_export_image (&image_ID, &drawable_ID, "Print",
215 (GIMP_EXPORT_CAN_HANDLE_RGB |
216 GIMP_EXPORT_CAN_HANDLE_GRAY |
217 GIMP_EXPORT_CAN_HANDLE_INDEXED |
218 GIMP_EXPORT_CAN_HANDLE_ALPHA));
219 if (export == GIMP_EXPORT_CANCEL)
220 {
221 values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
222 fprintf(stderr,"Cannot handle image type\n");
223 return;
224 }
225 break;
226 default:
227 break;
228 }
229
230 /*
231 * Get drawable...
232 */
233
234 drawable = gimp_drawable_get (drawable_ID);
235 stpui_set_image_dimensions(drawable->width, drawable->height);
236 gimp_image_get_resolution (image_ID, &xres, &yres);
237 stpui_set_image_resolution(xres, yres);
238 stpui_set_image_channel_depth(8);
239 base_type = gimp_image_base_type(image_ID);
240 switch (base_type)
241 {
242 case GIMP_INDEXED:
243 case GIMP_RGB:
244 stpui_set_image_type("RGB");
245 break;
246 case GIMP_GRAY:
247 stpui_set_image_type("Whitescale");
248 break;
249 default:
250 break;
251 }
252
253 image = Image_GimpDrawable_new(drawable, image_ID);
254 stp_set_float_parameter(gimp_vars.v, "AppGamma", gimp_gamma());
255
256 /*
257 * See how we will run
258 */
259
260 switch (run_mode)
261 {
262 case GIMP_RUN_INTERACTIVE:
263 /*
264 * Get information from the dialog...
265 */
266
267 if (!do_print_dialog (name, image_ID))
268 {
269 values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
270 }
271 else
272 {
273 stpui_plist_copy(&gimp_vars, stpui_get_current_printer());
274 }
275 break;
276
277 case GIMP_RUN_NONINTERACTIVE:
278 /*
279 * Make sure all the arguments are present...
280 */
281 if (nparams < 9)
282 values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
283 else
284 {
285 gimp_image_get_resolution(image_ID, &xres, &yres);
286 gdouble pixwidth = gimp_drawable_width(drawable_ID);
287 gdouble pixheight = gimp_drawable_height(drawable_ID);
288 gdouble pointwidth = gimp_pixels_to_units(pixwidth, GIMP_UNIT_POINT, xres);
289 gdouble pointheight = gimp_pixels_to_units(pixheight, GIMP_UNIT_POINT, yres);
290
291 stp_set_height(gimp_vars.v, pointheight);
292 stp_set_width(gimp_vars.v, pointwidth);
293
294 /*
295 * Avoid calling stpui_print with an invalid driver (SEGFAULT)
296 */
297
298 if (! stp_get_printer_by_driver(param[3].data.d_string) )
299 {
300 values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
301 fprintf(stderr, "Unknown driver %s\n", stp_get_driver(gimp_vars.v));
302 break;
303 }
304
305 stp_set_driver(gimp_vars.v, param[3].data.d_string);
306
307 stpui_plist_set_queue_name(&gimp_vars, param[4].data.d_string);
308
309 /*
310 * Left offset (points, -1 = centered)
311 */
312
313 stp_set_left(gimp_vars.v, param[6].data.d_float);
314
315 /*
316 * Top offset (points, -1 = centered)
317 */
318
319 stp_set_top(gimp_vars.v, param[5].data.d_float);
320
321 /*
322 * Parse remaining parameters from key-value string array
323 */
324
325 int kv_arr_len = param[7].data.d_int32;
326
327 if (kv_arr_len % 2 != 0)
328 {
329 /*
330 * Key with no Value
331 */
332
333 values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
334 fprintf(stderr,"Key with no value provided\n");
335 } else {
336
337 int k = 0;
338 for( k=0; k<kv_arr_len && values[0].data.d_status == GIMP_PDB_SUCCESS; k+=2 )
339 {
340 char *key = param[8].data.d_stringarray[k];
341 char *value = param[8].data.d_stringarray[k + 1];
342 char *endptr = NULL;
343 float float_value = 0;
344
345 stp_describe_parameter(gimp_vars.v, key, &desc);
346
347 switch(desc.p_type)
348 {
349 case STP_PARAMETER_TYPE_STRING_LIST:
350
351 /*
352 * Some useful string stp parameters
353 *
354 * "PrintingMode": BW, Color
355 * "Resolution": "300", "720", etc.
356 * "PageSize": "Letter", "A4", etc. TODO: Support Custom
357 * "MediaType": "Plain", "Glossy", etc.
358 * "InputSlot": "Tray1", "Manual", etc.
359 * "ColorCorrection": Color Correction model
360 * "InkType": Type of ink or cartridge
361 * "InkSet": Set of inks to use
362 * "DitherAlgorithm": Dither algorithm
363 * "Weave": Weave method
364 * "PrintingDirection": "Bidirectional", "Unidirectional"
365 */
366
367 stp_set_string_parameter(gimp_vars.v, key, value);
368 break;
369
370 case STP_PARAMETER_TYPE_INT:
371 stp_set_int_parameter(gimp_vars.v, key, atoi(value));
372 break;
373
374 case STP_PARAMETER_TYPE_BOOLEAN:
375 stp_set_boolean_parameter(gimp_vars.v, key, atoi(value));
376 break;
377
378 case STP_PARAMETER_TYPE_DOUBLE:
379 case STP_PARAMETER_TYPE_DIMENSION:
380
381 /*
382 * Some useful floating point stp parameters
383 *
384 * "Brightness" 0-400%
385 * "Gamma" Output gamma 0.1 - 3.0
386 * "Contrast" 0.1 - 3.0
387 * "Saturation" 0-1000%
388 * "Density" 0-200%
389 * "DropSize1" 0.0-1.0
390 * "DropSize2" 0.0-1.0
391 * "DropSize3" 0.0-1.0
392 */
393
394 float_value = strtof(value, &endptr);
395 if (float_value == 0 && endptr == value)
396 {
397 /*
398 * No conversion was performed -- invalid floating point number
399 */
400 *nreturn_vals = 1;
401 values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
402 fprintf(stderr,"Invalid floating point value provided for key: %s\n", key);
403 }
404 else
405 {
406 if (desc.p_type == STP_PARAMETER_TYPE_DOUBLE)
407 stp_set_float_parameter(gimp_vars.v, key, float_value);
408 else
409 stp_set_dimension_parameter(gimp_vars.v, key, float_value);
410 }
411 break;
412
413 case STP_PARAMETER_TYPE_CURVE:
414 case STP_PARAMETER_TYPE_FILE:
415 case STP_PARAMETER_TYPE_RAW:
416 case STP_PARAMETER_TYPE_ARRAY:
417 values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
418 fprintf(stderr,"Parameter type unsupported in gimp2 plugin for parameter %s\n", key);
419 break;
420
421 case STP_PARAMETER_TYPE_INVALID:
422
423 /*
424 * Output scaling (0-100%, -PPI)
425 */
426
427 if (strncmp("Scaling", key, 7) == 0)
428 {
429 float_value = strtof(value, &endptr);
430 if (float_value == 0 && endptr == value)
431 {
432 /*
433 * No conversion was performed -- invalid floating point number
434 */
435
436 values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
437 fprintf(stderr,"Invalid floating point value provided for key: Scaling\n");
438 }
439 else
440 {
441 gimp_vars.scaling = float_value;
442
443 if (gimp_vars.scaling == 0) {
444 values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
445 fprintf(stderr,"Scaling cannot be 0\n");
446 }
447 else if (gimp_vars.scaling > 0)
448 {
449 /*
450 * Scaling > 0 in %
451 */
452
453 stp_set_width(gimp_vars.v, pointwidth * gimp_vars.scaling / 100.);
454 stp_set_height(gimp_vars.v, pointheight * gimp_vars.scaling / 100.);
455 }
456 else /* gimp_vars < 0 */
457 {
458 /*
459 * Scaling < 0 in DPI
460 */
461
462 pointwidth = gimp_pixels_to_units(pixwidth, GIMP_UNIT_POINT, -gimp_vars.scaling);
463 pointheight = gimp_pixels_to_units(pixheight, GIMP_UNIT_POINT, -gimp_vars.scaling);
464 stp_set_width(gimp_vars.v, pointwidth);
465 stp_set_height(gimp_vars.v, pointheight);
466 }
467 }
468 break;
469 }
470
471 /*
472 * Output orientation (-1 = auto, 0 = portrait, 1 = landscape)
473 */
474
475 else if (strncmp("Orientation", key, 11) == 0)
476 {
477 gimp_vars.orientation = atoi(value);
478 break;
479 }
480
481 default:
482 values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
483 fprintf(stderr,"Parameter unsupported in gimp2 plugin for parameter %s\n", key);
484 break;
485 }
486 stp_parameter_description_destroy(&desc);
487 }
488 }
489 }
490 break;
491
492 case GIMP_RUN_WITH_LAST_VALS:
493 values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
494 break;
495
496 default:
497 values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
498 break;
499 }
500
501 if (gimp_thumbnail_data)
502 g_free(gimp_thumbnail_data);
503
504 /*
505 * Print the image...
506 */
507 if (values[0].data.d_status == GIMP_PDB_SUCCESS)
508 {
509 /*
510 * Set the tile cache size...
511 */
512
513 if (drawable->height > drawable->width)
514 gimp_tile_cache_ntiles ((drawable->height + gimp_tile_width () - 1) /
515 gimp_tile_width () + 1);
516 else
517 gimp_tile_cache_ntiles ((drawable->width + gimp_tile_width () - 1) /
518 gimp_tile_width () + 1);
519
520 if (! stpui_print(&gimp_vars, image))
521 {
522 values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
523 }
524
525 /*
526 * Store data...
527 * FIXME! This is broken!
528 */
529
530 #if 0
531 if (run_mode == GIMP_RUN_INTERACTIVE)
532 gimp_set_data (PLUG_IN_NAME, vars, sizeof (vars));
533 #endif
534 }
535
536 /*
537 * Detach from the drawable...
538 */
539 gimp_drawable_detach (drawable);
540
541 if (export == GIMP_EXPORT_EXPORT)
542 gimp_image_delete (image_ID);
543 stp_vars_destroy(gimp_vars.v);
544 }
545
546 /*
547 * 'do_print_dialog()' - Pop up the print dialog...
548 */
549
550 #pragma GCC diagnostic push
551 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
552 static void
gimp_errfunc(void * file,const char * buf,size_t bytes)553 gimp_errfunc(void *file, const char *buf, size_t bytes)
554 {
555 char formatbuf[32];
556 snprintf(formatbuf, 31, "%%%lus", (unsigned long) bytes);
557 g_message(formatbuf, buf);
558 }
559 #pragma GCC diagnostic pop
560
561 static gint
do_print_dialog(const gchar * proc_name,gint32 image_ID)562 do_print_dialog (const gchar *proc_name,
563 gint32 image_ID)
564 {
565 /*
566 * Generate the filename for the current user...
567 */
568 char *filename = gimp_personal_rc_file (cast_safe("printrc"));
569 stpui_set_printrc_file(filename);
570 g_free(filename);
571 if (! getenv("STP_PRINT_MESSAGES_TO_STDERR"))
572 stpui_set_errfunc(gimp_errfunc);
573 stpui_set_thumbnail_func(stpui_get_thumbnail_data_function);
574 stpui_set_thumbnail_data(gint2p(image_ID));
575 return stpui_do_print_dialog();
576 }
577