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