/* * Gutenprint based raster filter for the Common UNIX Printing System. * * Copyright 1993-2008 by Mike Sweet. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Contents: * * main() - Main entry and processing of driver. * cups_writefunc() - Write data to a file... * cancel_job() - Cancel the current job... * Image_get_appname() - Get the application we are running. * Image_get_row() - Get one row of the image. * Image_height() - Return the height of an image. * Image_init() - Initialize an image. * Image_conclude() - Close the progress display. * Image_width() - Return the width of an image. */ /* * Include necessary headers... */ #if 0 #define ENABLE_CUPS_LOAD_SAVE_OPTIONS #endif #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIMITS_H #include #endif #include "i18n.h" #include /* Solaris with gcc has problems because gcc's limits.h doesn't #define */ /* this */ #ifndef CHAR_BIT #define CHAR_BIT 8 #endif /* * Structure for page raster data... */ #if (CUPS_VERSION_MAJOR > 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1)) #define CUPS_HEADER_T cups_page_header2_t #define CUPS_READ_HEADER cupsRasterReadHeader2 #else #define CUPS_HEADER_T cups_page_header_t #define CUPS_READ_HEADER cupsRasterReadHeader #endif typedef struct { cups_raster_t *ras; /* Raster stream to read from */ int page; /* Current page number */ int row; /* Current row number */ int left; int right; int bottom; int top; int width; int height; int left_trim; int right_trim; int top_trim; int bottom_trim; int adjusted_width; int adjusted_height; stp_dimension_t d_left; stp_dimension_t d_right; stp_dimension_t d_bottom; stp_dimension_t d_top; stp_dimension_t d_width; stp_dimension_t d_height; stp_dimension_t d_left_trim; stp_dimension_t d_right_trim; stp_dimension_t d_bottom_trim; stp_dimension_t d_top_trim; int last_percent; int shrink_to_fit; CUPS_HEADER_T header; /* Page header from file */ } cups_image_t; static void cups_writefunc(void *file, const char *buf, size_t bytes); static void cups_errfunc(void *file, const char *buf, size_t bytes); static void cups_dbgfunc(void *file, const char *buf, size_t bytes); static void cancel_job(int sig); static const char *Image_get_appname(stp_image_t *image); static stp_image_status_t Image_get_row(stp_image_t *image, unsigned char *data, size_t byte_limit, int row); static int Image_height(stp_image_t *image); static int Image_width(stp_image_t *image); static void Image_conclude(stp_image_t *image); static void Image_init(stp_image_t *image); static stp_image_t theImage = { Image_init, NULL, /* reset */ Image_width, Image_height, Image_get_row, Image_get_appname, Image_conclude, NULL }; static volatile stp_image_status_t Image_status = STP_IMAGE_STATUS_OK; static double total_bytes_printed = 0; static int print_messages_as_errors = 0; static int suppress_messages = 0; static int suppress_verbose_messages = 0; static const stp_string_list_t *po = NULL; #ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS static const char *save_file_name = NULL; static const char *load_file_name = NULL; #endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ #pragma GCC diagnostic ignored "-Wformat-nonliteral" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-qual" static inline void * cast_safe(const void *ptr) { return (void *)ptr; } #pragma GCC diagnostic pop static void set_string_parameter(stp_vars_t *v, const char *name, const char *val) { if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Set special string %s to %s\n", name, val); stp_set_string_parameter(v, name, val); } static void set_special_parameter(stp_vars_t *v, const char *name, int choice) { stp_parameter_t desc; stp_describe_parameter(v, name, &desc); if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST) { #if 0 if (stp_check_string_parameter(v, name, STP_PARAMETER_ACTIVE)) { if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Not overriding special parameter %s (%s)\n", name, stp_get_string_parameter(v, name)); } else #endif if (choice < 0) { stp_clear_string_parameter(v, name); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Clear special parameter %s\n", name); } else if (choice >= stp_string_list_count(desc.bounds.str)) { if (! suppress_messages) stp_i18n_printf(po, _("ERROR: Unable to set Gutenprint option %s " "(%d > %d)!\n"), name, choice, stp_string_list_count(desc.bounds.str)); } else { stp_set_string_parameter (v, name, stp_string_list_param(desc.bounds.str, choice)->name); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Set special parameter %s to choice %d (%s)\n", name, choice, stp_string_list_param(desc.bounds.str, choice)->name); } } else { if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Unable to set special %s: not a string\n", name); } stp_parameter_description_destroy(&desc); } static void print_debug_block(const stp_vars_t *v, const cups_image_t *cups) { fprintf(stderr, "DEBUG: Gutenprint: Page data:\n"); fprintf(stderr, "DEBUG: Gutenprint: MediaClass = \"%s\"\n", cups->header.MediaClass); fprintf(stderr, "DEBUG: Gutenprint: MediaColor = \"%s\"\n", cups->header.MediaColor); fprintf(stderr, "DEBUG: Gutenprint: MediaType = \"%s\"\n", cups->header.MediaType); fprintf(stderr, "DEBUG: Gutenprint: OutputType = \"%s\"\n", cups->header.OutputType); fprintf(stderr, "DEBUG: Gutenprint: AdvanceDistance = %d\n", cups->header.AdvanceDistance); fprintf(stderr, "DEBUG: Gutenprint: AdvanceMedia = %d\n", cups->header.AdvanceMedia); fprintf(stderr, "DEBUG: Gutenprint: Collate = %d\n", cups->header.Collate); fprintf(stderr, "DEBUG: Gutenprint: CutMedia = %d\n", cups->header.CutMedia); fprintf(stderr, "DEBUG: Gutenprint: Duplex = %d\n", cups->header.Duplex); fprintf(stderr, "DEBUG: Gutenprint: HWResolution = [ %d %d ]\n", cups->header.HWResolution[0], cups->header.HWResolution[1]); fprintf(stderr, "DEBUG: Gutenprint: ImagingBoundingBox = [ %d %d %d %d ]\n", cups->header.ImagingBoundingBox[0], cups->header.ImagingBoundingBox[1], cups->header.ImagingBoundingBox[2], cups->header.ImagingBoundingBox[3]); fprintf(stderr, "DEBUG: Gutenprint: InsertSheet = %d\n", cups->header.InsertSheet); fprintf(stderr, "DEBUG: Gutenprint: Jog = %d\n", cups->header.Jog); fprintf(stderr, "DEBUG: Gutenprint: LeadingEdge = %d\n", cups->header.LeadingEdge); fprintf(stderr, "DEBUG: Gutenprint: Margins = [ %d %d ]\n", cups->header.Margins[0], cups->header.Margins[1]); fprintf(stderr, "DEBUG: Gutenprint: ManualFeed = %d\n", cups->header.ManualFeed); fprintf(stderr, "DEBUG: Gutenprint: MediaPosition = %d\n", cups->header.MediaPosition); fprintf(stderr, "DEBUG: Gutenprint: MediaWeight = %d\n", cups->header.MediaWeight); fprintf(stderr, "DEBUG: Gutenprint: MirrorPrint = %d\n", cups->header.MirrorPrint); fprintf(stderr, "DEBUG: Gutenprint: NegativePrint = %d\n", cups->header.NegativePrint); fprintf(stderr, "DEBUG: Gutenprint: NumCopies = %d\n", cups->header.NumCopies); fprintf(stderr, "DEBUG: Gutenprint: Orientation = %d\n", cups->header.Orientation); fprintf(stderr, "DEBUG: Gutenprint: OutputFaceUp = %d\n", cups->header.OutputFaceUp); fprintf(stderr, "DEBUG: Gutenprint: PageSize = [ %d %d ]\n", cups->header.PageSize[0], cups->header.PageSize[1]); fprintf(stderr, "DEBUG: Gutenprint: Separations = %d\n", cups->header.Separations); fprintf(stderr, "DEBUG: Gutenprint: TraySwitch = %d\n", cups->header.TraySwitch); fprintf(stderr, "DEBUG: Gutenprint: Tumble = %d\n", cups->header.Tumble); fprintf(stderr, "DEBUG: Gutenprint: cupsWidth = %d\n", cups->header.cupsWidth); fprintf(stderr, "DEBUG: Gutenprint: cupsHeight = %d\n", cups->header.cupsHeight); fprintf(stderr, "DEBUG: Gutenprint: cups->width = %d\n", cups->width); fprintf(stderr, "DEBUG: Gutenprint: cups->height = %d\n", cups->height); fprintf(stderr, "DEBUG: Gutenprint: cups->adjusted_width = %d\n", cups->adjusted_width); fprintf(stderr, "DEBUG: Gutenprint: cups->adjusted_height = %d\n", cups->adjusted_height); fprintf(stderr, "DEBUG: Gutenprint: cupsMediaType = %d\n", cups->header.cupsMediaType); fprintf(stderr, "DEBUG: Gutenprint: cupsBitsPerColor = %d\n", cups->header.cupsBitsPerColor); fprintf(stderr, "DEBUG: Gutenprint: cupsBitsPerPixel = %d\n", cups->header.cupsBitsPerPixel); fprintf(stderr, "DEBUG: Gutenprint: cupsBytesPerLine = %d\n", cups->header.cupsBytesPerLine); fprintf(stderr, "DEBUG: Gutenprint: cupsColorOrder = %d\n", cups->header.cupsColorOrder); fprintf(stderr, "DEBUG: Gutenprint: cupsColorSpace = %d\n", cups->header.cupsColorSpace); fprintf(stderr, "DEBUG: Gutenprint: cupsCompression = %d\n", cups->header.cupsCompression); fprintf(stderr, "DEBUG: Gutenprint: cupsRowCount = %d\n", cups->header.cupsRowCount); fprintf(stderr, "DEBUG: Gutenprint: cupsRowFeed = %d\n", cups->header.cupsRowFeed); fprintf(stderr, "DEBUG: Gutenprint: cupsRowStep = %d\n", cups->header.cupsRowStep); fprintf(stderr, "DEBUG: Gutenprint: shrink page to fit %d\n", cups->shrink_to_fit); stp_vars_print_error(v, "DEBUG"); fprintf(stderr, "DEBUG: Gutenprint: End page data\n"); } static int printer_supports_bw(const stp_vars_t *v) { stp_parameter_t desc; int status = 0; stp_describe_parameter(v, "PrintingMode", &desc); if (stp_string_list_is_present(desc.bounds.str, "BW")) status = 1; stp_parameter_description_destroy(&desc); return status; } static void validate_options(stp_vars_t *v, cups_image_t *cups) { stp_parameter_list_t params = stp_get_parameter_list(v); int nparams = stp_parameter_list_count(params); int i; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Validating options\n"); for (i = 0; i < nparams; i++) { const stp_parameter_t *param = stp_parameter_list_param(params, i); stp_parameter_t desc; stp_describe_parameter(v, param->name, &desc); if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST) { if (!stp_string_list_is_present (desc.bounds.str, stp_get_string_parameter(v, desc.name))) { if (! suppress_messages) { const char *val = stp_get_string_parameter(v, desc.name); fprintf(stderr, "DEBUG: Gutenprint: Clearing string %s (%s)\n", desc.name, val ? val : "(null)"); } stp_clear_string_parameter(v, desc.name); if (!desc.read_only && desc.is_mandatory && desc.is_active) { if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Setting default string %s to %s\n", desc.name, desc.deflt.str ? desc.deflt.str : "(null)"); stp_set_string_parameter(v, desc.name, desc.deflt.str); if (strcmp(desc.name, "PageSize") == 0) { const stp_papersize_t *ps = stp_describe_papersize(v, desc.deflt.str); if (ps->width > 0) { if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Setting page width to %.3f\n", ps->width); if (ps->width < stp_get_page_width(v)) stp_set_page_width(v, ps->width); } if (ps->height > 0) { if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Setting page height to %.3f\n", ps->height); if (ps->height < stp_get_page_height(v)) stp_set_page_height(v, ps->height); } } } } } stp_parameter_description_destroy(&desc); } if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Done validating options\n"); stp_parameter_list_destroy(params); } static stp_vars_t * initialize_page(cups_image_t *cups, const stp_vars_t *default_settings, const char *page_size_name) { stp_dimension_t tmp_left, tmp_right, tmp_top, tmp_bottom; stp_vars_t *v = stp_vars_create_copy(default_settings); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Initialize page\n"); if (cups->header.cupsBitsPerColor == 16) set_string_parameter(v, "ChannelBitDepth", "16"); else set_string_parameter(v, "ChannelBitDepth", "8"); switch (cups->header.cupsColorSpace) { case CUPS_CSPACE_W : /* DyeSub photo printers don't support black & white ink! */ if (printer_supports_bw(v)) set_string_parameter(v, "PrintingMode", "BW"); set_string_parameter(v, "InputImageType", "Whitescale"); break; case CUPS_CSPACE_K : /* DyeSub photo printers don't support black & white ink! */ if (printer_supports_bw(v)) set_string_parameter(v, "PrintingMode", "BW"); set_string_parameter(v, "InputImageType", "Grayscale"); break; case CUPS_CSPACE_RGB : set_string_parameter(v, "PrintingMode", "Color"); set_string_parameter(v, "InputImageType", "RGB"); break; case CUPS_CSPACE_CMY : set_string_parameter(v, "PrintingMode", "Color"); set_string_parameter(v, "InputImageType", "CMY"); break; case CUPS_CSPACE_CMYK : set_string_parameter(v, "PrintingMode", "Color"); set_string_parameter(v, "InputImageType", "CMYK"); break; case CUPS_CSPACE_KCMY : set_string_parameter(v, "PrintingMode", "Color"); set_string_parameter(v, "InputImageType", "KCMY"); break; default : stp_i18n_printf(po, _("ERROR: Gutenprint detected a bad colorspace " "(%d)!\n"), cups->header.cupsColorSpace); break; } set_special_parameter(v, "Resolution", cups->header.cupsCompression - 1); set_special_parameter(v, "Quality", cups->header.cupsRowFeed - 1); if (strlen(cups->header.MediaClass) > 0) set_string_parameter(v, "InputSlot", cups->header.MediaClass); if (strlen(cups->header.MediaType) > 0) set_string_parameter(v, "MediaType", cups->header.MediaType); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: PageSize = %dx%d\n", cups->header.PageSize[0], cups->header.PageSize[1]); if (page_size_name) { if (strcmp(page_size_name, "Custom") == 0) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Using custom page size for (%d, %d)\n", cups->header.PageSize[1], cups->header.PageSize[0]); stp_set_page_width(v, cups->header.PageSize[0]); stp_set_page_height(v, cups->header.PageSize[1]); } else if (stp_describe_papersize(v, page_size_name)) { stp_dimension_t width, height; if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Using page size %s with (%d, %d)\n", page_size_name, cups->header.PageSize[1], cups->header.PageSize[0]); set_string_parameter(v, "PageSize", page_size_name); stp_get_media_size(v, &width, &height); if (width > 0) stp_set_page_width(v, width); else stp_set_page_width(v, cups->header.PageSize[0]); if (height > 0) stp_set_page_height(v, height); else stp_set_page_height(v, cups->header.PageSize[1]); } else { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Can't find page size %s with (%d, %d), using custom page size\n", page_size_name, cups->header.PageSize[1], cups->header.PageSize[0]); stp_set_page_width(v, cups->header.PageSize[0]); stp_set_page_height(v, cups->header.PageSize[1]); } } else { if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: No named media size for (%d, %d)\n", cups->header.PageSize[1], cups->header.PageSize[0]); stp_set_page_width(v, cups->header.PageSize[0]); stp_set_page_height(v, cups->header.PageSize[1]); } /* * Duplex * Note that the names MUST match those in the printer driver(s) */ if (cups->header.Duplex != 0) { if (cups->header.Tumble != 0) set_string_parameter(v, "Duplex", "DuplexTumble"); else set_string_parameter(v, "Duplex", "DuplexNoTumble"); } cups->shrink_to_fit = (stp_check_int_parameter(v, "CUPSShrinkPage", STP_PARAMETER_ACTIVE) ? stp_get_int_parameter(v, "CUPSShrinkPage") : 0); set_string_parameter(v, "JobMode", "Job"); validate_options(v, cups); stp_get_media_size(v, &(cups->d_width), &(cups->d_height)); stp_get_maximum_imageable_area(v, &tmp_left, &tmp_right, &tmp_bottom, &tmp_top); stp_get_imageable_area(v, &(cups->d_left), &(cups->d_right), &(cups->d_bottom), &(cups->d_top)); if (! suppress_messages) { fprintf(stderr, "DEBUG: Gutenprint: limits w %.3f l %.3f r %.3f h %.3f t %.3f b %.3f\n", cups->d_width, cups->d_left, cups->d_right, cups->d_height, cups->d_top, cups->d_bottom); fprintf(stderr, "DEBUG: Gutenprint: max limits l %.3f r %.3f t %.3f b %.3f\n", tmp_left, tmp_right, tmp_top, tmp_bottom); } if (tmp_left < 0) tmp_left = 0; if (tmp_top < 0) tmp_top = 0; if (tmp_right > tmp_left + cups->d_width) tmp_right = cups->d_width; if (tmp_bottom > tmp_top + cups->d_height) tmp_bottom = cups->d_height; if (tmp_left < cups->d_left) { if (cups->shrink_to_fit != 1) { cups->d_left_trim = cups->d_left - tmp_left; tmp_left = cups->d_left; } else cups->d_left_trim = 0; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: left margin %.3f\n", cups->d_left_trim); } else { cups->d_left_trim = 0; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Adjusting left margin from %.3f to %.3f\n", cups->d_left, tmp_left); cups->d_left = tmp_left; } if (tmp_right > cups->d_right) { if (cups->shrink_to_fit != 1) { cups->d_right_trim = tmp_right - cups->d_right; tmp_right = cups->d_right; } else cups->d_right_trim = 0; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: right margin %.3f\n", cups->d_right_trim); } else { cups->d_right_trim = 0; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Adjusting right margin from %.3f to %.3f\n", cups->d_right, tmp_right); cups->d_right = tmp_right; } if (tmp_top < cups->d_top) { if (cups->shrink_to_fit != 1) { cups->d_top_trim = cups->d_top - tmp_top; tmp_top = cups->d_top; } else cups->d_top_trim = 0; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: top margin %.3f\n", cups->d_top_trim); } else { cups->d_top_trim = 0; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Adjusting top margin from %.3f to %.3f\n", cups->d_top, tmp_top); cups->d_top = tmp_top; } if (tmp_bottom > cups->d_bottom) { if (cups->shrink_to_fit != 1) { cups->d_bottom_trim = tmp_bottom - cups->d_bottom; tmp_bottom = cups->d_bottom; } else cups->d_bottom_trim = 0; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: bottom margin %.3f\n", cups->d_bottom_trim); } else { cups->d_bottom_trim = 0; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Adjusting bottom margin from %.3f to %.3f\n", cups->d_bottom, tmp_bottom); cups->d_bottom = tmp_bottom; } if (cups->shrink_to_fit == 2) { stp_dimension_t t_left, t_right, t_bottom, t_top; stp_get_imageable_area(v, &(t_left), &(t_right), &(t_bottom), &(t_top)); stp_set_width(v, t_right - t_left); stp_set_height(v, t_bottom - t_top); stp_set_left(v, t_left); stp_set_top(v, t_top); } else { stp_set_width(v, cups->d_right - cups->d_left); stp_set_height(v, cups->d_bottom - cups->d_top); stp_set_left(v, cups->d_left); stp_set_top(v, cups->d_top); } cups->d_right = cups->d_width - cups->d_right; if (cups->shrink_to_fit == 1) cups->d_width = tmp_right - tmp_left; else cups->d_width = cups->d_width - cups->d_left - cups->d_right; cups->width = cups->header.HWResolution[0] * cups->d_width / 72; cups->left = cups->header.HWResolution[0] * cups->d_left / 72; cups->right = cups->header.HWResolution[0] * cups->d_right / 72; cups->left_trim = cups->header.HWResolution[0] * cups->d_left_trim / 72; cups->right_trim = cups->header.HWResolution[0] * cups->d_right_trim / 72; cups->adjusted_width = cups->width; if (cups->adjusted_width > cups->header.cupsWidth) cups->adjusted_width = cups->header.cupsWidth; cups->d_bottom = cups->d_height - cups->d_bottom; if (cups->shrink_to_fit == 1) cups->d_height = tmp_bottom - tmp_top; else cups->d_height = cups->d_height - cups->d_top - cups->d_bottom; cups->height = cups->header.HWResolution[1] * cups->d_height / 72; cups->top = cups->header.HWResolution[1] * cups->d_top / 72; cups->bottom = cups->header.HWResolution[1] * cups->d_bottom / 72; cups->top_trim = cups->header.HWResolution[1] * cups->d_top_trim / 72; cups->bottom_trim = cups->header.HWResolution[1] * cups->d_bottom_trim / 72; cups->adjusted_height = cups->height; if (cups->adjusted_height > cups->header.cupsHeight) cups->adjusted_height = cups->header.cupsHeight; if (! suppress_messages) { fprintf(stderr, "DEBUG: Gutenprint: CUPS settings w %d l %d r %d h %d t %d b %d\n", cups->width, cups->left, cups->right, cups->height, cups->top, cups->bottom); fprintf(stderr, "DEBUG: Gutenprint: adjusted w %d h %d\n", cups->adjusted_width, cups->adjusted_height); } if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: End initialize page\n"); return v; } static void purge_excess_data(cups_image_t *cups) { char *buffer = stp_malloc(cups->header.cupsBytesPerLine); if (buffer) { if (! suppress_messages && ! suppress_verbose_messages ) fprintf(stderr, "DEBUG2: Gutenprint: Purging %d row%s\n", cups->header.cupsHeight - cups->row, ((cups->header.cupsHeight - cups->row) == 1 ? "" : "s")); while (cups->row < cups->header.cupsHeight) { cupsRasterReadPixels(cups->ras, (unsigned char *)buffer, cups->header.cupsBytesPerLine); cups->row ++; } } stp_free(buffer); } static void set_all_options(stp_vars_t *v, cups_option_t *options, int num_options, ppd_file_t *ppd) { stp_parameter_list_t params = stp_get_parameter_list(v); int nparams = stp_parameter_list_count(params); int i; const char *val; /* CUPS option value */ ppd_option_t *ppd_option; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Set options:\n"); val = cupsGetOption("StpiShrinkOutput", num_options, options); if (!val) { ppd_option = ppdFindOption(ppd, "StpiShrinkOutput"); if (ppd_option) val = ppd_option->defchoice; } if (val) { if (!strcasecmp(val, "crop")) stp_set_int_parameter(v, "CUPSShrinkPage", 0); else if (!strcasecmp(val, "expand")) stp_set_int_parameter(v, "CUPSShrinkPage", 2); else stp_set_int_parameter(v, "CUPSShrinkPage", 1); } else stp_set_int_parameter(v, "CUPSShrinkPage", 1); for (i = 0; i < nparams; i++) { const stp_parameter_t *param = stp_parameter_list_param(params, i); stp_parameter_t desc; char *ppd_option_name = stp_malloc(strlen(param->name) + 8); /* StpFineFOO\0 */ stp_describe_parameter(v, param->name, &desc); if (desc.p_type == STP_PARAMETER_TYPE_DOUBLE) { sprintf(ppd_option_name, "Stp%s", desc.name); val = cupsGetOption(ppd_option_name, num_options, options); if (!val) { ppd_option = ppdFindOption(ppd, ppd_option_name); if (ppd_option) val = ppd_option->defchoice; } if (val && !strncasecmp(val, "Custom.", 7)) { double dval = atof(val + 7); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Set float %s to %f\n", desc.name, dval); if (dval > desc.bounds.dbl.upper) dval = desc.bounds.dbl.upper; stp_set_float_parameter(v, desc.name, dval); } else if (val && strlen(val) > 0 && strcmp(val, "None") != 0) { double fine_val = 0; if (strchr(val, (int) '.')) { fine_val = atof(val); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Set float %s to %f (%s)\n", desc.name, fine_val, val); } else { double coarse_val = atof(val) * 0.001; sprintf(ppd_option_name, "StpFine%s", desc.name); val = cupsGetOption(ppd_option_name, num_options, options); if (!val) { ppd_option = ppdFindOption(ppd, ppd_option_name); if (ppd_option) val = ppd_option->defchoice; } if (val && strlen(val) > 0 && strcmp(val, "None") != 0) fine_val = atof(val) * 0.001; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Set float %s to %f + %f\n", desc.name, coarse_val, fine_val); fine_val += coarse_val; } if (fine_val > desc.bounds.dbl.upper) fine_val = desc.bounds.dbl.upper; if (fine_val < desc.bounds.dbl.lower) fine_val = desc.bounds.dbl.lower; stp_set_float_parameter(v, desc.name, fine_val); } } else { sprintf(ppd_option_name, "Stp%s", desc.name); val = cupsGetOption(ppd_option_name, num_options, options); if (!val) { ppd_option = ppdFindOption(ppd, ppd_option_name); if (ppd_option) val = ppd_option->defchoice; } if (val && ((strlen(val) > 0 && strcmp(val, "None") != 0) || (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST))) { stp_curve_t *curve; stp_raw_t *raw; switch (desc.p_type) { case STP_PARAMETER_TYPE_STRING_LIST: if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Set string %s to %s\n", desc.name, val); set_string_parameter(v, desc.name, val); break; case STP_PARAMETER_TYPE_INT: if (!strncasecmp(val, "Custom.", 7)) val += 7; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Set int %s to %s (%d)\n", desc.name, val, atoi(val)); stp_set_int_parameter(v, desc.name, atoi(val)); break; case STP_PARAMETER_TYPE_DIMENSION: if (!strncasecmp(val, "Custom.", 7)) val += 7; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Set dimension %s to %s (%d)\n", desc.name, val, atoi(val)); stp_set_dimension_parameter(v, desc.name, atoi(val)); break; case STP_PARAMETER_TYPE_BOOLEAN: if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Set bool %s to %s (%d)\n", desc.name, val, strcasecmp(val, "true") == 0 ? 1 : 0); stp_set_boolean_parameter (v, desc.name, strcasecmp(val, "true") == 0 ? 1 : 0); break; case STP_PARAMETER_TYPE_CURVE: curve = stp_curve_create_from_string(val); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Set curve %s to %s\n", desc.name, curve ? val : "(NULL)"); if (curve) { stp_set_curve_parameter(v, desc.name, curve); stp_curve_destroy(curve); } break; case STP_PARAMETER_TYPE_RAW: /* figure this out later, too */ raw = stp_xmlstrtoraw(val); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Set raw %s to %s\n", desc.name, raw ? val : "(NULL)"); if (raw) { stp_set_raw_parameter(v, desc.name, raw->data, raw->bytes); stp_free(cast_safe(raw->data)); stp_free(raw); } break; case STP_PARAMETER_TYPE_FILE: /* Probably not, security hole */ if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Ignoring option %s %s type %d\n", desc.name, val, desc.p_type); break; default: break; } } else if (val) { if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Not setting %s to '%s'\n", desc.name, val); } else { if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Not setting %s to (null)\n", desc.name); } } stp_parameter_description_destroy(&desc); stp_free(ppd_option_name); } if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: End options\n"); stp_parameter_list_destroy(params); } #ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS static void save_options(const char *save_name, const stp_vars_t *v) { FILE *f_options; int i; stp_vars_t *c = stp_vars_create(); stp_parameter_list_t params = stp_get_parameter_list(v); stp_parameter_t desc; stp_mxml_node_t *mxml = NULL; int param_count; if (!params) { stp_vars_destroy(c); return; } f_options = fopen(save_name, "w"); if (!f_options) { stp_parameter_list_destroy(params); stp_vars_destroy(c); return; } param_count = stp_parameter_list_count(params); stp_set_driver(c, stp_get_driver(v)); if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Saving parameters to %s\n", save_name); for (i = 0; i < param_count; i++) { const stp_parameter_t *lparam = stp_parameter_list_param(params, i); stp_describe_parameter(v, lparam->name, &desc); if (desc.read_only || !strcmp(desc.name, "ChannelBitDepth") || !stp_parameter_has_category_value(v, &desc, "Color", "Yes")) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: skipping non-color %s\n", desc.name); stp_parameter_description_destroy(&desc); continue; } switch (desc.p_type) { case STP_PARAMETER_TYPE_STRING_LIST: if (stp_check_string_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING string %s %s\n", desc.name, stp_get_string_parameter(v, desc.name)); stp_set_string_parameter(c, desc.name, stp_get_string_parameter(v, desc.name)); } else if (desc.is_mandatory) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted string %s %s\n", desc.name, desc.deflt.str); stp_set_string_parameter(c, desc.name, desc.deflt.str); } else if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: skipping string %s\n", desc.name); break; case STP_PARAMETER_TYPE_RAW: if (stp_check_raw_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) { const stp_raw_t *raw = stp_get_raw_parameter(v, desc.name); if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING raw %s\n", desc.name); stp_set_raw_parameter(c, desc.name, raw->data, raw->bytes); } else if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: skipping raw %s\n", desc.name); break; case STP_PARAMETER_TYPE_BOOLEAN: if (stp_check_boolean_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING bool %s %d\n", desc.name, stp_get_boolean_parameter(v, desc.name)); stp_set_boolean_parameter(c, desc.name, stp_get_boolean_parameter(v, desc.name)); } else if (desc.is_mandatory) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted bool %s %d\n", desc.name, desc.deflt.boolean); stp_set_boolean_parameter(c, desc.name, desc.deflt.boolean); } else if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: skipping bool %s\n", desc.name); break; case STP_PARAMETER_TYPE_INT: if (stp_check_int_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING int %s %d\n", desc.name, stp_get_int_parameter(v, desc.name)); stp_set_int_parameter(c, desc.name, stp_get_int_parameter(v, desc.name)); } else if (desc.is_mandatory) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted int %s %d\n", desc.name, desc.deflt.integer); stp_set_int_parameter(c, desc.name, desc.deflt.integer); } else if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: skipping int %s\n", desc.name); break; case STP_PARAMETER_TYPE_DOUBLE: if (stp_check_float_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING float %s %f\n", desc.name, stp_get_float_parameter(v, desc.name)); stp_set_float_parameter(c, desc.name, stp_get_float_parameter(v, desc.name)); } else if (desc.is_mandatory) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted float %s %f\n", desc.name, desc.deflt.dbl); stp_set_float_parameter(c, desc.name, desc.deflt.dbl); } else if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: skipping float %s\n", desc.name); break; case STP_PARAMETER_TYPE_DIMENSION: if (stp_check_dimension_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING dimension %s %d\n", desc.name, stp_get_dimension_parameter(v, desc.name)); stp_set_dimension_parameter(c, desc.name, stp_get_dimension_parameter(v, desc.name)); } else if (desc.is_mandatory) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted dimension %s %d\n", desc.name, desc.deflt.dimension); stp_set_dimension_parameter(c, desc.name, desc.deflt.dimension); } else if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: skipping dimension %s\n", desc.name); break; case STP_PARAMETER_TYPE_CURVE: if (stp_check_curve_parameter(v, desc.name, STP_PARAMETER_DEFAULTED)) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING curve %s\n", desc.name); stp_set_curve_parameter(c, desc.name, stp_get_curve_parameter(v, desc.name)); } else if (desc.is_mandatory) { if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: SAVING defaulted curve %s\n", desc.name); stp_set_curve_parameter(c, desc.name, desc.deflt.curve); } else if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: skipping curve %s\n", desc.name); break; default: if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Ignoring unknown type parameter %s (%d)\n", desc.name, desc.p_type); break; } stp_parameter_description_destroy(&desc); } stp_parameter_list_destroy(params); mxml = stp_xmltree_create_from_vars(c); if (mxml) { fputs("\n\n", f_options); stp_mxmlSaveFile(mxml, f_options, STP_MXML_NO_CALLBACK); stp_mxmlDelete(mxml); } (void) fclose(f_options); stp_vars_destroy(c); if (!suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Done saving parameters to %s\n", save_name); } static stp_vars_t * load_options(const char *load_name) { FILE *f_options = fopen(load_name, "r"); if (f_options) { stp_vars_t *settings = NULL; stp_mxml_node_t *mxml; mxml = stp_mxmlLoadFile(NULL, f_options, STP_MXML_NO_CALLBACK); if (mxml) { stp_mxml_node_t *nxml = stp_mxmlFindElement(mxml, mxml, "vars", NULL, NULL, STP_MXML_DESCEND); if (nxml) { settings = stp_vars_create_from_xmltree_ref(nxml->child, mxml); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: loading options from %s\n", load_file_name); if (! suppress_messages) stp_vars_print_error(settings, "DEBUG"); } } else fprintf(stderr, "DEBUG: Unable to load options from %s\n", load_file_name); fclose(f_options); return settings; } return NULL; } #endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ /* * 'main()' - Main entry and processing of driver. */ int /* O - Exit status */ main(int argc, /* I - Number of command-line arguments */ char *argv[]) /* I - Command-line arguments */ { int fd; /* File descriptor */ cups_image_t cups; /* CUPS image */ const char *ppdfile; /* PPD environment variable */ ppd_file_t *ppd; /* PPD file */ ppd_size_t *size; const stp_printer_t *printer; /* Printer driver */ int num_options; /* Number of CUPS options */ cups_option_t *options; /* CUPS options */ stp_vars_t *v = NULL; stp_vars_t *default_settings; int initialized_job = 0; const char *version_id; struct tms tms; long clocks_per_sec; struct timeval t1, t2; char *page_size_name = NULL; int aborted = 0; #ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS stp_vars_t *loaded_settings = NULL; #endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ /* * Don't buffer error/status messages... */ setbuf(stderr, NULL); if (getenv("STP_SUPPRESS_MESSAGES")) suppress_messages = 1; if (getenv("STP_SUPPRESS_VERBOSE_MESSAGES")) suppress_verbose_messages = 1; /* * Initialize libgutenprint */ po = stp_i18n_load(getenv("LANG")); theImage.rep = ∪︀ (void) gettimeofday(&t1, NULL); stp_set_global_errfunc(cups_errfunc); stp_set_global_dbgfunc(cups_dbgfunc); stp_set_global_errdata(stderr); stp_set_global_dbgdata(stderr); stp_init(); version_id = stp_get_version(); default_settings = stp_vars_create(); stp_set_outfunc(default_settings, cups_writefunc); stp_set_outdata(default_settings, stdout); /* * Check for valid arguments... */ if (argc < 6 || argc > 7) { /* * We don't have the correct number of arguments; write an error message * and return. */ stp_i18n_printf(po, _("Usage: rastertoprinter job-id user title copies " "options [file]\n")); return (1); } if (! suppress_messages) { fprintf(stderr, "DEBUG: Gutenprint: ============================================================\n"); fprintf(stderr, "DEBUG: Gutenprint: VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\n"); fprintf(stderr, "DEBUG: Gutenprint: %s Starting\n", version_id); fprintf(stderr, "DEBUG: Gutenprint: command line: %s '%s' '%s' '%s' '%s' %s%s%s%s\n", argv[0], argv[1], argv[2], argv[3], argv[4], "", argc >= 7 ? " '" : "", argc >= 7 ? argv[6] : "", argc >= 7 ? "'" : ""); } /* * Get the PPD file... */ if ((ppdfile = getenv("PPD")) == NULL) { stp_i18n_printf(po, _("ERROR: No PPD file, unable to continue!\n")); return (1); } if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: using PPD file %s\n", ppdfile); if ((ppd = ppdOpenFile(ppdfile)) == NULL) { stp_i18n_printf(po, _("ERROR: Gutenprint was unable to load PPD file " "\"%s\"!\n"), ppdfile); return (1); } if (ppd->modelname == NULL) { stp_i18n_printf(po, _("ERROR: Gutenprint did not find a ModelName " "attribute in PPD file \"%s\"!\n"), ppdfile); ppdClose(ppd); return (1); } if (ppd->nickname == NULL) { stp_i18n_printf(po, _("ERROR: Gutenprint did not find a NickName attribute " "in PPD file \"%s\"!\n"), ppdfile); ppdClose(ppd); return (1); } else if (strlen(ppd->nickname) < strlen(ppd->modelname) + strlen(CUPS_PPD_NICKNAME_STRING) + 3) { stp_i18n_printf(po, _("ERROR: Gutenprint found a corrupted NickName " "attribute in PPD file \"%s\"!\n"), ppdfile); ppdClose(ppd); return (1); } else if (strcmp(ppd->nickname + strlen(ppd->modelname) + strlen(CUPS_PPD_NICKNAME_STRING), version_id) != 0 && (strlen(ppd->nickname + strlen(ppd->modelname) + strlen(CUPS_PPD_NICKNAME_STRING)) < strlen(version_id) || !((strncmp(ppd->nickname + strlen(ppd->modelname) + strlen(CUPS_PPD_NICKNAME_STRING), version_id, strlen(version_id)) == 0) && *(ppd->nickname + strlen(ppd->modelname) + strlen(CUPS_PPD_NICKNAME_STRING)) != ' '))) { stp_i18n_printf(po, _("ERROR: The PPD version (%s) is not compatible with " "Gutenprint %s. Please run `%scups-genppdupdate' as administrator.\n"), ppd->nickname+strlen(ppd->modelname)+strlen(CUPS_PPD_NICKNAME_STRING), version_id, SBINDIR); fprintf(stderr, "DEBUG: Gutenprint: If you have upgraded your version of Gutenprint\n"); fprintf(stderr, "DEBUG: Gutenprint: recently, you must reinstall all printer queues.\n"); fprintf(stderr, "DEBUG: Gutenprint: If the previous installed version of Gutenprint\n"); fprintf(stderr, "DEBUG: Gutenprint: was 5.0.0 or higher, you can use the `cups-genppdupdate'\n"); fprintf(stderr, "DEBUG: Gutenprint: program to do this; if the previous installed version\n"); fprintf(stderr, "DEBUG: Gutenprint: was older, you can use the Modify Printer command via\n"); fprintf(stderr, "DEBUG: Gutenprint: the CUPS web interface: http://localhost:631/printers.\n"); ppdClose(ppd); return 1; } /* * Get the STP options, if any... */ num_options = cupsParseOptions(argv[5], 0, &options); ppdMarkDefaults(ppd); cupsMarkOptions(ppd, num_options, options); size = ppdPageSize(ppd, NULL); if (size) page_size_name = stp_strdup(size->name); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: CUPS option count is %d (%d bytes)\n", num_options, (int)strlen(argv[5])); if (num_options > 0) { int i; for (i = 0; i < num_options; i++) { if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: CUPS option %d %s = %s\n", i, options[i].name, options[i].value); #ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS if (!strcmp(options[i].name, "SaveFileName")) save_file_name = options[i].value; if (!strcmp(options[i].name, "LoadFileName")) load_file_name = options[i].value; #endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ } } /* * Figure out which driver to use... */ printer = stp_get_printer_by_driver(ppd->modelname); if (!printer) printer = stp_get_printer_by_long_name(ppd->modelname); if (printer == NULL) { stp_i18n_printf(po, _("ERROR: Unable to find Gutenprint driver named " "\"%s\"!\n"), ppd->modelname); ppdClose(ppd); return (1); } if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Driver %s\n", ppd->modelname); /* * Open the page stream... */ if (argc == 7) { if ((fd = open(argv[6], O_RDONLY)) == -1) { stp_i18n_printf(po, _("ERROR: Gutenprint was unable to open raster file " "\"%s\" - %s"), argv[6], strerror(errno)); sleep(1); return (1); } } else fd = 0; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Using fd %d\n", fd); stp_set_printer_defaults(default_settings, printer); #ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS if (load_file_name) loaded_settings = load_options(load_file_name); #endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ stp_set_float_parameter(default_settings, "AppGamma", 1.0); set_all_options(default_settings, options, num_options, ppd); cupsFreeOptions(num_options, options); ppdClose(ppd); cups.ras = cupsRasterOpen(fd, CUPS_RASTER_READ); /* * Process pages as needed... */ cups.page = 0; if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: About to start printing loop.\n"); /* * Read the first page header, which we need in order to set up * the page. */ signal(SIGTERM, cancel_job); while (CUPS_READ_HEADER(cups.ras, &cups.header)) { /* * We don't know how many pages we're going to print, and * we need to call stp_end_job at the completion of the job. * Therefore, we need to keep v in scope after the termination * of the loop to permit calling stp_end_job then. Therefore, * we have to free the previous page's stp_vars_t at the start * of the loop. */ if (v) stp_vars_destroy(v); /* * Setup printer driver variables... */ if (! suppress_messages) { fprintf(stderr, "DEBUG: Gutenprint: ================ Printing page %d ================\n", cups.page + 1); fprintf(stderr, "PAGE: %d %d\n", cups.page + 1, cups.header.NumCopies); } v = initialize_page(&cups, default_settings, page_size_name); #ifdef ENABLE_CUPS_LOAD_SAVE_OPTIONS if (loaded_settings) stp_copy_vars_from(v, loaded_settings); if (save_file_name) { save_options(save_file_name, v); save_file_name = NULL; } #endif /* ENABLE_CUPS_LOAD_SAVE_OPTIONS */ if (! suppress_messages) { fprintf(stderr, "DEBUG: Gutenprint: Interim page settings:\n"); stp_vars_print_error(v, "DEBUG"); } stp_merge_printvars(v, stp_printer_get_defaults(printer)); /* Pass along Collation settings */ stp_set_boolean_parameter(v, "Collate", cups.header.Collate); stp_set_boolean_parameter_active(v, "Collate", STP_PARAMETER_ACTIVE); /* Pass along Copy settings */ stp_set_int_parameter(v, "NumCopies", cups.header.NumCopies); stp_set_int_parameter_active(v, "NumCopies", STP_PARAMETER_ACTIVE); /* Pass along the page number */ stp_set_int_parameter(v, "PageNumber", cups.page); cups.row = 0; if (! suppress_messages) print_debug_block(v, &cups); print_messages_as_errors = 1; if (!initialized_job) { stp_start_job(v, &theImage); initialized_job = 1; } if (!stp_print(v, &theImage)) { if (Image_status != STP_IMAGE_STATUS_ABORT) { fprintf(stderr, "DEBUG: Gutenprint: Options failed to verify.\n"); fprintf(stderr, "DEBUG: Gutenprint: Make sure that you are using ESP Ghostscript rather\n"); fprintf(stderr, "DEBUG: Gutenprint: than GNU or AFPL Ghostscript with CUPS.\n"); fprintf(stderr, "DEBUG: Gutenprint: If this is not the cause, set LogLevel to debug to identify the problem.\n"); } aborted = 1; break; } print_messages_as_errors = 0; fflush(stdout); /* * Purge any remaining bitmap data... */ if (cups.row < cups.header.cupsHeight) purge_excess_data(&cups); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: ================ Done printing page %d ================\n", cups.page + 1); cups.page ++; } if (v) { if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: %s job\n", aborted ? "Aborted" : "Ending"); stp_end_job(v, &theImage); fflush(stdout); stp_vars_destroy(v); } cupsRasterClose(cups.ras); (void) times(&tms); (void) gettimeofday(&t2, NULL); clocks_per_sec = sysconf(_SC_CLK_TCK); fprintf(stderr, "DEBUG: Gutenprint: stats %.0fB, %.3fu, %.3fs, %.3fel\n", total_bytes_printed, (double) tms.tms_utime / clocks_per_sec, (double) tms.tms_stime / clocks_per_sec, (double) (t2.tv_sec - t1.tv_sec) + ((double) (t2.tv_usec - t1.tv_usec)) / 1000000.0); if (!suppress_messages) { fprintf(stderr, "DEBUG: Gutenprint: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); fprintf(stderr, "DEBUG: Gutenprint: ============================================================\n"); } stp_vars_destroy(default_settings); if (page_size_name) stp_free(page_size_name); if (fd != 0) close(fd); return aborted ? 1 : 0; } /* * 'cups_writefunc()' - Write data to a file... */ static void cups_writefunc(void *file, const char *buf, size_t bytes) { FILE *prn = (FILE *)file; total_bytes_printed += bytes; fwrite(buf, 1, bytes, prn); } static void cups_errfunc(void *file, const char *buf, size_t bytes) { size_t next_nl = 0; size_t where = 0; FILE *prn = (FILE *)file; while (where < bytes) { if (bytes - where > 6 && strncmp(buf, "ERROR:", 6) == 0) { fputs("ERROR: Gutenprint:", prn); buf += 6; } else if (print_messages_as_errors) fputs("ERROR: Gutenprint: ", prn); else if (strncmp(buf, "DEBUG", 5) != 0) fputs("DEBUG: Gutenprint: ", prn); while (next_nl < bytes) { if (buf[next_nl++] == '\n') break; } fwrite(buf + where, 1, next_nl - where, prn); where = next_nl; } } static void cups_dbgfunc(void *file, const char *buf, size_t bytes) { size_t next_nl = 0; size_t where = 0; FILE *prn = (FILE *)file; while (where < bytes) { if (bytes - where > 6 && strncmp(buf, "ERROR:", 6) == 0) { fputs("ERROR: Gutenprint:", prn); buf += 6; } else if (strncmp(buf, "DEBUG", 5) != 0) fputs("DEBUG: Gutenprint: ", prn); while (next_nl < bytes) { if (buf[next_nl++] == '\n') break; } fwrite(buf + where, 1, next_nl - where, prn); where = next_nl; } } /* * 'cancel_job()' - Cancel the current job... */ static void cancel_job(int sig) /* I - Signal */ { (void)sig; Image_status = STP_IMAGE_STATUS_ABORT; } /* * 'Image_get_appname()' - Get the application we are running. */ static const char * /* O - Application name */ Image_get_appname(stp_image_t *image) /* I - Image */ { (void)image; return ("CUPS driver based on Gutenprint"); } /* * 'Image_get_row()' - Get one row of the image. */ static void throwaway_data(int amount, cups_image_t *cups) { unsigned char trash[4096]; /* Throwaway */ int block_count = amount / 4096; int leftover = amount % 4096; while (block_count > 0) { cupsRasterReadPixels(cups->ras, trash, 4096); block_count--; } if (leftover) cupsRasterReadPixels(cups->ras, trash, leftover); } static stp_image_status_t Image_get_row(stp_image_t *image, /* I - Image */ unsigned char *data, /* O - Row */ size_t byte_limit, /* I - how many bytes in data */ int row) /* I - Row number (unused) */ { cups_image_t *cups; /* CUPS image */ int i; /* Looping var */ int bytes_per_line; int margin; stp_image_status_t tmp_image_status = Image_status; unsigned char *orig = data; /* Temporary pointer */ static int warned = 0; /* Error warning printed? */ int new_percent; int left_margin, right_margin; if ((cups = (cups_image_t *)(image->rep)) == NULL) { stp_i18n_printf(po, _("ERROR: Gutenprint image is not initialized! " "Please report this bug to " "gimp-print-devel@lists.sourceforge.net\n")); return STP_IMAGE_STATUS_ABORT; } bytes_per_line = ((cups->adjusted_width * cups->header.cupsBitsPerPixel) + CHAR_BIT - 1) / CHAR_BIT; left_margin = ((cups->left_trim * cups->header.cupsBitsPerPixel) + CHAR_BIT - 1) / CHAR_BIT; right_margin = ((cups->right_trim * cups->header.cupsBitsPerPixel) + CHAR_BIT - 1) / CHAR_BIT; margin = cups->header.cupsBytesPerLine - left_margin - bytes_per_line - right_margin; if (cups->row < cups->header.cupsHeight) { if (! suppress_messages && ! suppress_verbose_messages) fprintf(stderr, "DEBUG2: Gutenprint: Reading %d %d\n", bytes_per_line, cups->row); while (cups->row <= row && cups->row < cups->header.cupsHeight) { if (left_margin > 0) { if (! suppress_messages && ! suppress_verbose_messages) fprintf(stderr, "DEBUG2: Gutenprint: Tossing left %d (%d)\n", left_margin, cups->left_trim); throwaway_data(left_margin, cups); } cupsRasterReadPixels(cups->ras, data, bytes_per_line); cups->row ++; if (margin + right_margin > 0) { if (! suppress_messages && ! suppress_verbose_messages) fprintf(stderr, "DEBUG2: Gutenprint: Tossing right %d (%d) + %d\n", right_margin, cups->right_trim, margin); throwaway_data(margin + right_margin, cups); } } } else { switch (cups->header.cupsColorSpace) { case CUPS_CSPACE_K: case CUPS_CSPACE_CMYK: case CUPS_CSPACE_KCMY: case CUPS_CSPACE_CMY: memset(data, 0, bytes_per_line); break; case CUPS_CSPACE_RGB: case CUPS_CSPACE_W: memset(data, ((1 << CHAR_BIT) - 1), bytes_per_line); break; default: stp_i18n_printf(po, _("ERROR: Gutenprint detected a bad colorspace " "(%d)!\n"), cups->header.cupsColorSpace); return STP_IMAGE_STATUS_ABORT; } } /* * This exists to print non-ADSC input which has messed up the job * input, such as that generated by psnup. The output is barely * legible, but it's better than the garbage output otherwise. */ data = orig; if (cups->header.cupsBitsPerPixel == 1) { if (warned == 0) { fputs(_("WARNING: Gutenprint detected a bad color depth (1). " "Output quality is degraded. Are you using psnup or " "non-ADSC PostScript?\n"), stderr); warned = 1; } for (i = cups->adjusted_width - 1; i >= 0; i--) { if ( (data[i/8] >> (7 - i%8)) &0x1) data[i]=255; else data[i]=0; } } new_percent = (int) (100.0 * cups->row / cups->header.cupsHeight); if (new_percent > cups->last_percent) { if (! suppress_verbose_messages) { stp_i18n_printf(po, _("INFO: Printing page %d, %d%%\n"), cups->page + 1, new_percent); fprintf(stderr, "ATTR: job-media-progress=%d\n", new_percent); } cups->last_percent = new_percent; } if (tmp_image_status != STP_IMAGE_STATUS_OK) { if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Image status %d\n", tmp_image_status); } return tmp_image_status; } /* * 'Image_height()' - Return the height of an image. */ static int /* O - Height in pixels */ Image_height(stp_image_t *image) /* I - Image */ { cups_image_t *cups; /* CUPS image */ if ((cups = (cups_image_t *)(image->rep)) == NULL) return (0); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Image_height %d\n", cups->adjusted_height); return (cups->adjusted_height); } /* * 'Image_init()' - Initialize an image. */ static void Image_init(stp_image_t *image) /* I - Image */ { cups_image_t *cups; /* CUPS image */ if ((cups = (cups_image_t *)(image->rep)) == NULL) return; cups->last_percent = 0; if (! suppress_messages) stp_i18n_printf(po, _("INFO: Starting page %d...\n"), cups->page + 1); /* cups->page + 1 because users expect 1-based counting */ } /* * 'Image_progress_conclude()' - Close the progress display. */ static void Image_conclude(stp_image_t *image) /* I - Image */ { cups_image_t *cups; /* CUPS image */ if ((cups = (cups_image_t *)(image->rep)) == NULL) return; if (! suppress_messages) stp_i18n_printf(po, _("INFO: Finished page %d...\n"), cups->page + 1); } /* * 'Image_width()' - Return the width of an image. */ static int /* O - Width in pixels */ Image_width(stp_image_t *image) /* I - Image */ { cups_image_t *cups; /* CUPS image */ if ((cups = (cups_image_t *)(image->rep)) == NULL) return (0); if (! suppress_messages) fprintf(stderr, "DEBUG: Gutenprint: Image_width %d\n", cups->adjusted_width); return (cups->adjusted_width); }