1 /*
2 * Gutenprint based raster filter for the Common UNIX Printing System.
3 *
4 * Copyright 1993-2008 by Mike Sweet.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 *
19 * Contents:
20 *
21 * main() - Main entry and processing of driver.
22 * cups_writefunc() - Write data to a file...
23 * cancel_job() - Cancel the current job...
24 * Image_get_appname() - Get the application we are running.
25 * Image_get_row() - Get one row of the image.
26 * Image_height() - Return the height of an image.
27 * Image_init() - Initialize an image.
28 * Image_conclude() - Close the progress display.
29 * Image_width() - Return the width of an image.
30 */
31
32 /*
33 * Include necessary headers...
34 */
35
36 #if 0
37 #define ENABLE_CUPS_LOAD_SAVE_OPTIONS
38 #endif
39
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
43 #include <cups/cups.h>
44 #include <cups/ppd.h>
45 #include <cups/raster.h>
46 #include <signal.h>
47 #include <string.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 #include <fcntl.h>
51 #include <errno.h>
52 #include <sys/times.h>
53 #include <strings.h>
54 #include <sys/time.h>
55 #ifdef HAVE_LIMITS_H
56 #include <limits.h>
57 #endif
58 #include "i18n.h"
59 #include <gutenprint/xml.h>
60
61 /* Solaris with gcc has problems because gcc's limits.h doesn't #define */
62 /* this */
63 #ifndef CHAR_BIT
64 #define CHAR_BIT 8
65 #endif
66
67 /*
68 * Structure for page raster data...
69 */
70
71 #if (CUPS_VERSION_MAJOR > 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1))
72 #define CUPS_HEADER_T cups_page_header2_t
73 #define CUPS_READ_HEADER cupsRasterReadHeader2
74 #else
75 #define CUPS_HEADER_T cups_page_header_t
76 #define CUPS_READ_HEADER cupsRasterReadHeader
77 #endif
78
79 typedef struct
80 {
81 cups_raster_t *ras; /* Raster stream to read from */
82 int page; /* Current page number */
83 int row; /* Current row number */
84 int left;
85 int right;
86 int bottom;
87 int top;
88 int width;
89 int height;
90 int left_trim;
91 int right_trim;
92 int top_trim;
93 int bottom_trim;
94 int adjusted_width;
95 int adjusted_height;
96 stp_dimension_t d_left;
97 stp_dimension_t d_right;
98 stp_dimension_t d_bottom;
99 stp_dimension_t d_top;
100 stp_dimension_t d_width;
101 stp_dimension_t d_height;
102 stp_dimension_t d_left_trim;
103 stp_dimension_t d_right_trim;
104 stp_dimension_t d_bottom_trim;
105 stp_dimension_t d_top_trim;
106 int last_percent;
107 int shrink_to_fit;
108 CUPS_HEADER_T header; /* Page header from file */
109 } cups_image_t;
110
111 static void cups_writefunc(void *file, const char *buf, size_t bytes);
112 static void cups_errfunc(void *file, const char *buf, size_t bytes);
113 static void cups_dbgfunc(void *file, const char *buf, size_t bytes);
114 static void cancel_job(int sig);
115 static const char *Image_get_appname(stp_image_t *image);
116 static stp_image_status_t Image_get_row(stp_image_t *image,
117 unsigned char *data,
118 size_t byte_limit, int row);
119 static int Image_height(stp_image_t *image);
120 static int Image_width(stp_image_t *image);
121 static void Image_conclude(stp_image_t *image);
122 static void Image_init(stp_image_t *image);
123
124 static stp_image_t theImage =
125 {
126 Image_init,
127 NULL, /* reset */
128 Image_width,
129 Image_height,
130 Image_get_row,
131 Image_get_appname,
132 Image_conclude,
133 NULL
134 };
135
136 static volatile stp_image_status_t Image_status = STP_IMAGE_STATUS_OK;
137 static double total_bytes_printed = 0;
138 static int print_messages_as_errors = 0;
139 static int suppress_messages = 0;
140 static int suppress_verbose_messages = 0;
141 static const stp_string_list_t *po = NULL;
142 #ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS
143 static const char *save_file_name = NULL;
144 static const char *load_file_name = NULL;
145 #endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */
146
147 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
148 #pragma GCC diagnostic push
149 #pragma GCC diagnostic ignored "-Wcast-qual"
150 static inline void *
cast_safe(const void * ptr)151 cast_safe(const void *ptr)
152 {
153 return (void *)ptr;
154 }
155 #pragma GCC diagnostic pop
156
157 static void
set_string_parameter(stp_vars_t * v,const char * name,const char * val)158 set_string_parameter(stp_vars_t *v, const char *name, const char *val)
159 {
160 if (! suppress_messages)
161 fprintf(stderr, "DEBUG: Gutenprint: Set special string %s to %s\n", name, val);
162 stp_set_string_parameter(v, name, val);
163 }
164
165
166 static void
set_special_parameter(stp_vars_t * v,const char * name,int choice)167 set_special_parameter(stp_vars_t *v, const char *name, int choice)
168 {
169 stp_parameter_t desc;
170 stp_describe_parameter(v, name, &desc);
171 if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)
172 {
173 #if 0
174 if (stp_check_string_parameter(v, name, STP_PARAMETER_ACTIVE))
175 {
176 if (! suppress_messages)
177 fprintf(stderr, "DEBUG: Gutenprint: Not overriding special parameter %s (%s)\n",
178 name, stp_get_string_parameter(v, name));
179 }
180 else
181 #endif
182 if (choice < 0)
183 {
184 stp_clear_string_parameter(v, name);
185 if (! suppress_messages)
186 fprintf(stderr, "DEBUG: Gutenprint: Clear special parameter %s\n",
187 name);
188 }
189 else if (choice >= stp_string_list_count(desc.bounds.str))
190 {
191 if (! suppress_messages)
192 stp_i18n_printf(po, _("ERROR: Unable to set Gutenprint option %s "
193 "(%d > %d)!\n"), name, choice,
194 stp_string_list_count(desc.bounds.str));
195 }
196 else
197 {
198 stp_set_string_parameter
199 (v, name, stp_string_list_param(desc.bounds.str, choice)->name);
200 if (! suppress_messages)
201 fprintf(stderr, "DEBUG: Gutenprint: Set special parameter %s to choice %d (%s)\n",
202 name, choice,
203 stp_string_list_param(desc.bounds.str, choice)->name);
204 }
205 }
206 else
207 {
208 if (! suppress_messages)
209 fprintf(stderr, "DEBUG: Gutenprint: Unable to set special %s: not a string\n",
210 name);
211 }
212 stp_parameter_description_destroy(&desc);
213 }
214
215 static void
print_debug_block(const stp_vars_t * v,const cups_image_t * cups)216 print_debug_block(const stp_vars_t *v, const cups_image_t *cups)
217 {
218 fprintf(stderr, "DEBUG: Gutenprint: Page data:\n");
219 fprintf(stderr, "DEBUG: Gutenprint: MediaClass = \"%s\"\n", cups->header.MediaClass);
220 fprintf(stderr, "DEBUG: Gutenprint: MediaColor = \"%s\"\n", cups->header.MediaColor);
221 fprintf(stderr, "DEBUG: Gutenprint: MediaType = \"%s\"\n", cups->header.MediaType);
222 fprintf(stderr, "DEBUG: Gutenprint: OutputType = \"%s\"\n", cups->header.OutputType);
223
224 fprintf(stderr, "DEBUG: Gutenprint: AdvanceDistance = %d\n", cups->header.AdvanceDistance);
225 fprintf(stderr, "DEBUG: Gutenprint: AdvanceMedia = %d\n", cups->header.AdvanceMedia);
226 fprintf(stderr, "DEBUG: Gutenprint: Collate = %d\n", cups->header.Collate);
227 fprintf(stderr, "DEBUG: Gutenprint: CutMedia = %d\n", cups->header.CutMedia);
228 fprintf(stderr, "DEBUG: Gutenprint: Duplex = %d\n", cups->header.Duplex);
229 fprintf(stderr, "DEBUG: Gutenprint: HWResolution = [ %d %d ]\n", cups->header.HWResolution[0],
230 cups->header.HWResolution[1]);
231 fprintf(stderr, "DEBUG: Gutenprint: ImagingBoundingBox = [ %d %d %d %d ]\n",
232 cups->header.ImagingBoundingBox[0], cups->header.ImagingBoundingBox[1],
233 cups->header.ImagingBoundingBox[2], cups->header.ImagingBoundingBox[3]);
234 fprintf(stderr, "DEBUG: Gutenprint: InsertSheet = %d\n", cups->header.InsertSheet);
235 fprintf(stderr, "DEBUG: Gutenprint: Jog = %d\n", cups->header.Jog);
236 fprintf(stderr, "DEBUG: Gutenprint: LeadingEdge = %d\n", cups->header.LeadingEdge);
237 fprintf(stderr, "DEBUG: Gutenprint: Margins = [ %d %d ]\n", cups->header.Margins[0],
238 cups->header.Margins[1]);
239 fprintf(stderr, "DEBUG: Gutenprint: ManualFeed = %d\n", cups->header.ManualFeed);
240 fprintf(stderr, "DEBUG: Gutenprint: MediaPosition = %d\n", cups->header.MediaPosition);
241 fprintf(stderr, "DEBUG: Gutenprint: MediaWeight = %d\n", cups->header.MediaWeight);
242 fprintf(stderr, "DEBUG: Gutenprint: MirrorPrint = %d\n", cups->header.MirrorPrint);
243 fprintf(stderr, "DEBUG: Gutenprint: NegativePrint = %d\n", cups->header.NegativePrint);
244 fprintf(stderr, "DEBUG: Gutenprint: NumCopies = %d\n", cups->header.NumCopies);
245 fprintf(stderr, "DEBUG: Gutenprint: Orientation = %d\n", cups->header.Orientation);
246 fprintf(stderr, "DEBUG: Gutenprint: OutputFaceUp = %d\n", cups->header.OutputFaceUp);
247 fprintf(stderr, "DEBUG: Gutenprint: PageSize = [ %d %d ]\n", cups->header.PageSize[0],
248 cups->header.PageSize[1]);
249 fprintf(stderr, "DEBUG: Gutenprint: Separations = %d\n", cups->header.Separations);
250 fprintf(stderr, "DEBUG: Gutenprint: TraySwitch = %d\n", cups->header.TraySwitch);
251 fprintf(stderr, "DEBUG: Gutenprint: Tumble = %d\n", cups->header.Tumble);
252 fprintf(stderr, "DEBUG: Gutenprint: cupsWidth = %d\n", cups->header.cupsWidth);
253 fprintf(stderr, "DEBUG: Gutenprint: cupsHeight = %d\n", cups->header.cupsHeight);
254 fprintf(stderr, "DEBUG: Gutenprint: cups->width = %d\n", cups->width);
255 fprintf(stderr, "DEBUG: Gutenprint: cups->height = %d\n", cups->height);
256 fprintf(stderr, "DEBUG: Gutenprint: cups->adjusted_width = %d\n", cups->adjusted_width);
257 fprintf(stderr, "DEBUG: Gutenprint: cups->adjusted_height = %d\n", cups->adjusted_height);
258 fprintf(stderr, "DEBUG: Gutenprint: cupsMediaType = %d\n", cups->header.cupsMediaType);
259 fprintf(stderr, "DEBUG: Gutenprint: cupsBitsPerColor = %d\n", cups->header.cupsBitsPerColor);
260 fprintf(stderr, "DEBUG: Gutenprint: cupsBitsPerPixel = %d\n", cups->header.cupsBitsPerPixel);
261 fprintf(stderr, "DEBUG: Gutenprint: cupsBytesPerLine = %d\n", cups->header.cupsBytesPerLine);
262 fprintf(stderr, "DEBUG: Gutenprint: cupsColorOrder = %d\n", cups->header.cupsColorOrder);
263 fprintf(stderr, "DEBUG: Gutenprint: cupsColorSpace = %d\n", cups->header.cupsColorSpace);
264 fprintf(stderr, "DEBUG: Gutenprint: cupsCompression = %d\n", cups->header.cupsCompression);
265 fprintf(stderr, "DEBUG: Gutenprint: cupsRowCount = %d\n", cups->header.cupsRowCount);
266 fprintf(stderr, "DEBUG: Gutenprint: cupsRowFeed = %d\n", cups->header.cupsRowFeed);
267 fprintf(stderr, "DEBUG: Gutenprint: cupsRowStep = %d\n", cups->header.cupsRowStep);
268 fprintf(stderr, "DEBUG: Gutenprint: shrink page to fit %d\n", cups->shrink_to_fit);
269 stp_vars_print_error(v, "DEBUG");
270 fprintf(stderr, "DEBUG: Gutenprint: End page data\n");
271 }
272
273 static int
printer_supports_bw(const stp_vars_t * v)274 printer_supports_bw(const stp_vars_t *v)
275 {
276 stp_parameter_t desc;
277 int status = 0;
278 stp_describe_parameter(v, "PrintingMode", &desc);
279 if (stp_string_list_is_present(desc.bounds.str, "BW"))
280 status = 1;
281 stp_parameter_description_destroy(&desc);
282 return status;
283 }
284
285 static void
validate_options(stp_vars_t * v,cups_image_t * cups)286 validate_options(stp_vars_t *v, cups_image_t *cups)
287 {
288 stp_parameter_list_t params = stp_get_parameter_list(v);
289 int nparams = stp_parameter_list_count(params);
290 int i;
291 if (! suppress_messages)
292 fprintf(stderr, "DEBUG: Gutenprint: Validating options\n");
293 for (i = 0; i < nparams; i++)
294 {
295 const stp_parameter_t *param = stp_parameter_list_param(params, i);
296 stp_parameter_t desc;
297 stp_describe_parameter(v, param->name, &desc);
298 if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)
299 {
300 if (!stp_string_list_is_present
301 (desc.bounds.str, stp_get_string_parameter(v, desc.name)))
302 {
303 if (! suppress_messages)
304 {
305 const char *val = stp_get_string_parameter(v, desc.name);
306 fprintf(stderr, "DEBUG: Gutenprint: Clearing string %s (%s)\n",
307 desc.name, val ? val : "(null)");
308 }
309 stp_clear_string_parameter(v, desc.name);
310 if (!desc.read_only && desc.is_mandatory && desc.is_active)
311 {
312 if (! suppress_messages)
313 fprintf(stderr, "DEBUG: Gutenprint: Setting default string %s to %s\n",
314 desc.name, desc.deflt.str ? desc.deflt.str : "(null)");
315 stp_set_string_parameter(v, desc.name, desc.deflt.str);
316 if (strcmp(desc.name, "PageSize") == 0)
317 {
318 const stp_papersize_t *ps =
319 stp_describe_papersize(v, desc.deflt.str);
320 if (ps->width > 0)
321 {
322 if (! suppress_messages)
323 fprintf(stderr, "DEBUG: Gutenprint: Setting page width to %.3f\n",
324 ps->width);
325 if (ps->width < stp_get_page_width(v))
326 stp_set_page_width(v, ps->width);
327 }
328 if (ps->height > 0)
329 {
330 if (! suppress_messages)
331 fprintf(stderr, "DEBUG: Gutenprint: Setting page height to %.3f\n",
332 ps->height);
333 if (ps->height < stp_get_page_height(v))
334 stp_set_page_height(v, ps->height);
335 }
336 }
337 }
338 }
339 }
340 stp_parameter_description_destroy(&desc);
341 }
342 if (! suppress_messages)
343 fprintf(stderr, "DEBUG: Gutenprint: Done validating options\n");
344 stp_parameter_list_destroy(params);
345 }
346
347 static stp_vars_t *
initialize_page(cups_image_t * cups,const stp_vars_t * default_settings,const char * page_size_name)348 initialize_page(cups_image_t *cups, const stp_vars_t *default_settings,
349 const char *page_size_name)
350 {
351 stp_dimension_t tmp_left, tmp_right, tmp_top, tmp_bottom;
352 stp_vars_t *v = stp_vars_create_copy(default_settings);
353
354 if (! suppress_messages)
355 fprintf(stderr, "DEBUG: Gutenprint: Initialize page\n");
356
357 if (cups->header.cupsBitsPerColor == 16)
358 set_string_parameter(v, "ChannelBitDepth", "16");
359 else
360 set_string_parameter(v, "ChannelBitDepth", "8");
361 switch (cups->header.cupsColorSpace)
362 {
363 case CUPS_CSPACE_W :
364 /* DyeSub photo printers don't support black & white ink! */
365 if (printer_supports_bw(v))
366 set_string_parameter(v, "PrintingMode", "BW");
367 set_string_parameter(v, "InputImageType", "Whitescale");
368 break;
369 case CUPS_CSPACE_K :
370 /* DyeSub photo printers don't support black & white ink! */
371 if (printer_supports_bw(v))
372 set_string_parameter(v, "PrintingMode", "BW");
373 set_string_parameter(v, "InputImageType", "Grayscale");
374 break;
375 case CUPS_CSPACE_RGB :
376 set_string_parameter(v, "PrintingMode", "Color");
377 set_string_parameter(v, "InputImageType", "RGB");
378 break;
379 case CUPS_CSPACE_CMY :
380 set_string_parameter(v, "PrintingMode", "Color");
381 set_string_parameter(v, "InputImageType", "CMY");
382 break;
383 case CUPS_CSPACE_CMYK :
384 set_string_parameter(v, "PrintingMode", "Color");
385 set_string_parameter(v, "InputImageType", "CMYK");
386 break;
387 case CUPS_CSPACE_KCMY :
388 set_string_parameter(v, "PrintingMode", "Color");
389 set_string_parameter(v, "InputImageType", "KCMY");
390 break;
391 default :
392 stp_i18n_printf(po, _("ERROR: Gutenprint detected a bad colorspace "
393 "(%d)!\n"), cups->header.cupsColorSpace);
394 break;
395 }
396
397 set_special_parameter(v, "Resolution", cups->header.cupsCompression - 1);
398
399 set_special_parameter(v, "Quality", cups->header.cupsRowFeed - 1);
400
401 if (strlen(cups->header.MediaClass) > 0)
402 set_string_parameter(v, "InputSlot", cups->header.MediaClass);
403
404 if (strlen(cups->header.MediaType) > 0)
405 set_string_parameter(v, "MediaType", cups->header.MediaType);
406
407 if (! suppress_messages)
408 fprintf(stderr, "DEBUG: Gutenprint: PageSize = %dx%d\n", cups->header.PageSize[0],
409 cups->header.PageSize[1]);
410
411 if (page_size_name)
412 {
413 if (strcmp(page_size_name, "Custom") == 0)
414 {
415 if (!suppress_messages)
416 fprintf(stderr, "DEBUG: Gutenprint: Using custom page size for (%d, %d)\n",
417 cups->header.PageSize[1], cups->header.PageSize[0]);
418 stp_set_page_width(v, cups->header.PageSize[0]);
419 stp_set_page_height(v, cups->header.PageSize[1]);
420 }
421 else if (stp_describe_papersize(v, page_size_name))
422 {
423 stp_dimension_t width, height;
424 if (!suppress_messages)
425 fprintf(stderr, "DEBUG: Gutenprint: Using page size %s with (%d, %d)\n",
426 page_size_name, cups->header.PageSize[1], cups->header.PageSize[0]);
427 set_string_parameter(v, "PageSize", page_size_name);
428 stp_get_media_size(v, &width, &height);
429 if (width > 0)
430 stp_set_page_width(v, width);
431 else
432 stp_set_page_width(v, cups->header.PageSize[0]);
433 if (height > 0)
434 stp_set_page_height(v, height);
435 else
436 stp_set_page_height(v, cups->header.PageSize[1]);
437 }
438 else
439 {
440 if (!suppress_messages)
441 fprintf(stderr, "DEBUG: Gutenprint: Can't find page size %s with (%d, %d), using custom page size\n",
442 page_size_name, cups->header.PageSize[1], cups->header.PageSize[0]);
443 stp_set_page_width(v, cups->header.PageSize[0]);
444 stp_set_page_height(v, cups->header.PageSize[1]);
445 }
446 }
447 else
448 {
449 if (! suppress_messages)
450 fprintf(stderr, "DEBUG: Gutenprint: No named media size for (%d, %d)\n",
451 cups->header.PageSize[1], cups->header.PageSize[0]);
452 stp_set_page_width(v, cups->header.PageSize[0]);
453 stp_set_page_height(v, cups->header.PageSize[1]);
454 }
455
456 /*
457 * Duplex
458 * Note that the names MUST match those in the printer driver(s)
459 */
460
461 if (cups->header.Duplex != 0)
462 {
463 if (cups->header.Tumble != 0)
464 set_string_parameter(v, "Duplex", "DuplexTumble");
465 else
466 set_string_parameter(v, "Duplex", "DuplexNoTumble");
467 }
468
469 cups->shrink_to_fit =
470 (stp_check_int_parameter(v, "CUPSShrinkPage", STP_PARAMETER_ACTIVE) ?
471 stp_get_int_parameter(v, "CUPSShrinkPage") : 0);
472
473 set_string_parameter(v, "JobMode", "Job");
474 validate_options(v, cups);
475 stp_get_media_size(v, &(cups->d_width), &(cups->d_height));
476 stp_get_maximum_imageable_area(v, &tmp_left, &tmp_right,
477 &tmp_bottom, &tmp_top);
478 stp_get_imageable_area(v, &(cups->d_left), &(cups->d_right),
479 &(cups->d_bottom), &(cups->d_top));
480 if (! suppress_messages)
481 {
482 fprintf(stderr, "DEBUG: Gutenprint: limits w %.3f l %.3f r %.3f h %.3f t %.3f b %.3f\n",
483 cups->d_width, cups->d_left, cups->d_right, cups->d_height, cups->d_top, cups->d_bottom);
484 fprintf(stderr, "DEBUG: Gutenprint: max limits l %.3f r %.3f t %.3f b %.3f\n",
485 tmp_left, tmp_right, tmp_top, tmp_bottom);
486 }
487
488 if (tmp_left < 0)
489 tmp_left = 0;
490 if (tmp_top < 0)
491 tmp_top = 0;
492 if (tmp_right > tmp_left + cups->d_width)
493 tmp_right = cups->d_width;
494 if (tmp_bottom > tmp_top + cups->d_height)
495 tmp_bottom = cups->d_height;
496 if (tmp_left < cups->d_left)
497 {
498 if (cups->shrink_to_fit != 1)
499 {
500 cups->d_left_trim = cups->d_left - tmp_left;
501 tmp_left = cups->d_left;
502 }
503 else
504 cups->d_left_trim = 0;
505 if (! suppress_messages)
506 fprintf(stderr, "DEBUG: Gutenprint: left margin %.3f\n", cups->d_left_trim);
507 }
508 else
509 {
510 cups->d_left_trim = 0;
511 if (! suppress_messages)
512 fprintf(stderr, "DEBUG: Gutenprint: Adjusting left margin from %.3f to %.3f\n",
513 cups->d_left, tmp_left);
514 cups->d_left = tmp_left;
515 }
516 if (tmp_right > cups->d_right)
517 {
518 if (cups->shrink_to_fit != 1)
519 {
520 cups->d_right_trim = tmp_right - cups->d_right;
521 tmp_right = cups->d_right;
522 }
523 else
524 cups->d_right_trim = 0;
525 if (! suppress_messages)
526 fprintf(stderr, "DEBUG: Gutenprint: right margin %.3f\n", cups->d_right_trim);
527 }
528 else
529 {
530 cups->d_right_trim = 0;
531 if (! suppress_messages)
532 fprintf(stderr, "DEBUG: Gutenprint: Adjusting right margin from %.3f to %.3f\n",
533 cups->d_right, tmp_right);
534 cups->d_right = tmp_right;
535 }
536 if (tmp_top < cups->d_top)
537 {
538 if (cups->shrink_to_fit != 1)
539 {
540 cups->d_top_trim = cups->d_top - tmp_top;
541 tmp_top = cups->d_top;
542 }
543 else
544 cups->d_top_trim = 0;
545 if (! suppress_messages)
546 fprintf(stderr, "DEBUG: Gutenprint: top margin %.3f\n", cups->d_top_trim);
547 }
548 else
549 {
550 cups->d_top_trim = 0;
551 if (! suppress_messages)
552 fprintf(stderr, "DEBUG: Gutenprint: Adjusting top margin from %.3f to %.3f\n",
553 cups->d_top, tmp_top);
554 cups->d_top = tmp_top;
555 }
556 if (tmp_bottom > cups->d_bottom)
557 {
558 if (cups->shrink_to_fit != 1)
559 {
560 cups->d_bottom_trim = tmp_bottom - cups->d_bottom;
561 tmp_bottom = cups->d_bottom;
562 }
563 else
564 cups->d_bottom_trim = 0;
565 if (! suppress_messages)
566 fprintf(stderr, "DEBUG: Gutenprint: bottom margin %.3f\n", cups->d_bottom_trim);
567 }
568 else
569 {
570 cups->d_bottom_trim = 0;
571 if (! suppress_messages)
572 fprintf(stderr, "DEBUG: Gutenprint: Adjusting bottom margin from %.3f to %.3f\n",
573 cups->d_bottom, tmp_bottom);
574 cups->d_bottom = tmp_bottom;
575 }
576
577 if (cups->shrink_to_fit == 2)
578 {
579 stp_dimension_t t_left, t_right, t_bottom, t_top;
580 stp_get_imageable_area(v, &(t_left), &(t_right), &(t_bottom), &(t_top));
581 stp_set_width(v, t_right - t_left);
582 stp_set_height(v, t_bottom - t_top);
583 stp_set_left(v, t_left);
584 stp_set_top(v, t_top);
585 }
586 else
587 {
588 stp_set_width(v, cups->d_right - cups->d_left);
589 stp_set_height(v, cups->d_bottom - cups->d_top);
590 stp_set_left(v, cups->d_left);
591 stp_set_top(v, cups->d_top);
592 }
593
594 cups->d_right = cups->d_width - cups->d_right;
595 if (cups->shrink_to_fit == 1)
596 cups->d_width = tmp_right - tmp_left;
597 else
598 cups->d_width = cups->d_width - cups->d_left - cups->d_right;
599 cups->width = cups->header.HWResolution[0] * cups->d_width / 72;
600 cups->left = cups->header.HWResolution[0] * cups->d_left / 72;
601 cups->right = cups->header.HWResolution[0] * cups->d_right / 72;
602 cups->left_trim = cups->header.HWResolution[0] * cups->d_left_trim / 72;
603 cups->right_trim = cups->header.HWResolution[0] * cups->d_right_trim / 72;
604 cups->adjusted_width = cups->width;
605 if (cups->adjusted_width > cups->header.cupsWidth)
606 cups->adjusted_width = cups->header.cupsWidth;
607
608 cups->d_bottom = cups->d_height - cups->d_bottom;
609 if (cups->shrink_to_fit == 1)
610 cups->d_height = tmp_bottom - tmp_top;
611 else
612 cups->d_height = cups->d_height - cups->d_top - cups->d_bottom;
613 cups->height = cups->header.HWResolution[1] * cups->d_height / 72;
614 cups->top = cups->header.HWResolution[1] * cups->d_top / 72;
615 cups->bottom = cups->header.HWResolution[1] * cups->d_bottom / 72;
616 cups->top_trim = cups->header.HWResolution[1] * cups->d_top_trim / 72;
617 cups->bottom_trim = cups->header.HWResolution[1] * cups->d_bottom_trim / 72;
618 cups->adjusted_height = cups->height;
619 if (cups->adjusted_height > cups->header.cupsHeight)
620 cups->adjusted_height = cups->header.cupsHeight;
621 if (! suppress_messages)
622 {
623 fprintf(stderr, "DEBUG: Gutenprint: CUPS settings w %d l %d r %d h %d t %d b %d\n",
624 cups->width, cups->left, cups->right,
625 cups->height, cups->top, cups->bottom);
626 fprintf(stderr, "DEBUG: Gutenprint: adjusted w %d h %d\n",
627 cups->adjusted_width, cups->adjusted_height);
628
629 }
630
631 if (! suppress_messages)
632 fprintf(stderr, "DEBUG: Gutenprint: End initialize page\n");
633 return v;
634 }
635
636 static void
purge_excess_data(cups_image_t * cups)637 purge_excess_data(cups_image_t *cups)
638 {
639 char *buffer = stp_malloc(cups->header.cupsBytesPerLine);
640 if (buffer)
641 {
642 if (! suppress_messages && ! suppress_verbose_messages )
643 fprintf(stderr, "DEBUG2: Gutenprint: Purging %d row%s\n",
644 cups->header.cupsHeight - cups->row,
645 ((cups->header.cupsHeight - cups->row) == 1 ? "" : "s"));
646 while (cups->row < cups->header.cupsHeight)
647 {
648 cupsRasterReadPixels(cups->ras, (unsigned char *)buffer,
649 cups->header.cupsBytesPerLine);
650 cups->row ++;
651 }
652 }
653 stp_free(buffer);
654 }
655
656 static void
set_all_options(stp_vars_t * v,cups_option_t * options,int num_options,ppd_file_t * ppd)657 set_all_options(stp_vars_t *v, cups_option_t *options, int num_options,
658 ppd_file_t *ppd)
659 {
660 stp_parameter_list_t params = stp_get_parameter_list(v);
661 int nparams = stp_parameter_list_count(params);
662 int i;
663 const char *val; /* CUPS option value */
664 ppd_option_t *ppd_option;
665 if (! suppress_messages)
666 fprintf(stderr, "DEBUG: Gutenprint: Set options:\n");
667 val = cupsGetOption("StpiShrinkOutput", num_options, options);
668 if (!val)
669 {
670 ppd_option = ppdFindOption(ppd, "StpiShrinkOutput");
671 if (ppd_option)
672 val = ppd_option->defchoice;
673 }
674 if (val)
675 {
676 if (!strcasecmp(val, "crop"))
677 stp_set_int_parameter(v, "CUPSShrinkPage", 0);
678 else if (!strcasecmp(val, "expand"))
679 stp_set_int_parameter(v, "CUPSShrinkPage", 2);
680 else
681 stp_set_int_parameter(v, "CUPSShrinkPage", 1);
682 }
683 else
684 stp_set_int_parameter(v, "CUPSShrinkPage", 1);
685 for (i = 0; i < nparams; i++)
686 {
687 const stp_parameter_t *param = stp_parameter_list_param(params, i);
688 stp_parameter_t desc;
689 char *ppd_option_name = stp_malloc(strlen(param->name) + 8); /* StpFineFOO\0 */
690
691 stp_describe_parameter(v, param->name, &desc);
692 if (desc.p_type == STP_PARAMETER_TYPE_DOUBLE)
693 {
694 sprintf(ppd_option_name, "Stp%s", desc.name);
695 val = cupsGetOption(ppd_option_name, num_options, options);
696 if (!val)
697 {
698 ppd_option = ppdFindOption(ppd, ppd_option_name);
699 if (ppd_option)
700 val = ppd_option->defchoice;
701 }
702 if (val && !strncasecmp(val, "Custom.", 7))
703 {
704 double dval = atof(val + 7);
705
706 if (! suppress_messages)
707 fprintf(stderr, "DEBUG: Gutenprint: Set float %s to %f\n",
708 desc.name, dval);
709 if (dval > desc.bounds.dbl.upper)
710 dval = desc.bounds.dbl.upper;
711 stp_set_float_parameter(v, desc.name, dval);
712 }
713 else if (val && strlen(val) > 0 && strcmp(val, "None") != 0)
714 {
715 double fine_val = 0;
716 if (strchr(val, (int) '.'))
717 {
718 fine_val = atof(val);
719 if (! suppress_messages)
720 fprintf(stderr, "DEBUG: Gutenprint: Set float %s to %f (%s)\n",
721 desc.name, fine_val, val);
722 }
723 else
724 {
725 double coarse_val = atof(val) * 0.001;
726 sprintf(ppd_option_name, "StpFine%s", desc.name);
727 val = cupsGetOption(ppd_option_name, num_options, options);
728 if (!val)
729 {
730 ppd_option = ppdFindOption(ppd, ppd_option_name);
731 if (ppd_option)
732 val = ppd_option->defchoice;
733 }
734 if (val && strlen(val) > 0 && strcmp(val, "None") != 0)
735 fine_val = atof(val) * 0.001;
736 if (! suppress_messages)
737 fprintf(stderr, "DEBUG: Gutenprint: Set float %s to %f + %f\n",
738 desc.name, coarse_val, fine_val);
739 fine_val += coarse_val;
740 }
741 if (fine_val > desc.bounds.dbl.upper)
742 fine_val = desc.bounds.dbl.upper;
743 if (fine_val < desc.bounds.dbl.lower)
744 fine_val = desc.bounds.dbl.lower;
745 stp_set_float_parameter(v, desc.name, fine_val);
746 }
747 }
748 else
749 {
750 sprintf(ppd_option_name, "Stp%s", desc.name);
751 val = cupsGetOption(ppd_option_name, num_options, options);
752 if (!val)
753 {
754 ppd_option = ppdFindOption(ppd, ppd_option_name);
755 if (ppd_option)
756 val = ppd_option->defchoice;
757 }
758 if (val && ((strlen(val) > 0 && strcmp(val, "None") != 0) ||
759 (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)))
760 {
761 stp_curve_t *curve;
762 stp_raw_t *raw;
763 switch (desc.p_type)
764 {
765 case STP_PARAMETER_TYPE_STRING_LIST:
766 if (! suppress_messages)
767 fprintf(stderr, "DEBUG: Gutenprint: Set string %s to %s\n",
768 desc.name, val);
769 set_string_parameter(v, desc.name, val);
770 break;
771 case STP_PARAMETER_TYPE_INT:
772 if (!strncasecmp(val, "Custom.", 7))
773 val += 7;
774
775 if (! suppress_messages)
776 fprintf(stderr, "DEBUG: Gutenprint: Set int %s to %s (%d)\n",
777 desc.name, val, atoi(val));
778 stp_set_int_parameter(v, desc.name, atoi(val));
779 break;
780 case STP_PARAMETER_TYPE_DIMENSION:
781 if (!strncasecmp(val, "Custom.", 7))
782 val += 7;
783
784 if (! suppress_messages)
785 fprintf(stderr, "DEBUG: Gutenprint: Set dimension %s to %s (%d)\n",
786 desc.name, val, atoi(val));
787
788 stp_set_dimension_parameter(v, desc.name, atoi(val));
789 break;
790 case STP_PARAMETER_TYPE_BOOLEAN:
791 if (! suppress_messages)
792 fprintf(stderr, "DEBUG: Gutenprint: Set bool %s to %s (%d)\n",
793 desc.name, val, strcasecmp(val, "true") == 0 ? 1 : 0);
794 stp_set_boolean_parameter
795 (v, desc.name, strcasecmp(val, "true") == 0 ? 1 : 0);
796 break;
797 case STP_PARAMETER_TYPE_CURVE:
798 curve = stp_curve_create_from_string(val);
799 if (! suppress_messages)
800 fprintf(stderr, "DEBUG: Gutenprint: Set curve %s to %s\n",
801 desc.name, curve ? val : "(NULL)");
802 if (curve)
803 {
804 stp_set_curve_parameter(v, desc.name, curve);
805 stp_curve_destroy(curve);
806 }
807 break;
808 case STP_PARAMETER_TYPE_RAW: /* figure this out later, too */
809 raw = stp_xmlstrtoraw(val);
810 if (! suppress_messages)
811 fprintf(stderr, "DEBUG: Gutenprint: Set raw %s to %s\n",
812 desc.name, raw ? val : "(NULL)");
813 if (raw)
814 {
815 stp_set_raw_parameter(v, desc.name, raw->data, raw->bytes);
816 stp_free(cast_safe(raw->data));
817 stp_free(raw);
818 }
819 break;
820 case STP_PARAMETER_TYPE_FILE: /* Probably not, security hole */
821 if (! suppress_messages)
822 fprintf(stderr, "DEBUG: Gutenprint: Ignoring option %s %s type %d\n",
823 desc.name, val, desc.p_type);
824 break;
825 default:
826 break;
827 }
828 }
829 else if (val)
830 {
831 if (! suppress_messages)
832 fprintf(stderr, "DEBUG: Gutenprint: Not setting %s to '%s'\n",
833 desc.name, val);
834 }
835 else
836 {
837 if (! suppress_messages)
838 fprintf(stderr, "DEBUG: Gutenprint: Not setting %s to (null)\n",
839 desc.name);
840 }
841 }
842 stp_parameter_description_destroy(&desc);
843 stp_free(ppd_option_name);
844 }
845 if (! suppress_messages)
846 fprintf(stderr, "DEBUG: Gutenprint: End options\n");
847 stp_parameter_list_destroy(params);
848 }
849
850 #ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS
851 static void
save_options(const char * save_name,const stp_vars_t * v)852 save_options(const char *save_name, const stp_vars_t *v)
853 {
854 FILE *f_options;
855 int i;
856 stp_vars_t *c = stp_vars_create();
857 stp_parameter_list_t params = stp_get_parameter_list(v);
858 stp_parameter_t desc;
859 stp_mxml_node_t *mxml = NULL;
860 int param_count;
861
862 if (!params)
863 {
864 stp_vars_destroy(c);
865 return;
866 }
867 f_options = fopen(save_name, "w");
868 if (!f_options)
869 {
870 stp_parameter_list_destroy(params);
871 stp_vars_destroy(c);
872 return;
873 }
874 param_count = stp_parameter_list_count(params);
875 stp_set_driver(c, stp_get_driver(v));
876 if (!suppress_messages)
877 fprintf(stderr, "DEBUG: Gutenprint: Saving parameters to %s\n", save_name);
878 for (i = 0; i < param_count; i++)
879 {
880 const stp_parameter_t *lparam =
881 stp_parameter_list_param(params, i);
882 stp_describe_parameter(v, lparam->name, &desc);
883 if (desc.read_only || !strcmp(desc.name, "ChannelBitDepth") ||
884 !stp_parameter_has_category_value(v, &desc, "Color", "Yes"))
885 {
886 if (!suppress_messages)
887 fprintf(stderr, "DEBUG: Gutenprint: skipping non-color %s\n",
888 desc.name);
889 stp_parameter_description_destroy(&desc);
890 continue;
891 }
892 switch (desc.p_type)
893 {
894 case STP_PARAMETER_TYPE_STRING_LIST:
895 if (stp_check_string_parameter(v, desc.name,
896 STP_PARAMETER_DEFAULTED))
897 {
898 if (!suppress_messages)
899 fprintf(stderr, "DEBUG: Gutenprint: SAVING string %s %s\n",
900 desc.name, stp_get_string_parameter(v, desc.name));
901 stp_set_string_parameter(c, desc.name,
902 stp_get_string_parameter(v, desc.name));
903 }
904 else if (desc.is_mandatory)
905 {
906 if (!suppress_messages)
907 fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted string %s %s\n",
908 desc.name, desc.deflt.str);
909 stp_set_string_parameter(c, desc.name, desc.deflt.str);
910 }
911 else if (!suppress_messages)
912 fprintf(stderr, "DEBUG: Gutenprint: skipping string %s\n", desc.name);
913 break;
914 case STP_PARAMETER_TYPE_RAW:
915 if (stp_check_raw_parameter(v, desc.name,
916 STP_PARAMETER_DEFAULTED))
917 {
918 const stp_raw_t *raw = stp_get_raw_parameter(v, desc.name);
919 if (!suppress_messages)
920 fprintf(stderr, "DEBUG: Gutenprint: SAVING raw %s\n", desc.name);
921 stp_set_raw_parameter(c, desc.name, raw->data, raw->bytes);
922 }
923 else if (!suppress_messages)
924 fprintf(stderr, "DEBUG: Gutenprint: skipping raw %s\n",
925 desc.name);
926 break;
927 case STP_PARAMETER_TYPE_BOOLEAN:
928 if (stp_check_boolean_parameter(v, desc.name, STP_PARAMETER_DEFAULTED))
929 {
930 if (!suppress_messages)
931 fprintf(stderr, "DEBUG: Gutenprint: SAVING bool %s %d\n",
932 desc.name, stp_get_boolean_parameter(v, desc.name));
933 stp_set_boolean_parameter(c, desc.name,
934 stp_get_boolean_parameter(v, desc.name));
935 }
936 else if (desc.is_mandatory)
937 {
938 if (!suppress_messages)
939 fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted bool %s %d\n",
940 desc.name, desc.deflt.boolean);
941 stp_set_boolean_parameter(c, desc.name, desc.deflt.boolean);
942 }
943 else if (!suppress_messages)
944 fprintf(stderr, "DEBUG: Gutenprint: skipping bool %s\n", desc.name);
945 break;
946 case STP_PARAMETER_TYPE_INT:
947 if (stp_check_int_parameter(v, desc.name, STP_PARAMETER_DEFAULTED))
948 {
949 if (!suppress_messages)
950 fprintf(stderr, "DEBUG: Gutenprint: SAVING int %s %d\n",
951 desc.name, stp_get_int_parameter(v, desc.name));
952 stp_set_int_parameter(c, desc.name, stp_get_int_parameter(v, desc.name));
953 }
954 else if (desc.is_mandatory)
955 {
956 if (!suppress_messages)
957 fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted int %s %d\n",
958 desc.name, desc.deflt.integer);
959 stp_set_int_parameter(c, desc.name, desc.deflt.integer);
960 }
961 else if (!suppress_messages)
962 fprintf(stderr, "DEBUG: Gutenprint: skipping int %s\n", desc.name);
963 break;
964 case STP_PARAMETER_TYPE_DOUBLE:
965 if (stp_check_float_parameter(v, desc.name, STP_PARAMETER_DEFAULTED))
966 {
967 if (!suppress_messages)
968 fprintf(stderr, "DEBUG: Gutenprint: SAVING float %s %f\n",
969 desc.name, stp_get_float_parameter(v, desc.name));
970 stp_set_float_parameter(c, desc.name,
971 stp_get_float_parameter(v, desc.name));
972 }
973 else if (desc.is_mandatory)
974 {
975 if (!suppress_messages)
976 fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted float %s %f\n",
977 desc.name, desc.deflt.dbl);
978 stp_set_float_parameter(c, desc.name, desc.deflt.dbl);
979 }
980 else if (!suppress_messages)
981 fprintf(stderr, "DEBUG: Gutenprint: skipping float %s\n", desc.name);
982 break;
983 case STP_PARAMETER_TYPE_DIMENSION:
984 if (stp_check_dimension_parameter(v, desc.name, STP_PARAMETER_DEFAULTED))
985 {
986 if (!suppress_messages)
987 fprintf(stderr, "DEBUG: Gutenprint: SAVING dimension %s %d\n",
988 desc.name, stp_get_dimension_parameter(v, desc.name));
989 stp_set_dimension_parameter(c, desc.name,
990 stp_get_dimension_parameter(v, desc.name));
991 }
992 else if (desc.is_mandatory)
993 {
994 if (!suppress_messages)
995 fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted dimension %s %d\n",
996 desc.name, desc.deflt.dimension);
997 stp_set_dimension_parameter(c, desc.name, desc.deflt.dimension);
998 }
999 else if (!suppress_messages)
1000 fprintf(stderr, "DEBUG: Gutenprint: skipping dimension %s\n", desc.name);
1001 break;
1002 case STP_PARAMETER_TYPE_CURVE:
1003 if (stp_check_curve_parameter(v, desc.name, STP_PARAMETER_DEFAULTED))
1004 {
1005 if (!suppress_messages)
1006 fprintf(stderr, "DEBUG: Gutenprint: SAVING curve %s\n", desc.name);
1007 stp_set_curve_parameter(c, desc.name,
1008 stp_get_curve_parameter(v, desc.name));
1009 }
1010 else if (desc.is_mandatory)
1011 {
1012 if (!suppress_messages)
1013 fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted curve %s\n", desc.name);
1014 stp_set_curve_parameter(c, desc.name, desc.deflt.curve);
1015 }
1016 else if (!suppress_messages)
1017 fprintf(stderr, "DEBUG: Gutenprint: skipping curve %s\n",
1018 desc.name);
1019 break;
1020 default:
1021 if (!suppress_messages)
1022 fprintf(stderr, "DEBUG: Gutenprint: Ignoring unknown type parameter %s (%d)\n",
1023 desc.name, desc.p_type);
1024 break;
1025 }
1026 stp_parameter_description_destroy(&desc);
1027 }
1028 stp_parameter_list_destroy(params);
1029 mxml = stp_xmltree_create_from_vars(c);
1030 if (mxml)
1031 {
1032 fputs("<?xml version=\"1.0\"?>\n\n", f_options);
1033 stp_mxmlSaveFile(mxml, f_options, STP_MXML_NO_CALLBACK);
1034 stp_mxmlDelete(mxml);
1035 }
1036 (void) fclose(f_options);
1037 stp_vars_destroy(c);
1038 if (!suppress_messages)
1039 fprintf(stderr, "DEBUG: Gutenprint: Done saving parameters to %s\n", save_name);
1040 }
1041
1042 static stp_vars_t *
load_options(const char * load_name)1043 load_options(const char *load_name)
1044 {
1045 FILE *f_options = fopen(load_name, "r");
1046 if (f_options)
1047 {
1048 stp_vars_t *settings = NULL;
1049 stp_mxml_node_t *mxml;
1050 mxml = stp_mxmlLoadFile(NULL, f_options, STP_MXML_NO_CALLBACK);
1051 if (mxml)
1052 {
1053 stp_mxml_node_t *nxml =
1054 stp_mxmlFindElement(mxml, mxml, "vars", NULL, NULL,
1055 STP_MXML_DESCEND);
1056 if (nxml)
1057 {
1058 settings = stp_vars_create_from_xmltree_ref(nxml->child, mxml);
1059 if (! suppress_messages)
1060 fprintf(stderr, "DEBUG: Gutenprint: loading options from %s\n",
1061 load_file_name);
1062 if (! suppress_messages)
1063 stp_vars_print_error(settings, "DEBUG");
1064 }
1065 }
1066 else
1067 fprintf(stderr, "DEBUG: Unable to load options from %s\n",
1068 load_file_name);
1069 fclose(f_options);
1070 return settings;
1071 }
1072 return NULL;
1073 }
1074
1075 #endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */
1076
1077 /*
1078 * 'main()' - Main entry and processing of driver.
1079 */
1080
1081 int /* O - Exit status */
main(int argc,char * argv[])1082 main(int argc, /* I - Number of command-line arguments */
1083 char *argv[]) /* I - Command-line arguments */
1084 {
1085 int fd; /* File descriptor */
1086 cups_image_t cups; /* CUPS image */
1087 const char *ppdfile; /* PPD environment variable */
1088 ppd_file_t *ppd; /* PPD file */
1089 ppd_size_t *size;
1090 const stp_printer_t *printer; /* Printer driver */
1091 int num_options; /* Number of CUPS options */
1092 cups_option_t *options; /* CUPS options */
1093 stp_vars_t *v = NULL;
1094 stp_vars_t *default_settings;
1095 int initialized_job = 0;
1096 const char *version_id;
1097 struct tms tms;
1098 long clocks_per_sec;
1099 struct timeval t1, t2;
1100 char *page_size_name = NULL;
1101 int aborted = 0;
1102 #ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS
1103 stp_vars_t *loaded_settings = NULL;
1104 #endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */
1105
1106
1107 /*
1108 * Don't buffer error/status messages...
1109 */
1110
1111 setbuf(stderr, NULL);
1112
1113 if (getenv("STP_SUPPRESS_MESSAGES"))
1114 suppress_messages = 1;
1115
1116 if (getenv("STP_SUPPRESS_VERBOSE_MESSAGES"))
1117 suppress_verbose_messages = 1;
1118
1119 /*
1120 * Initialize libgutenprint
1121 */
1122
1123 po = stp_i18n_load(getenv("LANG"));
1124
1125 theImage.rep = ∪︀
1126
1127 (void) gettimeofday(&t1, NULL);
1128 stp_set_global_errfunc(cups_errfunc);
1129 stp_set_global_dbgfunc(cups_dbgfunc);
1130 stp_set_global_errdata(stderr);
1131 stp_set_global_dbgdata(stderr);
1132 stp_init();
1133 version_id = stp_get_version();
1134 default_settings = stp_vars_create();
1135 stp_set_outfunc(default_settings, cups_writefunc);
1136 stp_set_outdata(default_settings, stdout);
1137
1138 /*
1139 * Check for valid arguments...
1140 */
1141 if (argc < 6 || argc > 7)
1142 {
1143 /*
1144 * We don't have the correct number of arguments; write an error message
1145 * and return.
1146 */
1147
1148 stp_i18n_printf(po, _("Usage: rastertoprinter job-id user title copies "
1149 "options [file]\n"));
1150 return (1);
1151 }
1152
1153 if (! suppress_messages)
1154 {
1155 fprintf(stderr, "DEBUG: Gutenprint: ============================================================\n");
1156 fprintf(stderr, "DEBUG: Gutenprint: VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\n");
1157 fprintf(stderr, "DEBUG: Gutenprint: %s Starting\n", version_id);
1158 fprintf(stderr, "DEBUG: Gutenprint: command line: %s '%s' '%s' '%s' '%s' %s%s%s%s\n",
1159 argv[0], argv[1], argv[2], argv[3], argv[4], "<args>",
1160 argc >= 7 ? " '" : "",
1161 argc >= 7 ? argv[6] : "",
1162 argc >= 7 ? "'" : "");
1163 }
1164
1165 /*
1166 * Get the PPD file...
1167 */
1168
1169 if ((ppdfile = getenv("PPD")) == NULL)
1170 {
1171 stp_i18n_printf(po, _("ERROR: No PPD file, unable to continue!\n"));
1172 return (1);
1173 }
1174 if (! suppress_messages)
1175 fprintf(stderr, "DEBUG: Gutenprint: using PPD file %s\n", ppdfile);
1176
1177 if ((ppd = ppdOpenFile(ppdfile)) == NULL)
1178 {
1179 stp_i18n_printf(po, _("ERROR: Gutenprint was unable to load PPD file "
1180 "\"%s\"!\n"), ppdfile);
1181 return (1);
1182 }
1183
1184 if (ppd->modelname == NULL)
1185 {
1186 stp_i18n_printf(po, _("ERROR: Gutenprint did not find a ModelName "
1187 "attribute in PPD file \"%s\"!\n"), ppdfile);
1188 ppdClose(ppd);
1189 return (1);
1190 }
1191
1192 if (ppd->nickname == NULL)
1193 {
1194 stp_i18n_printf(po, _("ERROR: Gutenprint did not find a NickName attribute "
1195 "in PPD file \"%s\"!\n"), ppdfile);
1196 ppdClose(ppd);
1197 return (1);
1198 }
1199 else if (strlen(ppd->nickname) <
1200 strlen(ppd->modelname) + strlen(CUPS_PPD_NICKNAME_STRING) + 3)
1201 {
1202 stp_i18n_printf(po, _("ERROR: Gutenprint found a corrupted NickName "
1203 "attribute in PPD file \"%s\"!\n"), ppdfile);
1204 ppdClose(ppd);
1205 return (1);
1206 }
1207 else if (strcmp(ppd->nickname + strlen(ppd->modelname) +
1208 strlen(CUPS_PPD_NICKNAME_STRING), version_id) != 0 &&
1209 (strlen(ppd->nickname + strlen(ppd->modelname) +
1210 strlen(CUPS_PPD_NICKNAME_STRING)) < strlen(version_id) ||
1211 !((strncmp(ppd->nickname + strlen(ppd->modelname) +
1212 strlen(CUPS_PPD_NICKNAME_STRING), version_id,
1213 strlen(version_id)) == 0) &&
1214 *(ppd->nickname + strlen(ppd->modelname) +
1215 strlen(CUPS_PPD_NICKNAME_STRING)) != ' ')))
1216 {
1217 stp_i18n_printf(po, _("ERROR: The PPD version (%s) is not compatible with "
1218 "Gutenprint %s. Please run `%scups-genppdupdate' as administrator.\n"),
1219 ppd->nickname+strlen(ppd->modelname)+strlen(CUPS_PPD_NICKNAME_STRING),
1220 version_id, SBINDIR);
1221 fprintf(stderr, "DEBUG: Gutenprint: If you have upgraded your version of Gutenprint\n");
1222 fprintf(stderr, "DEBUG: Gutenprint: recently, you must reinstall all printer queues.\n");
1223 fprintf(stderr, "DEBUG: Gutenprint: If the previous installed version of Gutenprint\n");
1224 fprintf(stderr, "DEBUG: Gutenprint: was 5.0.0 or higher, you can use the `cups-genppdupdate'\n");
1225 fprintf(stderr, "DEBUG: Gutenprint: program to do this; if the previous installed version\n");
1226 fprintf(stderr, "DEBUG: Gutenprint: was older, you can use the Modify Printer command via\n");
1227 fprintf(stderr, "DEBUG: Gutenprint: the CUPS web interface: http://localhost:631/printers.\n");
1228 ppdClose(ppd);
1229 return 1;
1230 }
1231
1232 /*
1233 * Get the STP options, if any...
1234 */
1235
1236 num_options = cupsParseOptions(argv[5], 0, &options);
1237 ppdMarkDefaults(ppd);
1238 cupsMarkOptions(ppd, num_options, options);
1239 size = ppdPageSize(ppd, NULL);
1240
1241 if (size)
1242 page_size_name = stp_strdup(size->name);
1243
1244 if (! suppress_messages)
1245 fprintf(stderr, "DEBUG: Gutenprint: CUPS option count is %d (%d bytes)\n",
1246 num_options, (int)strlen(argv[5]));
1247
1248 if (num_options > 0)
1249 {
1250 int i;
1251 for (i = 0; i < num_options; i++)
1252 {
1253 if (! suppress_messages)
1254 fprintf(stderr, "DEBUG: Gutenprint: CUPS option %d %s = %s\n",
1255 i, options[i].name, options[i].value);
1256 #ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS
1257 if (!strcmp(options[i].name, "SaveFileName"))
1258 save_file_name = options[i].value;
1259 if (!strcmp(options[i].name, "LoadFileName"))
1260 load_file_name = options[i].value;
1261 #endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */
1262 }
1263 }
1264
1265 /*
1266 * Figure out which driver to use...
1267 */
1268
1269 printer = stp_get_printer_by_driver(ppd->modelname);
1270 if (!printer)
1271 printer = stp_get_printer_by_long_name(ppd->modelname);
1272
1273 if (printer == NULL)
1274 {
1275 stp_i18n_printf(po, _("ERROR: Unable to find Gutenprint driver named "
1276 "\"%s\"!\n"), ppd->modelname);
1277 ppdClose(ppd);
1278 return (1);
1279 }
1280 if (! suppress_messages)
1281 fprintf(stderr, "DEBUG: Gutenprint: Driver %s\n", ppd->modelname);
1282
1283 /*
1284 * Open the page stream...
1285 */
1286
1287 if (argc == 7)
1288 {
1289 if ((fd = open(argv[6], O_RDONLY)) == -1)
1290 {
1291 stp_i18n_printf(po, _("ERROR: Gutenprint was unable to open raster file "
1292 "\"%s\" - %s"), argv[6], strerror(errno));
1293 sleep(1);
1294 return (1);
1295 }
1296 }
1297 else
1298 fd = 0;
1299 if (! suppress_messages)
1300 fprintf(stderr, "DEBUG: Gutenprint: Using fd %d\n", fd);
1301
1302 stp_set_printer_defaults(default_settings, printer);
1303 #ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS
1304 if (load_file_name)
1305 loaded_settings = load_options(load_file_name);
1306 #endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */
1307 stp_set_float_parameter(default_settings, "AppGamma", 1.0);
1308 set_all_options(default_settings, options, num_options, ppd);
1309
1310 cupsFreeOptions(num_options, options);
1311 ppdClose(ppd);
1312
1313 cups.ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
1314
1315 /*
1316 * Process pages as needed...
1317 */
1318
1319 cups.page = 0;
1320
1321 if (! suppress_messages)
1322 fprintf(stderr, "DEBUG: Gutenprint: About to start printing loop.\n");
1323
1324 /*
1325 * Read the first page header, which we need in order to set up
1326 * the page.
1327 */
1328 signal(SIGTERM, cancel_job);
1329 while (CUPS_READ_HEADER(cups.ras, &cups.header))
1330 {
1331 /*
1332 * We don't know how many pages we're going to print, and
1333 * we need to call stp_end_job at the completion of the job.
1334 * Therefore, we need to keep v in scope after the termination
1335 * of the loop to permit calling stp_end_job then. Therefore,
1336 * we have to free the previous page's stp_vars_t at the start
1337 * of the loop.
1338 */
1339 if (v)
1340 stp_vars_destroy(v);
1341
1342 /*
1343 * Setup printer driver variables...
1344 */
1345 if (! suppress_messages)
1346 {
1347 fprintf(stderr, "DEBUG: Gutenprint: ================ Printing page %d ================\n", cups.page + 1);
1348 fprintf(stderr, "PAGE: %d %d\n", cups.page + 1, cups.header.NumCopies);
1349 }
1350 v = initialize_page(&cups, default_settings, page_size_name);
1351 #ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS
1352 if (loaded_settings)
1353 stp_copy_vars_from(v, loaded_settings);
1354 if (save_file_name)
1355 {
1356 save_options(save_file_name, v);
1357 save_file_name = NULL;
1358 }
1359 #endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */
1360 if (! suppress_messages)
1361 {
1362 fprintf(stderr, "DEBUG: Gutenprint: Interim page settings:\n");
1363 stp_vars_print_error(v, "DEBUG");
1364 }
1365
1366 stp_merge_printvars(v, stp_printer_get_defaults(printer));
1367
1368 /* Pass along Collation settings */
1369 stp_set_boolean_parameter(v, "Collate", cups.header.Collate);
1370 stp_set_boolean_parameter_active(v, "Collate", STP_PARAMETER_ACTIVE);
1371 /* Pass along Copy settings */
1372 stp_set_int_parameter(v, "NumCopies", cups.header.NumCopies);
1373 stp_set_int_parameter_active(v, "NumCopies", STP_PARAMETER_ACTIVE);
1374 /* Pass along the page number */
1375 stp_set_int_parameter(v, "PageNumber", cups.page);
1376 cups.row = 0;
1377 if (! suppress_messages)
1378 print_debug_block(v, &cups);
1379 print_messages_as_errors = 1;
1380
1381 if (!initialized_job)
1382 {
1383 stp_start_job(v, &theImage);
1384 initialized_job = 1;
1385 }
1386
1387 if (!stp_print(v, &theImage))
1388 {
1389 if (Image_status != STP_IMAGE_STATUS_ABORT)
1390 {
1391 fprintf(stderr, "DEBUG: Gutenprint: Options failed to verify.\n");
1392 fprintf(stderr, "DEBUG: Gutenprint: Make sure that you are using ESP Ghostscript rather\n");
1393 fprintf(stderr, "DEBUG: Gutenprint: than GNU or AFPL Ghostscript with CUPS.\n");
1394 fprintf(stderr, "DEBUG: Gutenprint: If this is not the cause, set LogLevel to debug to identify the problem.\n");
1395 }
1396 aborted = 1;
1397 break;
1398 }
1399 print_messages_as_errors = 0;
1400
1401 fflush(stdout);
1402
1403 /*
1404 * Purge any remaining bitmap data...
1405 */
1406 if (cups.row < cups.header.cupsHeight)
1407 purge_excess_data(&cups);
1408 if (! suppress_messages)
1409 fprintf(stderr, "DEBUG: Gutenprint: ================ Done printing page %d ================\n", cups.page + 1);
1410 cups.page ++;
1411 }
1412 if (v)
1413 {
1414 if (! suppress_messages)
1415 fprintf(stderr, "DEBUG: Gutenprint: %s job\n",
1416 aborted ? "Aborted" : "Ending");
1417 stp_end_job(v, &theImage);
1418 fflush(stdout);
1419 stp_vars_destroy(v);
1420 }
1421 cupsRasterClose(cups.ras);
1422 (void) times(&tms);
1423 (void) gettimeofday(&t2, NULL);
1424 clocks_per_sec = sysconf(_SC_CLK_TCK);
1425 fprintf(stderr, "DEBUG: Gutenprint: stats %.0fB, %.3fu, %.3fs, %.3fel\n",
1426 total_bytes_printed,
1427 (double) tms.tms_utime / clocks_per_sec,
1428 (double) tms.tms_stime / clocks_per_sec,
1429 (double) (t2.tv_sec - t1.tv_sec) +
1430 ((double) (t2.tv_usec - t1.tv_usec)) / 1000000.0);
1431 if (!suppress_messages)
1432 {
1433 fprintf(stderr, "DEBUG: Gutenprint: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
1434 fprintf(stderr, "DEBUG: Gutenprint: ============================================================\n");
1435 }
1436 stp_vars_destroy(default_settings);
1437 if (page_size_name)
1438 stp_free(page_size_name);
1439 if (fd != 0)
1440 close(fd);
1441 return aborted ? 1 : 0;
1442 }
1443
1444
1445 /*
1446 * 'cups_writefunc()' - Write data to a file...
1447 */
1448
1449 static void
cups_writefunc(void * file,const char * buf,size_t bytes)1450 cups_writefunc(void *file, const char *buf, size_t bytes)
1451 {
1452 FILE *prn = (FILE *)file;
1453 total_bytes_printed += bytes;
1454 fwrite(buf, 1, bytes, prn);
1455 }
1456
1457 static void
cups_errfunc(void * file,const char * buf,size_t bytes)1458 cups_errfunc(void *file, const char *buf, size_t bytes)
1459 {
1460 size_t next_nl = 0;
1461 size_t where = 0;
1462 FILE *prn = (FILE *)file;
1463 while (where < bytes)
1464 {
1465 if (bytes - where > 6 && strncmp(buf, "ERROR:", 6) == 0)
1466 {
1467 fputs("ERROR: Gutenprint:", prn);
1468 buf += 6;
1469 }
1470 else if (print_messages_as_errors)
1471 fputs("ERROR: Gutenprint: ", prn);
1472 else if (strncmp(buf, "DEBUG", 5) != 0)
1473 fputs("DEBUG: Gutenprint: ", prn);
1474 while (next_nl < bytes)
1475 {
1476 if (buf[next_nl++] == '\n')
1477 break;
1478 }
1479 fwrite(buf + where, 1, next_nl - where, prn);
1480 where = next_nl;
1481 }
1482 }
1483
1484 static void
cups_dbgfunc(void * file,const char * buf,size_t bytes)1485 cups_dbgfunc(void *file, const char *buf, size_t bytes)
1486 {
1487 size_t next_nl = 0;
1488 size_t where = 0;
1489 FILE *prn = (FILE *)file;
1490 while (where < bytes)
1491 {
1492 if (bytes - where > 6 && strncmp(buf, "ERROR:", 6) == 0)
1493 {
1494 fputs("ERROR: Gutenprint:", prn);
1495 buf += 6;
1496 }
1497 else if (strncmp(buf, "DEBUG", 5) != 0)
1498 fputs("DEBUG: Gutenprint: ", prn);
1499 while (next_nl < bytes)
1500 {
1501 if (buf[next_nl++] == '\n')
1502 break;
1503 }
1504 fwrite(buf + where, 1, next_nl - where, prn);
1505 where = next_nl;
1506 }
1507 }
1508
1509
1510 /*
1511 * 'cancel_job()' - Cancel the current job...
1512 */
1513
1514 static void
cancel_job(int sig)1515 cancel_job(int sig) /* I - Signal */
1516 {
1517 (void)sig;
1518 Image_status = STP_IMAGE_STATUS_ABORT;
1519 }
1520
1521 /*
1522 * 'Image_get_appname()' - Get the application we are running.
1523 */
1524
1525 static const char * /* O - Application name */
Image_get_appname(stp_image_t * image)1526 Image_get_appname(stp_image_t *image) /* I - Image */
1527 {
1528 (void)image;
1529
1530 return ("CUPS driver based on Gutenprint");
1531 }
1532
1533
1534 /*
1535 * 'Image_get_row()' - Get one row of the image.
1536 */
1537
1538 static void
throwaway_data(int amount,cups_image_t * cups)1539 throwaway_data(int amount, cups_image_t *cups)
1540 {
1541 unsigned char trash[4096]; /* Throwaway */
1542 int block_count = amount / 4096;
1543 int leftover = amount % 4096;
1544 while (block_count > 0)
1545 {
1546 cupsRasterReadPixels(cups->ras, trash, 4096);
1547 block_count--;
1548 }
1549 if (leftover)
1550 cupsRasterReadPixels(cups->ras, trash, leftover);
1551 }
1552
1553 static stp_image_status_t
Image_get_row(stp_image_t * image,unsigned char * data,size_t byte_limit,int row)1554 Image_get_row(stp_image_t *image, /* I - Image */
1555 unsigned char *data, /* O - Row */
1556 size_t byte_limit, /* I - how many bytes in data */
1557 int row) /* I - Row number (unused) */
1558 {
1559 cups_image_t *cups; /* CUPS image */
1560 int i; /* Looping var */
1561 int bytes_per_line;
1562 int margin;
1563 stp_image_status_t tmp_image_status = Image_status;
1564 unsigned char *orig = data; /* Temporary pointer */
1565 static int warned = 0; /* Error warning printed? */
1566 int new_percent;
1567 int left_margin, right_margin;
1568
1569 if ((cups = (cups_image_t *)(image->rep)) == NULL)
1570 {
1571 stp_i18n_printf(po, _("ERROR: Gutenprint image is not initialized! "
1572 "Please report this bug to "
1573 "gimp-print-devel@lists.sourceforge.net\n"));
1574 return STP_IMAGE_STATUS_ABORT;
1575 }
1576 bytes_per_line =
1577 ((cups->adjusted_width * cups->header.cupsBitsPerPixel) + CHAR_BIT - 1) /
1578 CHAR_BIT;
1579
1580 left_margin = ((cups->left_trim * cups->header.cupsBitsPerPixel) + CHAR_BIT - 1) /
1581 CHAR_BIT;
1582 right_margin = ((cups->right_trim * cups->header.cupsBitsPerPixel) + CHAR_BIT - 1) /
1583 CHAR_BIT;
1584 margin = cups->header.cupsBytesPerLine - left_margin - bytes_per_line -
1585 right_margin;
1586
1587 if (cups->row < cups->header.cupsHeight)
1588 {
1589 if (! suppress_messages && ! suppress_verbose_messages)
1590 fprintf(stderr, "DEBUG2: Gutenprint: Reading %d %d\n",
1591 bytes_per_line, cups->row);
1592 while (cups->row <= row && cups->row < cups->header.cupsHeight)
1593 {
1594 if (left_margin > 0)
1595 {
1596 if (! suppress_messages && ! suppress_verbose_messages)
1597 fprintf(stderr, "DEBUG2: Gutenprint: Tossing left %d (%d)\n",
1598 left_margin, cups->left_trim);
1599 throwaway_data(left_margin, cups);
1600 }
1601 cupsRasterReadPixels(cups->ras, data, bytes_per_line);
1602 cups->row ++;
1603 if (margin + right_margin > 0)
1604 {
1605 if (! suppress_messages && ! suppress_verbose_messages)
1606 fprintf(stderr, "DEBUG2: Gutenprint: Tossing right %d (%d) + %d\n",
1607 right_margin, cups->right_trim, margin);
1608 throwaway_data(margin + right_margin, cups);
1609 }
1610 }
1611 }
1612 else
1613 {
1614 switch (cups->header.cupsColorSpace)
1615 {
1616 case CUPS_CSPACE_K:
1617 case CUPS_CSPACE_CMYK:
1618 case CUPS_CSPACE_KCMY:
1619 case CUPS_CSPACE_CMY:
1620 memset(data, 0, bytes_per_line);
1621 break;
1622 case CUPS_CSPACE_RGB:
1623 case CUPS_CSPACE_W:
1624 memset(data, ((1 << CHAR_BIT) - 1), bytes_per_line);
1625 break;
1626 default:
1627 stp_i18n_printf(po, _("ERROR: Gutenprint detected a bad colorspace "
1628 "(%d)!\n"), cups->header.cupsColorSpace);
1629 return STP_IMAGE_STATUS_ABORT;
1630 }
1631 }
1632
1633 /*
1634 * This exists to print non-ADSC input which has messed up the job
1635 * input, such as that generated by psnup. The output is barely
1636 * legible, but it's better than the garbage output otherwise.
1637 */
1638 data = orig;
1639 if (cups->header.cupsBitsPerPixel == 1)
1640 {
1641 if (warned == 0)
1642 {
1643 fputs(_("WARNING: Gutenprint detected a bad color depth (1). "
1644 "Output quality is degraded. Are you using psnup or "
1645 "non-ADSC PostScript?\n"), stderr);
1646 warned = 1;
1647 }
1648 for (i = cups->adjusted_width - 1; i >= 0; i--)
1649 {
1650 if ( (data[i/8] >> (7 - i%8)) &0x1)
1651 data[i]=255;
1652 else
1653 data[i]=0;
1654 }
1655 }
1656
1657 new_percent = (int) (100.0 * cups->row / cups->header.cupsHeight);
1658 if (new_percent > cups->last_percent)
1659 {
1660 if (! suppress_verbose_messages)
1661 {
1662 stp_i18n_printf(po, _("INFO: Printing page %d, %d%%\n"),
1663 cups->page + 1, new_percent);
1664 fprintf(stderr, "ATTR: job-media-progress=%d\n", new_percent);
1665 }
1666 cups->last_percent = new_percent;
1667 }
1668
1669 if (tmp_image_status != STP_IMAGE_STATUS_OK)
1670 {
1671 if (! suppress_messages)
1672 fprintf(stderr, "DEBUG: Gutenprint: Image status %d\n", tmp_image_status);
1673 }
1674 return tmp_image_status;
1675 }
1676
1677
1678 /*
1679 * 'Image_height()' - Return the height of an image.
1680 */
1681
1682 static int /* O - Height in pixels */
Image_height(stp_image_t * image)1683 Image_height(stp_image_t *image) /* I - Image */
1684 {
1685 cups_image_t *cups; /* CUPS image */
1686
1687
1688 if ((cups = (cups_image_t *)(image->rep)) == NULL)
1689 return (0);
1690
1691 if (! suppress_messages)
1692 fprintf(stderr, "DEBUG: Gutenprint: Image_height %d\n", cups->adjusted_height);
1693 return (cups->adjusted_height);
1694 }
1695
1696
1697 /*
1698 * 'Image_init()' - Initialize an image.
1699 */
1700
1701 static void
Image_init(stp_image_t * image)1702 Image_init(stp_image_t *image) /* I - Image */
1703 {
1704 cups_image_t *cups; /* CUPS image */
1705
1706 if ((cups = (cups_image_t *)(image->rep)) == NULL)
1707 return;
1708 cups->last_percent = 0;
1709
1710 if (! suppress_messages)
1711 stp_i18n_printf(po, _("INFO: Starting page %d...\n"), cups->page + 1);
1712 /* cups->page + 1 because users expect 1-based counting */
1713 }
1714
1715 /*
1716 * 'Image_progress_conclude()' - Close the progress display.
1717 */
1718
1719 static void
Image_conclude(stp_image_t * image)1720 Image_conclude(stp_image_t *image) /* I - Image */
1721 {
1722 cups_image_t *cups; /* CUPS image */
1723
1724
1725 if ((cups = (cups_image_t *)(image->rep)) == NULL)
1726 return;
1727
1728 if (! suppress_messages)
1729 stp_i18n_printf(po, _("INFO: Finished page %d...\n"), cups->page + 1);
1730 }
1731
1732 /*
1733 * 'Image_width()' - Return the width of an image.
1734 */
1735
1736 static int /* O - Width in pixels */
Image_width(stp_image_t * image)1737 Image_width(stp_image_t *image) /* I - Image */
1738 {
1739 cups_image_t *cups; /* CUPS image */
1740
1741
1742 if ((cups = (cups_image_t *)(image->rep)) == NULL)
1743 return (0);
1744
1745 if (! suppress_messages)
1746 fprintf(stderr, "DEBUG: Gutenprint: Image_width %d\n", cups->adjusted_width);
1747 return (cups->adjusted_width);
1748 }
1749