1/* 2 * "$Id: print-ps.c,v 1.104 2011/03/04 13:05:08 rlk Exp $" 3 * 4 * Print plug-in Adobe PostScript driver for the GIMP. 5 * 6 * Copyright 1997-2002 Michael Sweet (mike@easysw.com) and 7 * Robert Krawitz (rlk@alum.mit.edu) 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the Free 11 * Software Foundation; either version 2 of the License, or (at your option) 12 * any later version. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17 * for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22 */ 23 24/* 25 * This file must include only standard C header files. The core code must 26 * compile on generic platforms that don't support glib, gimp, gtk, etc. 27 */ 28 29#ifdef HAVE_CONFIG_H 30#include <config.h> 31#endif 32#include <gutenprint/gutenprint.h> 33#include <gutenprint/gutenprint-intl-internal.h> 34#include "gutenprint-internal.h" 35#include <time.h> 36#include <string.h> 37#include <math.h> 38#ifdef HAVE_LIMITS_H 39#include <limits.h> 40#endif 41#include <stdio.h> 42#include <unistd.h> 43#include "xmlppd.h" 44 45#ifdef _MSC_VER 46#define strncasecmp(s,t,n) _strnicmp(s,t,n) 47#define strcasecmp(s,t) _stricmp(s,t) 48#endif 49 50/* 51 * Local variables... 52 */ 53 54static char *m_ppd_file = NULL; 55static stp_mxml_node_t *m_ppd = NULL; 56 57 58/* 59 * Local functions... 60 */ 61 62static void ps_hex(const stp_vars_t *, unsigned short *, int); 63static void ps_ascii85(const stp_vars_t *, unsigned short *, int, int); 64 65static const stp_parameter_t the_parameters[] = 66{ 67 { 68 "PPDFile", N_("PPDFile"), "Color=Yes,Category=Basic Printer Setup", 69 N_("PPD File"), 70 STP_PARAMETER_TYPE_FILE, STP_PARAMETER_CLASS_FEATURE, 71 STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0 72 }, 73 { 74 "PageSize", N_("Page Size"), "Color=No,Category=Basic Printer Setup", 75 N_("Size of the paper being printed to"), 76 STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE, 77 STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0 78 }, 79 { 80 "ModelName", N_("Model Name"), "Color=Yes,Category=Basic Printer Setup", 81 N_("PPD File Model Name"), 82 STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE, 83 STP_PARAMETER_LEVEL_INTERNAL, 0, 0, STP_CHANNEL_NONE, 0, 0 84 }, 85 { 86 "PrintingMode", N_("Printing Mode"), "Color=Yes,Category=Core Parameter", 87 N_("Printing Output Mode"), 88 STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE, 89 STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0 90 }, 91}; 92 93static const int the_parameter_count = 94sizeof(the_parameters) / sizeof(const stp_parameter_t); 95 96static int 97ps_option_to_param(stp_parameter_t *param, stp_mxml_node_t *option) 98{ 99 const char *group_text = stp_mxmlElementGetAttr(option, "grouptext"); 100 101 if (group_text != NULL) 102 param->category = group_text; 103 else 104 param->category = NULL; 105 106 param->text = stp_mxmlElementGetAttr(option, "text"); 107 param->help = stp_mxmlElementGetAttr(option, "text"); 108 if (stp_mxmlElementGetAttr(option, "stptype")) 109 { 110 const char *default_value = stp_mxmlElementGetAttr(option, "default"); 111 double stp_default_value = strtod(stp_mxmlElementGetAttr(option, "stpdefault"), 0); 112 double lower_bound = strtod(stp_mxmlElementGetAttr(option, "stplower"), NULL); 113 double upper_bound = strtod(stp_mxmlElementGetAttr(option, "stpupper"), NULL); 114 param->p_type = atoi(stp_mxmlElementGetAttr(option, "stptype")); 115 param->is_mandatory = atoi(stp_mxmlElementGetAttr(option, "stpmandatory")); 116 param->p_class = atoi(stp_mxmlElementGetAttr(option, "stpclass")); 117 param->p_level = atoi(stp_mxmlElementGetAttr(option, "stplevel")); 118 param->channel = (unsigned char) atoi(stp_mxmlElementGetAttr(option, "stpchannel")); 119 param->read_only = 0; 120 param->is_active = 1; 121 param->verify_this_parameter = 1; 122 param->name = stp_mxmlElementGetAttr(option, "stpname"); 123 stp_deprintf(STP_DBG_PS, 124 "Gutenprint parameter %s type %d mandatory %d class %d level %d channel %d default %s %f", 125 param->name, param->p_type, param->is_mandatory, 126 param->p_class, param->p_level, param->channel, 127 default_value, stp_default_value); 128 switch (param->p_type) 129 { 130 case STP_PARAMETER_TYPE_DOUBLE: 131 param->deflt.dbl = stp_default_value; 132 param->bounds.dbl.upper = upper_bound; 133 param->bounds.dbl.lower = lower_bound; 134 stp_deprintf(STP_DBG_PS, " %.3f %.3f %.3f\n", 135 param->deflt.dbl, param->bounds.dbl.upper, 136 param->bounds.dbl.lower); 137 break; 138 case STP_PARAMETER_TYPE_DIMENSION: 139 param->deflt.dimension = atoi(default_value); 140 param->bounds.dimension.upper = (int) upper_bound; 141 param->bounds.dimension.lower = (int) lower_bound; 142 stp_deprintf(STP_DBG_PS, " %d %d %d\n", 143 param->deflt.dimension, param->bounds.dimension.upper, 144 param->bounds.dimension.lower); 145 break; 146 case STP_PARAMETER_TYPE_INT: 147 param->deflt.integer = atoi(default_value); 148 param->bounds.integer.upper = (int) upper_bound; 149 param->bounds.integer.lower = (int) lower_bound; 150 stp_deprintf(STP_DBG_PS, " %d %d %d\n", 151 param->deflt.integer, param->bounds.integer.upper, 152 param->bounds.integer.lower); 153 break; 154 case STP_PARAMETER_TYPE_BOOLEAN: 155 param->deflt.boolean = strcasecmp(default_value, "true") == 0 ? 1 : 0; 156 stp_deprintf(STP_DBG_PS, " %d\n", param->deflt.boolean); 157 break; 158 default: 159 stp_deprintf(STP_DBG_PS, "\n"); 160 break; 161 } 162 } 163 else 164 { 165 const char *ui = stp_mxmlElementGetAttr(option, "ui"); 166 param->name = stp_mxmlElementGetAttr(option, "name"); 167 if (strcasecmp(ui, "Boolean") == 0) 168 param->p_type = STP_PARAMETER_TYPE_BOOLEAN; 169 else 170 param->p_type = STP_PARAMETER_TYPE_STRING_LIST; 171 if (strcmp(param->name, "PageSize") == 0) 172 param->p_class = STP_PARAMETER_CLASS_CORE; 173 else 174 param->p_class = STP_PARAMETER_CLASS_FEATURE; 175 param->p_level = STP_PARAMETER_LEVEL_BASIC; 176 param->is_mandatory = 1; 177 param->is_active = 1; 178 param->channel = -1; 179 param->verify_this_parameter = 1; 180 param->read_only = 0; 181 } 182 183 return 0; 184} 185 186/* 187 * 'ps_parameters()' - Return the parameter values for the given parameter. 188 */ 189 190static int 191ppd_whitespace_callback(stp_mxml_node_t *node, int where) 192{ 193 return 0; 194} 195 196static int 197check_ppd_file(const stp_vars_t *v) 198{ 199 const char *ppd_file = stp_get_file_parameter(v, "PPDFile"); 200 201 if (ppd_file == NULL || ppd_file[0] == 0) 202 { 203 stp_dprintf(STP_DBG_PS, v, "Empty PPD file\n"); 204 return 0; 205 } 206 else if (m_ppd_file && strcmp(m_ppd_file, ppd_file) == 0) 207 { 208 stp_dprintf(STP_DBG_PS, v, "Not replacing PPD file %s\n", m_ppd_file); 209 return 1; 210 } 211 else 212 { 213 stp_dprintf(STP_DBG_PS, v, "Replacing PPD file %s with %s\n", 214 m_ppd_file ? m_ppd_file : "(null)", 215 ppd_file ? ppd_file : "(null)"); 216 if (m_ppd != NULL) 217 stp_mxmlDelete(m_ppd); 218 m_ppd = NULL; 219 220 if (m_ppd_file) 221 stp_free(m_ppd_file); 222 m_ppd_file = NULL; 223 224 if ((m_ppd = stpi_xmlppd_read_ppd_file(ppd_file)) == NULL) 225 { 226 stp_eprintf(v, "Unable to open PPD file %s\n", ppd_file); 227 return 0; 228 } 229 if (stp_get_debug_level() & STP_DBG_PS) 230 { 231 char *ppd_stuff = stp_mxmlSaveAllocString(m_ppd, ppd_whitespace_callback); 232 stp_dprintf(STP_DBG_PS, v, "%s", ppd_stuff); 233 stp_free(ppd_stuff); 234 } 235 236 m_ppd_file = stp_strdup(ppd_file); 237 return 1; 238 } 239} 240 241 242static stp_parameter_list_t 243ps_list_parameters(const stp_vars_t *v) 244{ 245 stp_parameter_list_t *ret = stp_parameter_list_create(); 246 stp_mxml_node_t *option; 247 int i; 248 int status = check_ppd_file(v); 249 stp_dprintf(STP_DBG_PS, v, "Adding parameters from %s (%d)\n", 250 m_ppd_file ? m_ppd_file : "(null)", status); 251 252 for (i = 0; i < the_parameter_count; i++) 253 stp_parameter_list_add_param(ret, &(the_parameters[i])); 254 255 if (status) 256 { 257 int num_options = stpi_xmlppd_find_option_count(m_ppd); 258 stp_dprintf(STP_DBG_PS, v, "Found %d parameters\n", num_options); 259 for (i=0; i < num_options; i++) 260 { 261 /* MEMORY LEAK!!! */ 262 stp_parameter_t *param = stp_malloc(sizeof(stp_parameter_t)); 263 option = stpi_xmlppd_find_option_index(m_ppd, i); 264 if (option) 265 { 266 ps_option_to_param(param, option); 267 if (param->p_type != STP_PARAMETER_TYPE_INVALID && 268 strcmp(param->name, "PageRegion") != 0 && 269 strcmp(param->name, "PageSize") != 0) 270 { 271 stp_dprintf(STP_DBG_PS, v, "Adding parameter %s %s\n", 272 param->name, param->text); 273 stp_parameter_list_add_param(ret, param); 274 } 275 else 276 stp_free(param); 277 } 278 } 279 } 280 return ret; 281} 282 283static void 284ps_parameters_internal(const stp_vars_t *v, const char *name, 285 stp_parameter_t *description) 286{ 287 int i; 288 stp_mxml_node_t *option; 289 int status = 0; 290 int num_choices; 291 const char *defchoice; 292 293 description->p_type = STP_PARAMETER_TYPE_INVALID; 294 description->deflt.str = 0; 295 description->is_active = 0; 296 297 if (name == NULL) 298 return; 299 300 status = check_ppd_file(v); 301 302 for (i = 0; i < the_parameter_count; i++) 303 { 304 if (strcmp(name, the_parameters[i].name) == 0) 305 { 306 stp_fill_parameter_settings(description, &(the_parameters[i])); 307 if (strcmp(name, "PPDFile") == 0) 308 description->is_active = 1; 309 else if (strcmp(name, "ModelName") == 0) 310 { 311 const char *nickname; 312 description->bounds.str = stp_string_list_create(); 313 if (m_ppd && stp_mxmlElementGetAttr(m_ppd, "nickname")) 314 nickname = stp_mxmlElementGetAttr(m_ppd, "nickname"); 315 else 316 nickname = _("None; please provide a PPD file"); 317 stp_string_list_add_string(description->bounds.str, 318 nickname, nickname); 319 description->deflt.str = nickname; 320 description->is_active = 1; 321 return; 322 } 323 else if (strcmp(name, "PrintingMode") == 0) 324 { 325 if (! m_ppd || strcmp(stp_mxmlElementGetAttr(m_ppd, "color"), "1") == 0) 326 { 327 description->bounds.str = stp_string_list_create(); 328 stp_string_list_add_string 329 (description->bounds.str, "Color", _("Color")); 330 stp_string_list_add_string 331 (description->bounds.str, "BW", _("Black and White")); 332 description->deflt.str = 333 stp_string_list_param(description->bounds.str, 0)->name; 334 description->is_active = 1; 335 } 336 else 337 description->is_active = 0; 338 return; 339 } 340 } 341 } 342 343 if (!status && strcmp(name, "PageSize") != 0) 344 return; 345 if ((option = stpi_xmlppd_find_option_named(m_ppd, name)) == NULL) 346 { 347 if (strcmp(name, "PageSize") == 0) 348 { 349 /* Provide a default set of page sizes */ 350 description->bounds.str = stp_string_list_create(); 351 stp_string_list_add_string 352 (description->bounds.str, "Letter", _("Letter")); 353 stp_string_list_add_string 354 (description->bounds.str, "A4", _("A4")); 355 stp_string_list_add_string 356 (description->bounds.str, "Custom", _("Custom")); 357 description->deflt.str = 358 stp_string_list_param(description->bounds.str, 0)->name; 359 description->is_active = 1; 360 return; 361 } 362 else 363 { 364 char *tmp = stp_malloc(strlen(name) + 4); 365 strcpy(tmp, "Stp"); 366 strncat(tmp, name, strlen(name) + 3); 367 if ((option = stpi_xmlppd_find_option_named(m_ppd, tmp)) == NULL) 368 { 369 stp_dprintf(STP_DBG_PS, v, "no parameter %s", name); 370 stp_free(tmp); 371 return; 372 } 373 stp_free(tmp); 374 } 375 } 376 377 ps_option_to_param(description, option); 378 if (description->p_type != STP_PARAMETER_TYPE_STRING_LIST) 379 return; 380 num_choices = atoi(stp_mxmlElementGetAttr(option, "num_choices")); 381 defchoice = stp_mxmlElementGetAttr(option, "default"); 382 description->bounds.str = stp_string_list_create(); 383 384 stp_dprintf(STP_DBG_PS, v, "describe parameter %s, output name=[%s] text=[%s] category=[%s] choices=[%d] default=[%s]\n", 385 name, description->name, description->text, 386 description->category, num_choices, defchoice); 387 388 /* Describe all choices for specified option. */ 389 for (i=0; i < num_choices; i++) 390 { 391 stp_mxml_node_t *choice = stpi_xmlppd_find_choice_index(option, i); 392 const char *choice_name = stp_mxmlElementGetAttr(choice, "name"); 393 const char *choice_text = stp_mxmlElementGetAttr(choice, "text"); 394 stp_string_list_add_string(description->bounds.str, choice_name, choice_text); 395 stp_dprintf(STP_DBG_PS, v, " parameter %s, choice %d [%s] [%s]", 396 name, i, choice_name, choice_text); 397 if (strcmp(choice_name, defchoice) == 0) 398 { 399 stp_dprintf(STP_DBG_PS, v, 400 " parameter %s, choice %d [%s] DEFAULT\n", 401 name, i, choice_name); 402 description->deflt.str = choice_name; 403 } 404 } 405 406 if (!description->deflt.str) 407 { 408 stp_dprintf(STP_DBG_PS, v, 409 " parameter %s, defaulting to [%s]", 410 name, stp_string_list_param(description->bounds.str, 0)->name); 411 description->deflt.str = stp_string_list_param(description->bounds.str, 0)->name; 412 } 413 if (stp_string_list_count(description->bounds.str) > 0) 414 description->is_active = 1; 415 return; 416} 417 418static void 419ps_parameters(const stp_vars_t *v, const char *name, 420 stp_parameter_t *description) 421{ 422#ifdef HAVE_LOCALE_H 423 char *locale = stp_strdup(setlocale(LC_ALL, NULL)); 424 setlocale(LC_ALL, "C"); 425#endif 426 ps_parameters_internal(v, name, description); 427#ifdef HAVE_LOCALE_H 428 setlocale(LC_ALL, locale); 429 stp_free(locale); 430#endif 431} 432 433/* 434 * 'ps_media_size()' - Return the size of the page. 435 */ 436 437static void 438ps_media_size_internal(const stp_vars_t *v, /* I */ 439 int *width, /* O - Width in points */ 440 int *height) /* O - Height in points */ 441{ 442 const char *pagesize = stp_get_string_parameter(v, "PageSize"); 443 int status = check_ppd_file(v); 444 if (!pagesize) 445 pagesize = ""; 446 447 stp_dprintf(STP_DBG_PS, v, 448 "ps_media_size(%d, \'%s\', \'%s\', %p, %p)\n", 449 stp_get_model_id(v), m_ppd_file, pagesize, 450 (void *) width, (void *) height); 451 452 stp_default_media_size(v, width, height); 453 454 if (status) 455 { 456 stp_mxml_node_t *paper = stpi_xmlppd_find_page_size(m_ppd, pagesize); 457 if (paper) 458 { 459 *width = atoi(stp_mxmlElementGetAttr(paper, "width")); 460 *height = atoi(stp_mxmlElementGetAttr(paper, "height")); 461 } 462 else 463 { 464 *width = 0; 465 *height = 0; 466 } 467 } 468 469 stp_dprintf(STP_DBG_PS, v, "dimensions %d %d\n", *width, *height); 470 return; 471} 472 473static void 474ps_media_size(const stp_vars_t *v, int *width, int *height) 475{ 476#ifdef HAVE_LOCALE_H 477 char *locale = stp_strdup(setlocale(LC_ALL, NULL)); 478 setlocale(LC_ALL, "C"); 479#endif 480 ps_media_size_internal(v, width, height); 481#ifdef HAVE_LOCALE_H 482 setlocale(LC_ALL, locale); 483 stp_free(locale); 484#endif 485} 486 487/* 488 * 'ps_imageable_area()' - Return the imageable area of the page. 489 */ 490 491static void 492ps_imageable_area_internal(const stp_vars_t *v, /* I */ 493 int use_max_area, /* I - Use maximum area */ 494 int *left, /* O - Left position in points */ 495 int *right, /* O - Right position in points */ 496 int *bottom, /* O - Bottom position in points */ 497 int *top) /* O - Top position in points */ 498{ 499 int width, height; 500 const char *pagesize = stp_get_string_parameter(v, "PageSize"); 501 if (!pagesize) 502 pagesize = ""; 503 504 /* Set some defaults. */ 505 ps_media_size_internal(v, &width, &height); 506 *left = 0; 507 *right = width; 508 *top = 0; 509 *bottom = height; 510 511 if (check_ppd_file(v)) 512 { 513 stp_mxml_node_t *paper = stpi_xmlppd_find_page_size(m_ppd, pagesize); 514 if (paper) 515 { 516 double pleft = atoi(stp_mxmlElementGetAttr(paper, "left")); 517 double pright = atoi(stp_mxmlElementGetAttr(paper, "right")); 518 double ptop = atoi(stp_mxmlElementGetAttr(paper, "top")); 519 double pbottom = atoi(stp_mxmlElementGetAttr(paper, "bottom")); 520 stp_dprintf(STP_DBG_PS, v, "size=l %f r %f b %f t %f h %d w %d\n", 521 pleft, pright, pbottom, ptop, height, width); 522 *left = (int) pleft; 523 *right = (int) pright; 524 *top = height - (int) ptop; 525 *bottom = height - (int) pbottom; 526 stp_dprintf(STP_DBG_PS, v, ">>>> l %d r %d b %d t %d h %d w %d\n", 527 *left, *right, *bottom, *top, height, width); 528 } 529 } 530 531 if (use_max_area) 532 { 533 if (*left > 0) 534 *left = 0; 535 if (*right < width) 536 *right = width; 537 if (*top > 0) 538 *top = 0; 539 if (*bottom < height) 540 *bottom = height; 541 } 542 543 stp_dprintf(STP_DBG_PS, v, "pagesize %s max_area=%d l %d r %d b %d t %d h %d w %d\n", 544 pagesize ? pagesize : "(null)", 545 use_max_area, *left, *right, *bottom, *top, width, height); 546 547 return; 548} 549 550static void 551ps_imageable_area(const stp_vars_t *v, /* I */ 552 int *left, /* O - Left position in points */ 553 int *right, /* O - Right position in points */ 554 int *bottom, /* O - Bottom position in points */ 555 int *top) /* O - Top position in points */ 556{ 557#ifdef HAVE_LOCALE_H 558 char *locale = stp_strdup(setlocale(LC_ALL, NULL)); 559 setlocale(LC_ALL, "C"); 560#endif 561 ps_imageable_area_internal(v, 0, left, right, bottom, top); 562#ifdef HAVE_LOCALE_H 563 setlocale(LC_ALL, locale); 564 stp_free(locale); 565#endif 566} 567 568static void 569ps_maximum_imageable_area(const stp_vars_t *v, /* I */ 570 int *left, /* O - Left position in points */ 571 int *right, /* O - Right position in points */ 572 int *bottom, /* O - Bottom position in points */ 573 int *top) /* O - Top position in points */ 574{ 575#ifdef HAVE_LOCALE_H 576 char *locale = stp_strdup(setlocale(LC_ALL, NULL)); 577 setlocale(LC_ALL, "C"); 578#endif 579 ps_imageable_area_internal(v, 1, left, right, bottom, top); 580#ifdef HAVE_LOCALE_H 581 setlocale(LC_ALL, locale); 582 stp_free(locale); 583#endif 584} 585 586static void 587ps_limit(const stp_vars_t *v, /* I */ 588 int *width, 589 int *height, 590 int *min_width, 591 int *min_height) 592{ 593 *width = INT_MAX; 594 *height = INT_MAX; 595 *min_width = 1; 596 *min_height = 1; 597} 598 599/* 600 * This is really bogus... 601 */ 602static void 603ps_describe_resolution_internal(const stp_vars_t *v, int *x, int *y) 604{ 605 const char *resolution = stp_get_string_parameter(v, "Resolution"); 606 *x = -1; 607 *y = -1; 608 if (resolution) 609 sscanf(resolution, "%dx%d", x, y); 610 return; 611} 612 613static void 614ps_describe_resolution(const stp_vars_t *v, int *x, int *y) 615{ 616#ifdef HAVE_LOCALE_H 617 char *locale = stp_strdup(setlocale(LC_ALL, NULL)); 618 setlocale(LC_ALL, "C"); 619#endif 620 ps_describe_resolution_internal(v, x, y); 621#ifdef HAVE_LOCALE_H 622 setlocale(LC_ALL, locale); 623 stp_free(locale); 624#endif 625} 626 627static const char * 628ps_describe_output(const stp_vars_t *v) 629{ 630 const char *print_mode = stp_get_string_parameter(v, "PrintingMode"); 631 const char *input_image_type = stp_get_string_parameter(v, "InputImageType"); 632 if (print_mode && strcmp(print_mode, "Color") == 0) 633 { 634 if (input_image_type && (strcmp(input_image_type, "CMYK") == 0 || 635 strcmp(input_image_type, "KCMY") == 0)) 636 return "CMYK"; 637 else 638 return "RGB"; 639 } 640 else 641 return "Whitescale"; 642} 643 644static stp_string_list_t * 645ps_external_options(const stp_vars_t *v) 646{ 647 stp_parameter_list_t param_list = ps_list_parameters(v); 648 stp_string_list_t *answer; 649 char *tmp; 650 char *ppd_name = NULL; 651 int i; 652#ifdef HAVE_LOCALE_H 653 char *locale; 654#endif 655 if (! param_list) 656 return NULL; 657 answer = stp_string_list_create(); 658#ifdef HAVE_LOCALE_H 659 locale = stp_strdup(setlocale(LC_ALL, NULL)); 660 setlocale(LC_ALL, "C"); 661#endif 662 for (i = 0; i < stp_parameter_list_count(param_list); i++) 663 { 664 const stp_parameter_t *param = stp_parameter_list_param(param_list, i); 665 stp_parameter_t desc; 666 stp_describe_parameter(v, param->name, &desc); 667 if (desc.is_active) 668 { 669 stp_mxml_node_t *option; 670 if (m_ppd && 671 (option = stpi_xmlppd_find_option_named(m_ppd, desc.name)) == NULL) 672 { 673 ppd_name = stp_malloc(strlen(desc.name) + 4); 674 strcpy(ppd_name, "Stp"); 675 strncat(ppd_name, desc.name, strlen(desc.name) + 3); 676 if ((option = stpi_xmlppd_find_option_named(m_ppd, ppd_name)) == NULL) 677 { 678 stp_dprintf(STP_DBG_PS, v, "no parameter %s", desc.name); 679 STP_SAFE_FREE(ppd_name); 680 } 681 } 682 switch (desc.p_type) 683 { 684 case STP_PARAMETER_TYPE_STRING_LIST: 685 if (stp_get_string_parameter(v, desc.name) && 686 strcmp(stp_get_string_parameter(v, desc.name), 687 desc.deflt.str)) 688 { 689 stp_dprintf(STP_DBG_PS, v, "Adding string parameter %s (%s): %s %s\n", 690 desc.name, ppd_name ? ppd_name : "(null)", 691 stp_get_string_parameter(v, desc.name), 692 desc.deflt.str); 693 stp_string_list_add_string(answer, 694 ppd_name ? ppd_name : desc.name, 695 stp_get_string_parameter(v, desc.name)); 696 } 697 break; 698 case STP_PARAMETER_TYPE_INT: 699 if (stp_get_int_parameter(v, desc.name) != desc.deflt.integer) 700 { 701 stp_dprintf(STP_DBG_PS, v, "Adding integer parameter %s (%s): %d %d\n", 702 desc.name, ppd_name ? ppd_name : "(null)", 703 stp_get_int_parameter(v, desc.name), 704 desc.deflt.integer); 705 stp_asprintf(&tmp, "%d", stp_get_int_parameter(v, desc.name)); 706 stp_string_list_add_string(answer, 707 ppd_name ? ppd_name : desc.name, 708 tmp); 709 stp_free(tmp); 710 } 711 break; 712 case STP_PARAMETER_TYPE_BOOLEAN: 713 if (stp_get_boolean_parameter(v, desc.name) != desc.deflt.boolean) 714 { 715 stp_dprintf(STP_DBG_PS, v, "Adding boolean parameter %s (%s): %d %d\n", 716 desc.name, ppd_name ? ppd_name : "(null)", 717 stp_get_boolean_parameter(v, desc.name), 718 desc.deflt.boolean); 719 stp_asprintf(&tmp, "%s", 720 stp_get_boolean_parameter(v, desc.name) ? 721 "True" : "False"); 722 stp_string_list_add_string(answer, 723 ppd_name ? ppd_name : desc.name, 724 tmp); 725 stp_free(tmp); 726 } 727 break; 728 case STP_PARAMETER_TYPE_DOUBLE: 729 if (fabs(stp_get_float_parameter(v, desc.name) - desc.deflt.dbl) > .00001) 730 { 731 stp_dprintf(STP_DBG_PS, v, "Adding float parameter %s (%s): %.3f %.3f\n", 732 desc.name, ppd_name ? ppd_name : "(null)", 733 stp_get_float_parameter(v, desc.name), 734 desc.deflt.dbl); 735 stp_asprintf(&tmp, "%.3f", 736 stp_get_float_parameter(v, desc.name)); 737 stp_string_list_add_string(answer, 738 ppd_name ? ppd_name : desc.name, 739 tmp); 740 stp_free(tmp); 741 } 742 break; 743 case STP_PARAMETER_TYPE_DIMENSION: 744 if (stp_get_dimension_parameter(v, desc.name) != 745 desc.deflt.dimension) 746 { 747 stp_dprintf(STP_DBG_PS, v, "Adding dimension parameter %s (%s): %d %d\n", 748 desc.name, ppd_name ? ppd_name : "(null)", 749 stp_get_dimension_parameter(v, desc.name), 750 desc.deflt.dimension); 751 stp_asprintf(&tmp, "%d", 752 stp_get_dimension_parameter(v, desc.name)); 753 stp_string_list_add_string(answer, 754 ppd_name ? ppd_name : desc.name, 755 tmp); 756 stp_free(tmp); 757 } 758 break; 759 default: 760 break; 761 } 762 STP_SAFE_FREE(ppd_name); 763 } 764 stp_parameter_description_destroy(&desc); 765 } 766#ifdef HAVE_LOCALE_H 767 setlocale(LC_ALL, locale); 768 stp_free(locale); 769#endif 770 return answer; 771} 772 773/* 774 * 'ps_print_device_settings()' - output postscript code from PPD into the 775 * postscript stream. 776 */ 777 778static void 779ps_print_device_settings(stp_vars_t *v) 780{ 781 int i; 782 stp_parameter_list_t param_list = ps_list_parameters(v); 783 if (! param_list) 784 return; 785 stp_puts("%%BeginSetup\n", v); 786 for (i = 0; i < stp_parameter_list_count(param_list); i++) 787 { 788 const stp_parameter_t *param = stp_parameter_list_param(param_list, i); 789 stp_parameter_t desc; 790 stp_describe_parameter(v, param->name, &desc); 791 if (desc.is_active) 792 { 793 switch (desc.p_type) 794 { 795 case STP_PARAMETER_TYPE_STRING_LIST: 796 case STP_PARAMETER_TYPE_BOOLEAN: 797 { 798 const char *val=NULL; 799 const char *defval=NULL; 800 801 /* If this is a bool parameter, set val to "True" or "False" - otherwise fetch from string parameter. */ 802 if(desc.p_type==STP_PARAMETER_TYPE_BOOLEAN) 803 { 804 val=stp_get_boolean_parameter(v,desc.name) ? "True" : "False"; 805 defval=desc.deflt.boolean ? "True" : "False"; 806 } 807 else 808 { 809 val=stp_get_string_parameter(v,desc.name); 810 defval=desc.deflt.str; 811 } 812 813 /* We only include the option's code if it's set to a value other than the default. */ 814 if(val && defval && (strcmp(val,defval)!=0)) 815 { 816 if(m_ppd) 817 { 818 /* If we have a PPD xml tree we hunt for the appropriate "option" and "choice"... */ 819 stp_mxml_node_t *node=m_ppd; 820 node=stp_mxmlFindElement(node,node, "option", "name", desc.name, STP_MXML_DESCEND); 821 if(node) 822 { 823 node=stp_mxmlFindElement(node,node, "choice", "name", val, STP_MXML_DESCEND); 824 if(node) 825 { 826 if(node->child && node->child->value.opaque && (strlen(node->child->value.opaque)>1)) 827 { 828 /* If we have opaque data for the child, we use %%BeginFeature and copy the code verbatim. */ 829 stp_puts("[{\n", v); 830 stp_zprintf(v, "%%%%BeginFeature: *%s %s\n", desc.name, val); 831 if(node->child->value.opaque) 832 stp_puts(node->child->value.opaque,v); 833 stp_puts("\n%%EndFeature\n", v); 834 stp_puts("} stopped cleartomark\n", v); 835 } 836 else 837 { 838 /* If we don't have code, we use %%IncludeFeature instead. */ 839 stp_puts("[{\n", v); 840 stp_zprintf(v, "%%%%IncludeFeature: *%s %s\n", desc.name, val); 841 if(node->child->value.opaque) 842 stp_puts(node->child->value.opaque,v); 843 stp_puts("} stopped cleartomark\n", v); 844 } 845 } 846 } 847 } 848 } 849 } 850 break; 851 case STP_PARAMETER_TYPE_INT: 852 if(stp_get_int_parameter(v,desc.name)!=desc.deflt.integer) 853 { 854 stp_puts("[{\n", v); 855 stp_zprintf(v, "%%%%IncludeFeature: *%s %d\n", desc.name, 856 stp_get_int_parameter(v, desc.name)); 857 stp_puts("} stopped cleartomark\n", v); 858 } 859 break; 860 case STP_PARAMETER_TYPE_DOUBLE: 861 if(stp_get_float_parameter(v,desc.name)!=desc.deflt.dbl) 862 { 863 stp_puts("[{\n", v); 864 stp_zprintf(v, "%%%%IncludeFeature: *%s %f\n", desc.name, 865 stp_get_float_parameter(v, desc.name)); 866 stp_puts("} stopped cleartomark\n", v); 867 } 868 break; 869 case STP_PARAMETER_TYPE_DIMENSION: 870 if(stp_get_dimension_parameter(v,desc.name)!=desc.deflt.dimension) 871 { 872 stp_puts("[{\n", v); 873 stp_zprintf(v, "%%%%IncludeFeature: *%s %d\n", desc.name, 874 stp_get_dimension_parameter(v, desc.name)); 875 stp_puts("} stopped cleartomark\n", v); 876 } 877 break; 878 default: 879 break; 880 } 881 } 882 stp_parameter_description_destroy(&desc); 883 } 884 stp_puts("%%EndSetup\n", v); 885 stp_parameter_list_destroy(param_list); 886} 887 888/* 889 * 'ps_print()' - Print an image to a PostScript printer. 890 */ 891 892static int 893ps_print_internal(stp_vars_t *v, stp_image_t *image) 894{ 895 int status = 1; 896 int model = stp_get_model_id(v); 897 const char *print_mode = stp_get_string_parameter(v, "PrintingMode"); 898 const char *input_image_type = stp_get_string_parameter(v, "InputImageType"); 899 unsigned short *out = NULL; 900 int top = stp_get_top(v); 901 int left = stp_get_left(v); 902 int y; /* Looping vars */ 903 int page_left, /* Left margin of page */ 904 page_right, /* Right margin of page */ 905 page_top, /* Top of page */ 906 page_bottom, /* Bottom of page */ 907 page_width, /* Width of page */ 908 page_height, /* Height of page */ 909 paper_width, /* Width of physical page */ 910 paper_height, /* Height of physical page */ 911 out_width, /* Width of image on page */ 912 out_height, /* Height of image on page */ 913 out_channels, /* Output bytes per pixel */ 914 out_ps_height, /* Output height (Level 2 output) */ 915 out_offset; /* Output offset (Level 2 output) */ 916 time_t curtime; /* Current time of day */ 917 unsigned zero_mask; 918 int image_height, 919 image_width; 920 int color_out = 0; 921 int cmyk_out = 0; 922 923 if (print_mode && strcmp(print_mode, "Color") == 0) 924 color_out = 1; 925 if (color_out && 926 input_image_type && (strcmp(input_image_type, "CMYK") == 0 || 927 strcmp(input_image_type, "KCMY") == 0)) 928 cmyk_out = 1; 929 930 stp_image_init(image); 931 932 /* 933 * Compute the output size... 934 */ 935 936 out_width = stp_get_width(v); 937 out_height = stp_get_height(v); 938 939 ps_imageable_area_internal(v, 0, &page_left, &page_right, &page_bottom, &page_top); 940 ps_media_size_internal(v, &paper_width, &paper_height); 941 page_width = page_right - page_left; 942 page_height = page_bottom - page_top; 943 944 image_height = stp_image_height(image); 945 image_width = stp_image_width(image); 946 947 /* 948 * Output a standard PostScript header with DSC comments... 949 */ 950 951 curtime = time(NULL); 952 953 top = paper_height - top; 954 955 stp_dprintf(STP_DBG_PS, v, 956 "out_width = %d, out_height = %d\n", out_width, out_height); 957 stp_dprintf(STP_DBG_PS, v, 958 "page_left = %d, page_right = %d, page_bottom = %d, page_top = %d\n", 959 page_left, page_right, page_bottom, page_top); 960 stp_dprintf(STP_DBG_PS, v, "left = %d, top = %d\n", left, top); 961 stp_dprintf(STP_DBG_PS, v, "page_width = %d, page_height = %d\n", 962 page_width, page_height); 963 964 stp_dprintf(STP_DBG_PS, v, "bounding box l %d b %d r %d t %d\n", 965 page_left, paper_height - page_bottom, 966 page_right, paper_height - page_top); 967 968 stp_puts("%!PS-Adobe-3.0\n", v); 969#ifdef HAVE_CONFIG_H 970 stp_zprintf(v, "%%%%Creator: %s/Gutenprint %s (%s)\n", 971 stp_image_get_appname(image), VERSION, RELEASE_DATE); 972#else 973 stp_zprintf(v, "%%%%Creator: %s/Gutenprint\n", stp_image_get_appname(image)); 974#endif 975 stp_zprintf(v, "%%%%CreationDate: %s", ctime(&curtime)); 976 stp_zprintf(v, "%%%%BoundingBox: %d %d %d %d\n", 977 page_left, paper_height - page_bottom, 978 page_right, paper_height - page_top); 979 stp_puts("%%DocumentData: Clean7Bit\n", v); 980 stp_zprintf(v, "%%%%LanguageLevel: %d\n", model + 1); 981 stp_puts("%%Pages: 1\n", v); 982 stp_puts("%%Orientation: Portrait\n", v); 983 stp_puts("%%EndComments\n", v); 984 985 ps_print_device_settings(v); 986 987 /* 988 * Output the page... 989 */ 990 991 stp_puts("%%Page: 1 1\n", v); 992 stp_puts("gsave\n", v); 993 994 stp_zprintf(v, "%d %d translate\n", left, top); 995 996 /* Force locale to "C", because decimal numbers in Postscript must 997 always be printed with a decimal point rather than the 998 locale-specific setting. */ 999 1000 stp_zprintf(v, "%.3f %.3f scale\n", 1001 (double)out_width / ((double)image_width), 1002 (double)out_height / ((double)image_height)); 1003 1004 stp_channel_reset(v); 1005 stp_channel_add(v, 0, 0, 1.0); 1006 if (color_out) 1007 { 1008 stp_channel_add(v, 1, 0, 1.0); 1009 stp_channel_add(v, 2, 0, 1.0); 1010 if (cmyk_out) 1011 { 1012 stp_channel_add(v, 3, 0, 1.0); 1013 stp_set_string_parameter(v, "STPIOutputType", "CMYK"); 1014 } 1015 else 1016 stp_set_string_parameter(v, "STPIOutputType", "RGB"); 1017 } 1018 else 1019 stp_set_string_parameter(v, "STPIOutputType", "Whitescale"); 1020 1021 stp_set_boolean_parameter(v, "SimpleGamma", 1); 1022 1023 out_channels = stp_color_init(v, image, 256); 1024 1025 if (model == 0) 1026 { 1027 stp_zprintf(v, "/picture %d string def\n", image_width * out_channels); 1028 1029 stp_zprintf(v, "%d %d 8\n", image_width, image_height); 1030 1031 stp_puts("[ 1 0 0 -1 0 1 ]\n", v); 1032 1033 if (cmyk_out) 1034 stp_puts("{currentfile picture readhexstring pop} false 4 colorimage\n", v); 1035 else if (color_out) 1036 stp_puts("{currentfile picture readhexstring pop} false 3 colorimage\n", v); 1037 else 1038 stp_puts("{currentfile picture readhexstring pop} image\n", v); 1039 1040 for (y = 0; y < image_height; y ++) 1041 { 1042 if (stp_color_get_row(v, image, y, &zero_mask)) 1043 { 1044 status = 2; 1045 break; 1046 } 1047 1048 out = stp_channel_get_input(v); 1049 1050 /* Convert from KCMY to CMYK */ 1051 if (cmyk_out) 1052 { 1053 int x; 1054 unsigned short *pos = out; 1055 for (x = 0; x < image_width; x++, pos += 4) 1056 { 1057 unsigned short p0 = pos[0]; 1058 pos[0] = pos[1]; 1059 pos[1] = pos[2]; 1060 pos[2] = pos[3]; 1061 pos[3] = p0; 1062 } 1063 } 1064 ps_hex(v, out, image_width * out_channels); 1065 } 1066 } 1067 else 1068 { 1069 unsigned short *tmp_buf = 1070 stp_malloc(sizeof(unsigned short) * (image_width * out_channels + 4)); 1071 if (cmyk_out) 1072 stp_puts("/DeviceCMYK setcolorspace\n", v); 1073 else if (color_out) 1074 stp_puts("/DeviceRGB setcolorspace\n", v); 1075 else 1076 stp_puts("/DeviceGray setcolorspace\n", v); 1077 1078 stp_puts("<<\n", v); 1079 stp_puts("\t/ImageType 1\n", v); 1080 1081 stp_zprintf(v, "\t/Width %d\n", image_width); 1082 stp_zprintf(v, "\t/Height %d\n", image_height); 1083 stp_puts("\t/BitsPerComponent 8\n", v); 1084 1085 if (cmyk_out) 1086 stp_puts("\t/Decode [ 0 1 0 1 0 1 0 1 ]\n", v); 1087 else if (color_out) 1088 stp_puts("\t/Decode [ 0 1 0 1 0 1 ]\n", v); 1089 else 1090 stp_puts("\t/Decode [ 0 1 ]\n", v); 1091 1092 stp_puts("\t/DataSource currentfile /ASCII85Decode filter\n", v); 1093 1094 if ((image_width * 72 / out_width) < 100) 1095 stp_puts("\t/Interpolate true\n", v); 1096 1097 stp_puts("\t/ImageMatrix [ 1 0 0 -1 0 1 ]\n", v); 1098 1099 stp_puts(">>\n", v); 1100 stp_puts("image\n", v); 1101 1102 for (y = 0, out_offset = 0; y < image_height; y ++) 1103 { 1104 unsigned short *where; 1105 /* FIXME!!! */ 1106 if (stp_color_get_row(v, image, y /*, out + out_offset */ , &zero_mask)) 1107 { 1108 status = 2; 1109 break; 1110 } 1111 out = stp_channel_get_input(v); 1112 if (out_offset > 0) 1113 { 1114 memcpy(tmp_buf + out_offset, out, 1115 image_width * out_channels * sizeof(unsigned short)); 1116 where = tmp_buf; 1117 } 1118 else 1119 where = out; 1120 1121 /* Convert from KCMY to CMYK */ 1122 if (cmyk_out) 1123 { 1124 int x; 1125 unsigned short *pos = where; 1126 for (x = 0; x < image_width; x++, pos += 4) 1127 { 1128 unsigned short p0 = pos[0]; 1129 pos[0] = pos[1]; 1130 pos[1] = pos[2]; 1131 pos[2] = pos[3]; 1132 pos[3] = p0; 1133 } 1134 } 1135 1136 out_ps_height = out_offset + image_width * out_channels; 1137 1138 if (y < (image_height - 1)) 1139 { 1140 ps_ascii85(v, where, out_ps_height & ~3, 0); 1141 out_offset = out_ps_height & 3; 1142 } 1143 else 1144 { 1145 ps_ascii85(v, where, out_ps_height, 1); 1146 out_offset = 0; 1147 } 1148 1149 if (out_offset > 0) 1150 memcpy(tmp_buf, where + out_ps_height - out_offset, 1151 out_offset * sizeof(unsigned short)); 1152 } 1153 stp_free(tmp_buf); 1154 } 1155 stp_image_conclude(image); 1156 1157 stp_puts("grestore\n", v); 1158 stp_puts("showpage\n", v); 1159 stp_puts("%%Trailer\n", v); 1160 stp_puts("%%EOF\n", v); 1161 return status; 1162} 1163 1164static int 1165ps_print(const stp_vars_t *v, stp_image_t *image) 1166{ 1167 int status; 1168#ifdef HAVE_LOCALE_H 1169 char *locale; 1170#endif 1171 stp_vars_t *nv = stp_vars_create_copy(v); 1172 stp_prune_inactive_options(nv); 1173 if (!stp_verify(nv)) 1174 { 1175 stp_eprintf(nv, "Print options not verified; cannot print.\n"); 1176 return 0; 1177 } 1178#ifdef HAVE_LOCALE_H 1179 locale = stp_strdup(setlocale(LC_ALL, NULL)); 1180 setlocale(LC_ALL, "C"); 1181#endif 1182 status = ps_print_internal(nv, image); 1183#ifdef HAVE_LOCALE_H 1184 setlocale(LC_ALL, locale); 1185 stp_free(locale); 1186#endif 1187 stp_vars_destroy(nv); 1188 return status; 1189} 1190 1191 1192/* 1193 * 'ps_hex()' - Print binary data as a series of hexadecimal numbers. 1194 */ 1195 1196static void 1197ps_hex(const stp_vars_t *v, /* I - File to print to */ 1198 unsigned short *data, /* I - Data to print */ 1199 int length) /* I - Number of bytes to print */ 1200{ 1201 int col; /* Current column */ 1202 static const char *hex = "0123456789ABCDEF"; 1203 1204 col = 0; 1205 while (length > 0) 1206 { 1207 unsigned char pixel = (*data & 0xff00) >> 8; 1208 /* 1209 * Put the hex chars out to the file; note that we don't use stp_zprintf() 1210 * for speed reasons... 1211 */ 1212 1213 stp_putc(hex[pixel >> 4], v); 1214 stp_putc(hex[pixel & 15], v); 1215 1216 data ++; 1217 length --; 1218 1219 col += 2; 1220 if (col >= 72) 1221 { 1222 col = 0; 1223 stp_putc('\n', v); 1224 } 1225 } 1226 1227 if (col > 0) 1228 stp_putc('\n', v); 1229} 1230 1231 1232/* 1233 * 'ps_ascii85()' - Print binary data as a series of base-85 numbers. 1234 */ 1235 1236static void 1237ps_ascii85(const stp_vars_t *v, /* I - File to print to */ 1238 unsigned short *data, /* I - Data to print */ 1239 int length, /* I - Number of bytes to print */ 1240 int last_line) /* I - Last line of raster data? */ 1241{ 1242 int i; /* Looping var */ 1243 unsigned b; /* Binary data word */ 1244 unsigned char c[5]; /* ASCII85 encoded chars */ 1245 static int column = 0; /* Current column */ 1246 1247#define OUTBUF_SIZE 4096 1248 unsigned char outbuffer[OUTBUF_SIZE+10]; 1249 int outp=0; 1250 1251 while (length > 3) 1252 { 1253 unsigned char d0 = (data[0] & 0xff00) >> 8; 1254 unsigned char d1 = (data[1] & 0xff00) >> 8; 1255 unsigned char d2 = (data[2] & 0xff00) >> 8; 1256 unsigned char d3 = (data[3] & 0xff00) >> 8; 1257 b = (((((d0 << 8) | d1) << 8) | d2) << 8) | d3; 1258 1259 if (b == 0) 1260 { 1261 outbuffer[outp++]='z'; 1262 column ++; 1263 } 1264 else 1265 { 1266 outbuffer[outp+4] = (b % 85) + '!'; 1267 b /= 85; 1268 outbuffer[outp+3] = (b % 85) + '!'; 1269 b /= 85; 1270 outbuffer[outp+2] = (b % 85) + '!'; 1271 b /= 85; 1272 outbuffer[outp+1] = (b % 85) + '!'; 1273 b /= 85; 1274 outbuffer[outp] = b + '!'; 1275 1276 outp+=5; 1277 column += 5; 1278 } 1279 1280 if (column > 72) 1281 { 1282 outbuffer[outp++]='\n'; 1283 column = 0; 1284 } 1285 1286 if(outp>=OUTBUF_SIZE) 1287 { 1288 stp_zfwrite((const char *)outbuffer, outp, 1, v); 1289 outp=0; 1290 } 1291 1292 data += 4; 1293 length -= 4; 1294 } 1295 1296 if(outp) 1297 stp_zfwrite((const char *)outbuffer, outp, 1, v); 1298 1299 if (last_line) 1300 { 1301 if (length > 0) 1302 { 1303 for (b = 0, i = length; i > 0; b = (b << 8) | data[0], data ++, i --); 1304 1305 c[4] = (b % 85) + '!'; 1306 b /= 85; 1307 c[3] = (b % 85) + '!'; 1308 b /= 85; 1309 c[2] = (b % 85) + '!'; 1310 b /= 85; 1311 c[1] = (b % 85) + '!'; 1312 b /= 85; 1313 c[0] = b + '!'; 1314 1315 stp_zfwrite((const char *)c, length + 1, 1, v); 1316 } 1317 1318 stp_puts("~>\n", v); 1319 column = 0; 1320 } 1321} 1322 1323 1324static const stp_printfuncs_t print_ps_printfuncs = 1325{ 1326 ps_list_parameters, 1327 ps_parameters, 1328 ps_media_size, 1329 ps_imageable_area, 1330 ps_maximum_imageable_area, 1331 ps_limit, 1332 ps_print, 1333 ps_describe_resolution, 1334 ps_describe_output, 1335 stp_verify_printer_params, 1336 NULL, 1337 NULL, 1338 ps_external_options 1339}; 1340 1341 1342static stp_family_t print_ps_module_data = 1343 { 1344 &print_ps_printfuncs, 1345 NULL 1346 }; 1347 1348 1349static int 1350print_ps_module_init(void) 1351{ 1352 return stp_family_register(print_ps_module_data.printer_list); 1353} 1354 1355 1356static int 1357print_ps_module_exit(void) 1358{ 1359 return stp_family_unregister(print_ps_module_data.printer_list); 1360} 1361 1362 1363/* Module header */ 1364#define stp_module_version print_ps_LTX_stp_module_version 1365#define stp_module_data print_ps_LTX_stp_module_data 1366 1367stp_module_version_t stp_module_version = {0, 0}; 1368 1369stp_module_t stp_module_data = 1370 { 1371 "ps", 1372 VERSION, 1373 "Postscript family driver", 1374 STP_MODULE_CLASS_FAMILY, 1375 NULL, 1376 print_ps_module_init, 1377 print_ps_module_exit, 1378 (void *) &print_ps_module_data 1379 }; 1380 1381