/* * * Print plug-in Adobe PostScript driver for the GIMP. * * Copyright 1997-2002 Michael Sweet (mike@easysw.com) and * Robert Krawitz (rlk@alum.mit.edu) * * 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 . */ /* * This file must include only standard C header files. The core code must * compile on generic platforms that don't support glib, gimp, gtk, etc. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "gutenprint-internal.h" #include #include #include #ifdef HAVE_LIMITS_H #include #endif #include #include #include #include "xmlppd.h" #ifdef _MSC_VER #define strncasecmp(s,t,n) _strnicmp(s,t,n) #define strcasecmp(s,t) _stricmp(s,t) #endif /* * Local variables... */ static char *m_ppd_file = NULL; static stp_mxml_node_t *m_ppd = NULL; /* * Local functions... */ static void ps_hex(const stp_vars_t *, unsigned short *, int); static void ps_ascii85(const stp_vars_t *, unsigned short *, int, int); static const stp_parameter_t the_parameters[] = { { "PPDFile", N_("PPDFile"), "Color=Yes,Category=Basic Printer Setup", N_("PPD File"), STP_PARAMETER_TYPE_FILE, STP_PARAMETER_CLASS_FEATURE, STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0 }, { "PageSize", N_("Page Size"), "Color=No,Category=Basic Printer Setup", N_("Size of the paper being printed to"), STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE, STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0 }, { "ModelName", N_("Model Name"), "Color=Yes,Category=Basic Printer Setup", N_("PPD File Model Name"), STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE, STP_PARAMETER_LEVEL_INTERNAL, 0, 0, STP_CHANNEL_NONE, 0, 0 }, { "PrintingMode", N_("Printing Mode"), "Color=Yes,Category=Core Parameter", N_("Printing Output Mode"), STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE, STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0 }, }; static const int the_parameter_count = sizeof(the_parameters) / sizeof(const stp_parameter_t); static int ps_option_to_param(const stp_vars_t *v, stp_parameter_t *param, stp_mxml_node_t *option) { const char *group_text = stp_mxmlElementGetAttr(option, "grouptext"); if (group_text != NULL) param->category = group_text; else param->category = NULL; param->text = stp_mxmlElementGetAttr(option, "text"); param->help = stp_mxmlElementGetAttr(option, "text"); if (stp_mxmlElementGetAttr(option, "stptype")) { const char *default_value = stp_mxmlElementGetAttr(option, "default"); double stp_default_value = strtod(stp_mxmlElementGetAttr(option, "stpdefault"), 0); double lower_bound = strtod(stp_mxmlElementGetAttr(option, "stplower"), NULL); double upper_bound = strtod(stp_mxmlElementGetAttr(option, "stpupper"), NULL); param->p_type = atoi(stp_mxmlElementGetAttr(option, "stptype")); param->is_mandatory = atoi(stp_mxmlElementGetAttr(option, "stpmandatory")); param->p_class = atoi(stp_mxmlElementGetAttr(option, "stpclass")); param->p_level = atoi(stp_mxmlElementGetAttr(option, "stplevel")); param->channel = (unsigned char) atoi(stp_mxmlElementGetAttr(option, "stpchannel")); param->read_only = 0; param->is_active = 1; param->verify_this_parameter = 1; param->name = stp_mxmlElementGetAttr(option, "stpname"); stp_dprintf(STP_DBG_PS, v, "Gutenprint parameter %s type %d mandatory %d class %d level %d channel %d default %s %f", param->name, param->p_type, param->is_mandatory, param->p_class, param->p_level, param->channel, default_value, stp_default_value); switch (param->p_type) { case STP_PARAMETER_TYPE_DOUBLE: param->deflt.dbl = stp_default_value; param->bounds.dbl.upper = upper_bound; param->bounds.dbl.lower = lower_bound; stp_dprintf(STP_DBG_PS, v, " %.3f %.3f %.3f\n", param->deflt.dbl, param->bounds.dbl.upper, param->bounds.dbl.lower); break; case STP_PARAMETER_TYPE_DIMENSION: param->deflt.dimension = atoi(default_value); param->bounds.dimension.upper = (stp_dimension_t) upper_bound; param->bounds.dimension.lower = (stp_dimension_t) lower_bound; stp_dprintf(STP_DBG_PS, v, " %f %f %f\n", param->deflt.dimension, param->bounds.dimension.upper, param->bounds.dimension.lower); break; case STP_PARAMETER_TYPE_INT: param->deflt.integer = atoi(default_value); param->bounds.integer.upper = (int) upper_bound; param->bounds.integer.lower = (int) lower_bound; stp_dprintf(STP_DBG_PS, v, " %d %d %d\n", param->deflt.integer, param->bounds.integer.upper, param->bounds.integer.lower); break; case STP_PARAMETER_TYPE_BOOLEAN: param->deflt.boolean = strcasecmp(default_value, "true") == 0 ? 1 : 0; stp_dprintf(STP_DBG_PS, v, " %d\n", param->deflt.boolean); break; default: stp_dprintf(STP_DBG_PS, v, "\n"); break; } } else { const char *ui = stp_mxmlElementGetAttr(option, "ui"); param->name = stp_mxmlElementGetAttr(option, "name"); if (strcasecmp(ui, "Boolean") == 0) param->p_type = STP_PARAMETER_TYPE_BOOLEAN; else param->p_type = STP_PARAMETER_TYPE_STRING_LIST; if (strcmp(param->name, "PageSize") == 0) param->p_class = STP_PARAMETER_CLASS_CORE; else param->p_class = STP_PARAMETER_CLASS_FEATURE; param->p_level = STP_PARAMETER_LEVEL_BASIC; param->is_mandatory = 1; param->is_active = 1; param->channel = -1; param->verify_this_parameter = 1; param->read_only = 0; } return 0; } /* * 'ps_parameters()' - Return the parameter values for the given parameter. */ static int ppd_whitespace_callback(stp_mxml_node_t *node, int where) { return 0; } static int check_ppd_file(const stp_vars_t *v) { const char *ppd_file = stp_get_file_parameter(v, "PPDFile"); if (ppd_file == NULL || ppd_file[0] == 0) { stp_dprintf(STP_DBG_PS, v, "Empty PPD file\n"); return 0; } else if (m_ppd_file && strcmp(m_ppd_file, ppd_file) == 0) { stp_dprintf(STP_DBG_PS, v, "Not replacing PPD file %s\n", m_ppd_file); return 1; } else { stp_dprintf(STP_DBG_PS, v, "Replacing PPD file %s with %s\n", m_ppd_file ? m_ppd_file : "(null)", ppd_file ? ppd_file : "(null)"); if (m_ppd != NULL) stp_mxmlDelete(m_ppd); m_ppd = NULL; if (m_ppd_file) stp_free(m_ppd_file); m_ppd_file = NULL; if ((m_ppd = stpi_xmlppd_read_ppd_file(ppd_file)) == NULL) { stp_eprintf(v, "Unable to open PPD file %s\n", ppd_file); return 0; } if (stp_get_debug_level() & STP_DBG_PS) { char *ppd_stuff = stp_mxmlSaveAllocString(m_ppd, ppd_whitespace_callback); stp_dprintf(STP_DBG_PS, v, "%s", ppd_stuff); stp_free(ppd_stuff); } m_ppd_file = stp_strdup(ppd_file); return 1; } } static stp_parameter_list_t ps_list_parameters(const stp_vars_t *v) { stp_parameter_list_t *ret = stp_parameter_list_create(); stp_mxml_node_t *option; int i; int status = check_ppd_file(v); stp_dprintf(STP_DBG_PS, v, "Adding parameters from %s (%d)\n", m_ppd_file ? m_ppd_file : "(null)", status); for (i = 0; i < the_parameter_count; i++) stp_parameter_list_add_param(ret, &(the_parameters[i])); if (status) { int num_options = stpi_xmlppd_find_option_count(m_ppd); stp_dprintf(STP_DBG_PS, v, "Found %d parameters\n", num_options); for (i=0; i < num_options; i++) { /* MEMORY LEAK!!! */ stp_parameter_t *param = stp_malloc(sizeof(stp_parameter_t)); option = stpi_xmlppd_find_option_index(m_ppd, i); if (option) { ps_option_to_param(v, param, option); if (param->p_type != STP_PARAMETER_TYPE_INVALID && strcmp(param->name, "PageRegion") != 0 && strcmp(param->name, "PageSize") != 0) { stp_dprintf(STP_DBG_PS, v, "Adding parameter %s %s\n", param->name, param->text); stp_parameter_list_add_param(ret, param); } else stp_free(param); } } } return ret; } static void ps_parameters_internal(const stp_vars_t *v, const char *name, stp_parameter_t *description) { int i; stp_mxml_node_t *option; int status = 0; int num_choices; const char *defchoice; description->p_type = STP_PARAMETER_TYPE_INVALID; description->deflt.str = 0; description->is_active = 0; if (name == NULL) return; status = check_ppd_file(v); for (i = 0; i < the_parameter_count; i++) { if (strcmp(name, the_parameters[i].name) == 0) { stp_fill_parameter_settings(description, &(the_parameters[i])); if (strcmp(name, "PPDFile") == 0) description->is_active = 1; else if (strcmp(name, "ModelName") == 0) { const char *nickname; description->bounds.str = stp_string_list_create(); if (m_ppd && stp_mxmlElementGetAttr(m_ppd, "nickname")) nickname = stp_mxmlElementGetAttr(m_ppd, "nickname"); else nickname = _("None; please provide a PPD file"); stp_string_list_add_string_unsafe(description->bounds.str, nickname, nickname); description->deflt.str = nickname; description->is_active = 1; return; } else if (strcmp(name, "PrintingMode") == 0) { if (! m_ppd || strcmp(stp_mxmlElementGetAttr(m_ppd, "color"), "1") == 0) { description->bounds.str = stp_string_list_create(); stp_string_list_add_string (description->bounds.str, "Color", _("Color")); stp_string_list_add_string (description->bounds.str, "BW", _("Black and White")); description->deflt.str = stp_string_list_param(description->bounds.str, 0)->name; description->is_active = 1; } else description->is_active = 0; return; } } } if (!status && strcmp(name, "PageSize") != 0) return; if ((option = stpi_xmlppd_find_option_named(m_ppd, name)) == NULL) { if (strcmp(name, "PageSize") == 0) { /* Provide a default set of page sizes */ description->bounds.str = stp_string_list_create(); stp_string_list_add_string (description->bounds.str, "Letter", _("Letter")); stp_string_list_add_string (description->bounds.str, "A4", _("A4")); stp_string_list_add_string (description->bounds.str, "Custom", _("Custom")); description->deflt.str = stp_string_list_param(description->bounds.str, 0)->name; description->is_active = 1; return; } else { char *tmp = stp_malloc(strlen(name) + 4); strcpy(tmp, "Stp"); strncat(tmp, name, strlen(name) + 3); if ((option = stpi_xmlppd_find_option_named(m_ppd, tmp)) == NULL) { stp_dprintf(STP_DBG_PS, v, "no parameter %s", name); stp_free(tmp); return; } stp_free(tmp); } } ps_option_to_param(v, description, option); if (description->p_type != STP_PARAMETER_TYPE_STRING_LIST) return; num_choices = atoi(stp_mxmlElementGetAttr(option, "num_choices")); defchoice = stp_mxmlElementGetAttr(option, "default"); description->bounds.str = stp_string_list_create(); stp_dprintf(STP_DBG_PS, v, "describe parameter %s, output name=[%s] text=[%s] category=[%s] choices=[%d] default=[%s]\n", name, description->name, description->text, description->category, num_choices, defchoice); /* Describe all choices for specified option. */ for (i=0; i < num_choices; i++) { stp_mxml_node_t *choice = stpi_xmlppd_find_choice_index(option, i); const char *choice_name = stp_mxmlElementGetAttr(choice, "name"); const char *choice_text = stp_mxmlElementGetAttr(choice, "text"); stp_string_list_add_string(description->bounds.str, choice_name, choice_text); stp_dprintf(STP_DBG_PS, v, " parameter %s, choice %d [%s] [%s]", name, i, choice_name, choice_text); if (strcmp(choice_name, defchoice) == 0) { stp_dprintf(STP_DBG_PS, v, " parameter %s, choice %d [%s] DEFAULT\n", name, i, choice_name); description->deflt.str = choice_name; } } if (!description->deflt.str) { stp_dprintf(STP_DBG_PS, v, " parameter %s, defaulting to [%s]", name, stp_string_list_param(description->bounds.str, 0)->name); description->deflt.str = stp_string_list_param(description->bounds.str, 0)->name; } if (stp_string_list_count(description->bounds.str) > 0) description->is_active = 1; return; } static void ps_parameters(const stp_vars_t *v, const char *name, stp_parameter_t *description) { #ifdef HAVE_LOCALE_H char *locale = stp_strdup(setlocale(LC_ALL, NULL)); setlocale(LC_ALL, "C"); #endif ps_parameters_internal(v, name, description); #ifdef HAVE_LOCALE_H setlocale(LC_ALL, locale); stp_free(locale); #endif } /* * 'ps_media_size()' - Return the size of the page. */ static void ps_media_size_internal(const stp_vars_t *v, /* I */ stp_dimension_t *width, /* O - Width in points */ stp_dimension_t *height) /* O - Height in points */ { const char *pagesize = stp_get_string_parameter(v, "PageSize"); int status = check_ppd_file(v); if (!pagesize) pagesize = ""; stp_dprintf(STP_DBG_PS, v, "ps_media_size(%d, \'%s\', \'%s\', %p, %p)\n", stp_get_model_id(v), m_ppd_file, pagesize, (void *) width, (void *) height); stp_default_media_size(v, width, height); if (status) { stp_mxml_node_t *paper = stpi_xmlppd_find_page_size(m_ppd, pagesize); if (paper) { *width = atoi(stp_mxmlElementGetAttr(paper, "width")); *height = atoi(stp_mxmlElementGetAttr(paper, "height")); } else { *width = 0; *height = 0; } } stp_dprintf(STP_DBG_PS, v, "dimensions %f %f\n", *width, *height); return; } static const stp_papersize_t * ps_describe_papersize(const stp_vars_t *v, const char *name) { int status = check_ppd_file(v); if (status) { stp_mxml_node_t *paper = stpi_xmlppd_find_page_size(m_ppd, name); if (paper) { const char *papersize_list_name = m_ppd_file ? m_ppd_file : "NOPPD"; stp_papersize_list_t *ourlist = stpi_find_papersize_list_named(papersize_list_name); const stp_papersize_t *papersize; const stp_papersize_t *standard_papersize = stpi_get_listed_papersize(name, "standard"); if (! ourlist) ourlist = stpi_new_papersize_list(papersize_list_name); papersize = stpi_get_papersize_by_name(ourlist, name); if (! papersize) { stp_papersize_t *npapersize = stp_malloc(sizeof(stp_papersize_t)); npapersize->name = stp_strdup(name); npapersize->text = stp_strdup(name); npapersize->comment = NULL; /* * Note that we used the width and height from the PPD file, * not from the standard definition. This is so that if the * PPD file is for another driver that uses slightly different * dimensions than we do that our description matches that of * driver in use. */ npapersize->width = atof(stp_mxmlElementGetAttr(paper, "width")); npapersize->height = atof(stp_mxmlElementGetAttr(paper, "height")); /* * Only use auxiliary information from our list if our paper size * really is substantially the same as what the PPD file says! */ if (standard_papersize && fabs(npapersize->width - standard_papersize->width) < 1 && fabs(npapersize->height - standard_papersize->height) < 1) { npapersize->paper_unit = standard_papersize->paper_unit; npapersize->paper_size_type = standard_papersize->paper_size_type; npapersize->top = standard_papersize->top; npapersize->left = standard_papersize->left; npapersize->bottom = standard_papersize->bottom; npapersize->right = standard_papersize->right; } else { npapersize->top = 0; npapersize->left = 0; npapersize->bottom = 0; npapersize->right = 0; npapersize->paper_unit = PAPERSIZE_ENGLISH_STANDARD; npapersize->paper_size_type = PAPERSIZE_TYPE_STANDARD; } if (stpi_papersize_create(ourlist, npapersize)) return npapersize; } return papersize; } } return NULL; } static void ps_media_size(const stp_vars_t *v, stp_dimension_t *width, stp_dimension_t *height) { #ifdef HAVE_LOCALE_H char *locale = stp_strdup(setlocale(LC_ALL, NULL)); setlocale(LC_ALL, "C"); #endif ps_media_size_internal(v, width, height); #ifdef HAVE_LOCALE_H setlocale(LC_ALL, locale); stp_free(locale); #endif } /* * 'ps_imageable_area()' - Return the imageable area of the page. */ static void ps_imageable_area_internal(const stp_vars_t *v, /* I */ int use_max_area, /* I - Use maximum area */ stp_dimension_t *left, /* O - Left position in points */ stp_dimension_t *right, /* O - Right position in points */ stp_dimension_t *bottom, /* O - Bottom position in points */ stp_dimension_t *top) /* O - Top position in points */ { stp_dimension_t width, height; const char *pagesize = stp_get_string_parameter(v, "PageSize"); if (!pagesize) pagesize = ""; /* Set some defaults. */ ps_media_size_internal(v, &width, &height); *left = 0; *right = width; *top = 0; *bottom = height; if (check_ppd_file(v)) { stp_mxml_node_t *paper = stpi_xmlppd_find_page_size(m_ppd, pagesize); if (paper) { double pleft = atoi(stp_mxmlElementGetAttr(paper, "left")); double pright = atoi(stp_mxmlElementGetAttr(paper, "right")); double ptop = atoi(stp_mxmlElementGetAttr(paper, "top")); double pbottom = atoi(stp_mxmlElementGetAttr(paper, "bottom")); stp_dprintf(STP_DBG_PS, v, "size=l %f r %f b %f t %f h %f w %f\n", pleft, pright, pbottom, ptop, height, width); *left = (stp_dimension_t) pleft; *right = (stp_dimension_t) pright; *top = height - (stp_dimension_t) ptop; *bottom = height - (stp_dimension_t) pbottom; stp_dprintf(STP_DBG_PS, v, ">>>> l %f r %f b %f t %f h %f w %f\n", *left, *right, *bottom, *top, height, width); } } if (use_max_area) { if (*left > 0) *left = 0; if (*right < width) *right = width; if (*top > 0) *top = 0; if (*bottom < height) *bottom = height; } stp_dprintf(STP_DBG_PS, v, "pagesize %s max_area=%d l %f r %f b %f t %f h %f w %f\n", pagesize ? pagesize : "(null)", use_max_area, *left, *right, *bottom, *top, width, height); return; } static void ps_imageable_area(const stp_vars_t *v, /* I */ stp_dimension_t *left, /* O - Left position in points */ stp_dimension_t *right, /* O - Right position in points */ stp_dimension_t *bottom, /* O - Bottom position in points */ stp_dimension_t *top) /* O - Top position in points */ { #ifdef HAVE_LOCALE_H char *locale = stp_strdup(setlocale(LC_ALL, NULL)); setlocale(LC_ALL, "C"); #endif ps_imageable_area_internal(v, 0, left, right, bottom, top); #ifdef HAVE_LOCALE_H setlocale(LC_ALL, locale); stp_free(locale); #endif } static void ps_maximum_imageable_area(const stp_vars_t *v, /* I */ stp_dimension_t *left, /* O - Left position in points */ stp_dimension_t *right, /* O - Right position in points */ stp_dimension_t *bottom, /* O - Bottom position in points */ stp_dimension_t *top) /* O - Top position in points */ { #ifdef HAVE_LOCALE_H char *locale = stp_strdup(setlocale(LC_ALL, NULL)); setlocale(LC_ALL, "C"); #endif ps_imageable_area_internal(v, 1, left, right, bottom, top); #ifdef HAVE_LOCALE_H setlocale(LC_ALL, locale); stp_free(locale); #endif } static void ps_limit(const stp_vars_t *v, /* I */ stp_dimension_t *width, stp_dimension_t *height, stp_dimension_t *min_width, stp_dimension_t *min_height) { *width = (stp_dimension_t) INT_MAX; *height = (stp_dimension_t) INT_MAX; *min_width = 1; *min_height = 1; } /* * This is really bogus... */ static void ps_describe_resolution_internal(const stp_vars_t *v, stp_resolution_t *x, stp_resolution_t *y) { const char *resolution = stp_get_string_parameter(v, "Resolution"); *x = -1; *y = -1; if (resolution) { int tx = -1; int ty = -1; sscanf(resolution, "%dx%d", &tx, &ty); *x = (stp_resolution_t) tx; *y = (stp_resolution_t) ty; } return; } static void ps_describe_resolution(const stp_vars_t *v, stp_resolution_t *x, stp_resolution_t *y) { #ifdef HAVE_LOCALE_H char *locale = stp_strdup(setlocale(LC_ALL, NULL)); setlocale(LC_ALL, "C"); #endif ps_describe_resolution_internal(v, x, y); #ifdef HAVE_LOCALE_H setlocale(LC_ALL, locale); stp_free(locale); #endif } static const char * ps_describe_output(const stp_vars_t *v) { const char *print_mode = stp_get_string_parameter(v, "PrintingMode"); const char *input_image_type = stp_get_string_parameter(v, "InputImageType"); if (print_mode && strcmp(print_mode, "Color") == 0) { if (input_image_type && (strcmp(input_image_type, "CMYK") == 0 || strcmp(input_image_type, "KCMY") == 0)) return "CMYK"; else return "RGB"; } else return "Whitescale"; } static stp_string_list_t * ps_external_options(const stp_vars_t *v) { stp_parameter_list_t param_list = ps_list_parameters(v); stp_string_list_t *answer; char *tmp; char *ppd_name = NULL; int i; #ifdef HAVE_LOCALE_H char *locale; #endif if (! param_list) return NULL; answer = stp_string_list_create(); #ifdef HAVE_LOCALE_H locale = stp_strdup(setlocale(LC_ALL, NULL)); setlocale(LC_ALL, "C"); #endif for (i = 0; i < stp_parameter_list_count(param_list); i++) { const stp_parameter_t *param = stp_parameter_list_param(param_list, i); stp_parameter_t desc; stp_describe_parameter(v, param->name, &desc); if (desc.is_active) { stp_mxml_node_t *option; if (m_ppd && (option = stpi_xmlppd_find_option_named(m_ppd, desc.name)) == NULL) { ppd_name = stp_malloc(strlen(desc.name) + 4); strcpy(ppd_name, "Stp"); strncat(ppd_name, desc.name, strlen(desc.name) + 3); if ((option = stpi_xmlppd_find_option_named(m_ppd, ppd_name)) == NULL) { stp_dprintf(STP_DBG_PS, v, "no parameter %s", desc.name); STP_SAFE_FREE(ppd_name); } } switch (desc.p_type) { case STP_PARAMETER_TYPE_STRING_LIST: if (stp_get_string_parameter(v, desc.name) && strcmp(stp_get_string_parameter(v, desc.name), desc.deflt.str)) { stp_dprintf(STP_DBG_PS, v, "Adding string parameter %s (%s): %s %s\n", desc.name, ppd_name ? ppd_name : "(null)", stp_get_string_parameter(v, desc.name), desc.deflt.str); stp_string_list_add_string(answer, ppd_name ? ppd_name : desc.name, stp_get_string_parameter(v, desc.name)); } break; case STP_PARAMETER_TYPE_INT: if (stp_get_int_parameter(v, desc.name) != desc.deflt.integer) { stp_dprintf(STP_DBG_PS, v, "Adding integer parameter %s (%s): %d %d\n", desc.name, ppd_name ? ppd_name : "(null)", stp_get_int_parameter(v, desc.name), desc.deflt.integer); stp_asprintf(&tmp, "%d", stp_get_int_parameter(v, desc.name)); stp_string_list_add_string(answer, ppd_name ? ppd_name : desc.name, tmp); stp_free(tmp); } break; case STP_PARAMETER_TYPE_BOOLEAN: if (stp_get_boolean_parameter(v, desc.name) != desc.deflt.boolean) { stp_dprintf(STP_DBG_PS, v, "Adding boolean parameter %s (%s): %d %d\n", desc.name, ppd_name ? ppd_name : "(null)", stp_get_boolean_parameter(v, desc.name), desc.deflt.boolean); stp_asprintf(&tmp, "%s", stp_get_boolean_parameter(v, desc.name) ? "True" : "False"); stp_string_list_add_string(answer, ppd_name ? ppd_name : desc.name, tmp); stp_free(tmp); } break; case STP_PARAMETER_TYPE_DOUBLE: if (fabs(stp_get_float_parameter(v, desc.name) - desc.deflt.dbl) > .00001) { stp_dprintf(STP_DBG_PS, v, "Adding float parameter %s (%s): %.3f %.3f\n", desc.name, ppd_name ? ppd_name : "(null)", stp_get_float_parameter(v, desc.name), desc.deflt.dbl); stp_asprintf(&tmp, "%.3f", stp_get_float_parameter(v, desc.name)); stp_string_list_add_string(answer, ppd_name ? ppd_name : desc.name, tmp); stp_free(tmp); } break; case STP_PARAMETER_TYPE_DIMENSION: if (stp_get_dimension_parameter(v, desc.name) != desc.deflt.dimension) { stp_dprintf(STP_DBG_PS, v, "Adding dimension parameter %s (%s): %f %f\n", desc.name, ppd_name ? ppd_name : "(null)", stp_get_dimension_parameter(v, desc.name), desc.deflt.dimension); stp_asprintf(&tmp, "%f", stp_get_dimension_parameter(v, desc.name)); stp_string_list_add_string(answer, ppd_name ? ppd_name : desc.name, tmp); stp_free(tmp); } break; default: break; } STP_SAFE_FREE(ppd_name); } stp_parameter_description_destroy(&desc); } #ifdef HAVE_LOCALE_H setlocale(LC_ALL, locale); stp_free(locale); #endif return answer; } /* * 'ps_print_device_settings()' - output postscript code from PPD into the * postscript stream. */ static void ps_print_device_settings(stp_vars_t *v) { int i; stp_parameter_list_t param_list = ps_list_parameters(v); if (! param_list) return; stp_puts("%%BeginSetup\n", v); for (i = 0; i < stp_parameter_list_count(param_list); i++) { const stp_parameter_t *param = stp_parameter_list_param(param_list, i); stp_parameter_t desc; stp_describe_parameter(v, param->name, &desc); if (desc.is_active) { switch (desc.p_type) { case STP_PARAMETER_TYPE_STRING_LIST: case STP_PARAMETER_TYPE_BOOLEAN: { const char *val=NULL; const char *defval=NULL; /* If this is a bool parameter, set val to "True" or "False" - otherwise fetch from string parameter. */ if(desc.p_type==STP_PARAMETER_TYPE_BOOLEAN) { val=stp_get_boolean_parameter(v,desc.name) ? "True" : "False"; defval=desc.deflt.boolean ? "True" : "False"; } else { val=stp_get_string_parameter(v,desc.name); defval=desc.deflt.str; } /* We only include the option's code if it's set to a value other than the default. */ if(val && defval && (strcmp(val,defval)!=0)) { if(m_ppd) { /* If we have a PPD xml tree we hunt for the appropriate "option" and "choice"... */ stp_mxml_node_t *node=m_ppd; node=stp_mxmlFindElement(node,node, "option", "name", desc.name, STP_MXML_DESCEND); if(node) { node=stp_mxmlFindElement(node,node, "choice", "name", val, STP_MXML_DESCEND); if(node && node->child) { if(node->child->value.opaque && (strlen(node->child->value.opaque)>1)) { /* If we have opaque data for the child, we use %%BeginFeature and copy the code verbatim. */ stp_puts("[{\n", v); stp_zprintf(v, "%%%%BeginFeature: *%s %s\n", desc.name, val); if(node->child->value.opaque) stp_puts(node->child->value.opaque,v); stp_puts("\n%%EndFeature\n", v); stp_puts("} stopped cleartomark\n", v); } else { /* If we don't have code, we use %%IncludeFeature instead. */ stp_puts("[{\n", v); stp_zprintf(v, "%%%%IncludeFeature: *%s %s\n", desc.name, val); if(node->child->value.opaque) stp_puts(node->child->value.opaque,v); stp_puts("} stopped cleartomark\n", v); } } } } } } break; case STP_PARAMETER_TYPE_INT: if(stp_get_int_parameter(v,desc.name)!=desc.deflt.integer) { stp_puts("[{\n", v); stp_zprintf(v, "%%%%IncludeFeature: *%s %d\n", desc.name, stp_get_int_parameter(v, desc.name)); stp_puts("} stopped cleartomark\n", v); } break; case STP_PARAMETER_TYPE_DOUBLE: if(stp_get_float_parameter(v,desc.name)!=desc.deflt.dbl) { stp_puts("[{\n", v); stp_zprintf(v, "%%%%IncludeFeature: *%s %f\n", desc.name, stp_get_float_parameter(v, desc.name)); stp_puts("} stopped cleartomark\n", v); } break; case STP_PARAMETER_TYPE_DIMENSION: if(stp_get_dimension_parameter(v,desc.name)!=desc.deflt.dimension) { stp_puts("[{\n", v); stp_zprintf(v, "%%%%IncludeFeature: *%s %f\n", desc.name, stp_get_dimension_parameter(v, desc.name)); stp_puts("} stopped cleartomark\n", v); } break; default: break; } } stp_parameter_description_destroy(&desc); } stp_puts("%%EndSetup\n", v); stp_parameter_list_destroy(param_list); } /* * 'ps_print()' - Print an image to a PostScript printer. */ static int ps_print_internal(stp_vars_t *v, stp_image_t *image) { int status = 1; int model = stp_get_model_id(v); const char *print_mode = stp_get_string_parameter(v, "PrintingMode"); const char *input_image_type = stp_get_string_parameter(v, "InputImageType"); unsigned short *out = NULL; stp_dimension_t top = stp_get_top(v); stp_dimension_t left = stp_get_left(v); int y; /* Looping vars */ stp_dimension_t page_left, /* Left margin of page */ page_right, /* Right margin of page */ page_top, /* Top of page */ page_bottom, /* Bottom of page */ page_width, /* Width of page */ page_height, /* Height of page */ paper_width, /* Width of physical page */ paper_height; /* Height of physical page */ int out_width, /* Width of image on page */ out_height, /* Height of image on page */ out_channels, /* Output bytes per pixel */ out_ps_height, /* Output height (Level 2 output) */ out_offset; /* Output offset (Level 2 output) */ time_t curtime; /* Current time of day */ unsigned zero_mask; int image_height, image_width; int color_out = 0; int cmyk_out = 0; if (print_mode && strcmp(print_mode, "Color") == 0) color_out = 1; if (color_out && input_image_type && (strcmp(input_image_type, "CMYK") == 0 || strcmp(input_image_type, "KCMY") == 0)) cmyk_out = 1; stp_image_init(image); /* * Compute the output size... */ out_width = stp_get_width(v); out_height = stp_get_height(v); ps_imageable_area_internal(v, 0, &page_left, &page_right, &page_bottom, &page_top); ps_media_size_internal(v, &paper_width, &paper_height); page_width = page_right - page_left; page_height = page_bottom - page_top; image_height = stp_image_height(image); image_width = stp_image_width(image); /* * Output a standard PostScript header with DSC comments... */ curtime = stpi_time(NULL); top = paper_height - top; stp_dprintf(STP_DBG_PS, v, "out_width = %d, out_height = %d\n", out_width, out_height); stp_dprintf(STP_DBG_PS, v, "page_left = %f, page_right = %f, page_bottom = %f, page_top = %f\n", page_left, page_right, page_bottom, page_top); stp_dprintf(STP_DBG_PS, v, "left = %f, top = %f\n", left, top); stp_dprintf(STP_DBG_PS, v, "page_width = %f, page_height = %f\n", page_width, page_height); stp_dprintf(STP_DBG_PS, v, "bounding box l %f b %f r %f t %f\n", page_left, paper_height - page_bottom, page_right, paper_height - page_top); stp_puts("%!PS-Adobe-3.0\n", v); #ifdef HAVE_CONFIG_H stp_zprintf(v, "%%%%Creator: %s/Gutenprint %s (%s)\n", stp_image_get_appname(image), VERSION, RELEASE_DATE); #else stp_zprintf(v, "%%%%Creator: %s/Gutenprint\n", stp_image_get_appname(image)); #endif stp_zprintf(v, "%%%%CreationDate: %s", ctime(&curtime)); stp_zprintf(v, "%%%%BoundingBox: %f %f %f %f\n", page_left, paper_height - page_bottom, page_right, paper_height - page_top); stp_puts("%%DocumentData: Clean7Bit\n", v); stp_zprintf(v, "%%%%LanguageLevel: %d\n", model + 1); stp_puts("%%Pages: 1\n", v); stp_puts("%%Orientation: Portrait\n", v); stp_puts("%%EndComments\n", v); ps_print_device_settings(v); /* * Output the page... */ stp_puts("%%Page: 1 1\n", v); stp_puts("gsave\n", v); stp_zprintf(v, "%f %f translate\n", left, top); /* Force locale to "C", because decimal numbers in Postscript must always be printed with a decimal point rather than the locale-specific setting. */ stp_zprintf(v, "%.3f %.3f scale\n", (double)out_width / ((double)image_width), (double)out_height / ((double)image_height)); stp_channel_reset(v); stp_channel_add(v, 0, 0, 1.0); if (color_out) { stp_channel_add(v, 1, 0, 1.0); stp_channel_add(v, 2, 0, 1.0); if (cmyk_out) { stp_channel_add(v, 3, 0, 1.0); stp_set_string_parameter(v, "STPIOutputType", "CMYK"); } else stp_set_string_parameter(v, "STPIOutputType", "RGB"); } else stp_set_string_parameter(v, "STPIOutputType", "Whitescale"); stp_set_boolean_parameter(v, "SimpleGamma", 1); out_channels = stp_color_init(v, image, 256); if (model == 0) { stp_zprintf(v, "/picture %d string def\n", image_width * out_channels); stp_zprintf(v, "%d %d 8\n", image_width, image_height); stp_puts("[ 1 0 0 -1 0 1 ]\n", v); if (cmyk_out) stp_puts("{currentfile picture readhexstring pop} false 4 colorimage\n", v); else if (color_out) stp_puts("{currentfile picture readhexstring pop} false 3 colorimage\n", v); else stp_puts("{currentfile picture readhexstring pop} image\n", v); for (y = 0; y < image_height; y ++) { if (stp_color_get_row(v, image, y, &zero_mask)) { status = 2; break; } out = stp_channel_get_input(v); /* Convert from KCMY to CMYK */ if (cmyk_out) { int x; unsigned short *pos = out; for (x = 0; x < image_width; x++, pos += 4) { unsigned short p0 = pos[0]; pos[0] = pos[1]; pos[1] = pos[2]; pos[2] = pos[3]; pos[3] = p0; } } ps_hex(v, out, image_width * out_channels); } } else { unsigned short *tmp_buf = stp_malloc(sizeof(unsigned short) * (image_width * out_channels + 4)); if (cmyk_out) stp_puts("/DeviceCMYK setcolorspace\n", v); else if (color_out) stp_puts("/DeviceRGB setcolorspace\n", v); else stp_puts("/DeviceGray setcolorspace\n", v); stp_puts("<<\n", v); stp_puts("\t/ImageType 1\n", v); stp_zprintf(v, "\t/Width %d\n", image_width); stp_zprintf(v, "\t/Height %d\n", image_height); stp_puts("\t/BitsPerComponent 8\n", v); if (cmyk_out) stp_puts("\t/Decode [ 0 1 0 1 0 1 0 1 ]\n", v); else if (color_out) stp_puts("\t/Decode [ 0 1 0 1 0 1 ]\n", v); else stp_puts("\t/Decode [ 0 1 ]\n", v); stp_puts("\t/DataSource currentfile /ASCII85Decode filter\n", v); if ((image_width * 72 / out_width) < 100) stp_puts("\t/Interpolate true\n", v); stp_puts("\t/ImageMatrix [ 1 0 0 -1 0 1 ]\n", v); stp_puts(">>\n", v); stp_puts("image\n", v); for (y = 0, out_offset = 0; y < image_height; y ++) { unsigned short *where; /* FIXME!!! */ if (stp_color_get_row(v, image, y /*, out + out_offset */ , &zero_mask)) { status = 2; break; } out = stp_channel_get_input(v); if (out_offset > 0) { memcpy(tmp_buf + out_offset, out, image_width * out_channels * sizeof(unsigned short)); where = tmp_buf; } else where = out; /* Convert from KCMY to CMYK */ if (cmyk_out) { int x; unsigned short *pos = where; for (x = 0; x < image_width; x++, pos += 4) { unsigned short p0 = pos[0]; pos[0] = pos[1]; pos[1] = pos[2]; pos[2] = pos[3]; pos[3] = p0; } } out_ps_height = out_offset + image_width * out_channels; if (y < (image_height - 1)) { ps_ascii85(v, where, out_ps_height & ~3, 0); out_offset = out_ps_height & 3; } else { ps_ascii85(v, where, out_ps_height, 1); out_offset = 0; } if (out_offset > 0) memcpy(tmp_buf, where + out_ps_height - out_offset, out_offset * sizeof(unsigned short)); } stp_free(tmp_buf); } stp_image_conclude(image); stp_puts("grestore\n", v); stp_puts("showpage\n", v); stp_puts("%%Trailer\n", v); stp_puts("%%EOF\n", v); return status; } static int ps_print(const stp_vars_t *v, stp_image_t *image) { int status; #ifdef HAVE_LOCALE_H char *locale; #endif stp_vars_t *nv = stp_vars_create_copy(v); if (!stp_verify(nv)) { stp_eprintf(nv, "Print options not verified; cannot print.\n"); return 0; } #ifdef HAVE_LOCALE_H locale = stp_strdup(setlocale(LC_ALL, NULL)); setlocale(LC_ALL, "C"); #endif status = ps_print_internal(nv, image); #ifdef HAVE_LOCALE_H setlocale(LC_ALL, locale); stp_free(locale); #endif stp_vars_destroy(nv); return status; } /* * 'ps_hex()' - Print binary data as a series of hexadecimal numbers. */ static void ps_hex(const stp_vars_t *v, /* I - File to print to */ unsigned short *data, /* I - Data to print */ int length) /* I - Number of bytes to print */ { int col; /* Current column */ static const char *hex = "0123456789ABCDEF"; col = 0; while (length > 0) { unsigned char pixel = (*data & 0xff00) >> 8; /* * Put the hex chars out to the file; note that we don't use stp_zprintf() * for speed reasons... */ stp_putc(hex[pixel >> 4], v); stp_putc(hex[pixel & 15], v); data ++; length --; col += 2; if (col >= 72) { col = 0; stp_putc('\n', v); } } if (col > 0) stp_putc('\n', v); } /* * 'ps_ascii85()' - Print binary data as a series of base-85 numbers. */ static void ps_ascii85(const stp_vars_t *v, /* I - File to print to */ unsigned short *data, /* I - Data to print */ int length, /* I - Number of bytes to print */ int last_line) /* I - Last line of raster data? */ { int i; /* Looping var */ unsigned b; /* Binary data word */ unsigned char c[5]; /* ASCII85 encoded chars */ static int column = 0; /* Current column */ #define OUTBUF_SIZE 4096 unsigned char outbuffer[OUTBUF_SIZE+10]; int outp=0; while (length > 3) { unsigned char d0 = (data[0] & 0xff00) >> 8; unsigned char d1 = (data[1] & 0xff00) >> 8; unsigned char d2 = (data[2] & 0xff00) >> 8; unsigned char d3 = (data[3] & 0xff00) >> 8; b = (((((d0 << 8) | d1) << 8) | d2) << 8) | d3; if (b == 0) { outbuffer[outp++]='z'; column ++; } else { outbuffer[outp+4] = (b % 85) + '!'; b /= 85; outbuffer[outp+3] = (b % 85) + '!'; b /= 85; outbuffer[outp+2] = (b % 85) + '!'; b /= 85; outbuffer[outp+1] = (b % 85) + '!'; b /= 85; outbuffer[outp] = b + '!'; outp+=5; column += 5; } if (column > 72) { outbuffer[outp++]='\n'; column = 0; } if(outp>=OUTBUF_SIZE) { stp_zfwrite((const char *)outbuffer, outp, 1, v); outp=0; } data += 4; length -= 4; } if(outp) stp_zfwrite((const char *)outbuffer, outp, 1, v); if (last_line) { if (length > 0) { for (b = 0, i = length; i > 0; b = (b << 8) | data[0], data ++, i --); c[4] = (b % 85) + '!'; b /= 85; c[3] = (b % 85) + '!'; b /= 85; c[2] = (b % 85) + '!'; b /= 85; c[1] = (b % 85) + '!'; b /= 85; c[0] = b + '!'; stp_zfwrite((const char *)c, length + 1, 1, v); } stp_puts("~>\n", v); column = 0; } } static const stp_printfuncs_t print_ps_printfuncs = { ps_list_parameters, ps_parameters, ps_media_size, ps_imageable_area, ps_maximum_imageable_area, ps_limit, ps_print, ps_describe_resolution, ps_describe_output, stp_verify_printer_params, NULL, NULL, ps_external_options, ps_describe_papersize }; static stp_family_t print_ps_module_data = { &print_ps_printfuncs, NULL }; static int print_ps_module_init(void) { return stpi_family_register(print_ps_module_data.printer_list); } static int print_ps_module_exit(void) { return stpi_family_unregister(print_ps_module_data.printer_list); } /* Module header */ #define stp_module_version print_ps_LTX_stp_module_version #define stp_module_data print_ps_LTX_stp_module_data stp_module_version_t stp_module_version = {0, 0}; stp_module_t stp_module_data = { "ps", VERSION, "Postscript family driver", STP_MODULE_CLASS_FAMILY, NULL, print_ps_module_init, print_ps_module_exit, (void *) &print_ps_module_data };