From: Michael R Sweet Date: Fri, 18 May 2018 15:36:39 +0000 (-0700) Subject: Save work. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a17ce1d474bb66215a148885d1fae1b918e0fa73;p=thirdparty%2Fcups.git Save work. --- diff --git a/cups/xform-private.h b/cups/xform-private.h index 405f3dd12f..b0becc09d6 100644 --- a/cups/xform-private.h +++ b/cups/xform-private.h @@ -37,6 +37,7 @@ extern "C" { # define XFORM_FORMAT_JPEG "image/jpeg" # define XFORM_FORMAT_PCL "application/vnd.hp-pcl" # define XFORM_FORMAT_PDF "application/pdf" +# define XFORM_FORMAT_PNG "image/png" # define XFORM_FORMAT_POSTSCRIPT "application/postscript" # define XFORM_FORMAT_PWG_RASTER "image/pwg-raster" # define XFORM_FORMAT_TEXT "text/plain" diff --git a/cups/xform.c b/cups/xform.c index 4535d1ca76..056237033c 100644 --- a/cups/xform.c +++ b/cups/xform.c @@ -118,6 +118,12 @@ static void pdf_init(xform_ctx_t *ctx); static void pdf_start_job(xform_ctx_t *ctx); static void pdf_start_page(xform_ctx_t *ctx, unsigned page); +static void png_end_job(xform_ctx_t *ctx); +static void png_end_page(xform_ctx_t *ctx, unsigned page); +static void png_init(xform_ctx_t *ctx); +static void png_start_job(xform_ctx_t *ctx); +static void png_start_page(xform_ctx_t *ctx, unsigned page); + static void ps_end_job(xform_ctx_t *ctx); static void ps_end_page(xform_ctx_t *ctx, unsigned page); static void ps_init(xform_ctx_t *ctx); @@ -175,6 +181,8 @@ xformNew( pcl_init(ctx); else if (!_cups_strcasecmp(outformat, XFORM_FORMAT_PDF)) pdf_init(ctx); + else if (!_cups_strcasecmp(outformat, XFORM_FORMAT_PNG)) + png_init(ctx); /* For first-page previews */ else if (!_cups_strcasecmp(outformat, XFORM_FORMAT_POSTSCRIPT)) ps_init(ctx); else @@ -407,1140 +415,1238 @@ pack_rgba_to_gray( /* - * 'xform_log()' - Log a message. + * 'pcl_end_job()' - End a PCL "job". */ static void -xform_log(xform_ctx_t *ctx, /* I - Transform context */ - xform_loglevel_t level, /* I - Log level */ - const char *message, /* I - Printf-style message */ - ...) /* I - Additional arguments as needed */ +pcl_end_job(xform_ctx_t *ras, /* I - Raster information */ + _xform_write_cb_t cb, /* I - Write callback */ + void *ctx) /* I - Write context */ { - va_list ap; /* Pointer to additional arguments */ - char buffer[2048]; /* Message buffer */ - + (void)ras; - va_start(ap, message); - vsnprintf(buffer, sizeof(buffer), message, ap); - va_end(ap); + /* + * Send a PCL reset sequence. + */ - (ctx->logcb)(ctx->logdata, level, buffer); + (*cb)(ctx, (const unsigned char *)"\033E", 2); } -#if 0 /* - * 'main()' - Main entry for transform utility. + * 'pcl_end_page()' - End of PCL page. */ -int /* O - Exit status */ -main(int argc, /* I - Number of command-line args */ - char *argv[]) /* I - Command-line arguments */ +static void +pcl_end_page(xform_ctx_t *ras, /* I - Raster information */ + unsigned page, /* I - Current page */ + _xform_write_cb_t cb, /* I - Write callback */ + void *ctx) /* I - Write context */ { - int i; /* Looping var */ - const char *filename = NULL, /* File to transform */ - *content_type, /* Source content type */ - *device_uri, /* Destination URI */ - *output_type, /* Destination content type */ - *resolutions, /* pwg-raster-document-resolution-supported */ - *sheet_back, /* pwg-raster-document-sheet-back */ - *types, /* pwg-raster-document-type-supported */ - *opt; /* Option character */ - int num_options; /* Number of options */ - cups_option_t *options; /* Options */ - int fd = 1; /* Output file/socket */ - http_t *http = NULL; /* Output HTTP connection */ - void *write_ptr = &fd; /* Pointer to file/socket/HTTP connection */ - char resource[1024]; /* URI resource path */ - _xform_write_cb_t write_cb = (_xform_write_cb_t)write_fd; - /* Write callback */ - int status = 0; /* Exit status */ - _cups_thread_t monitor = 0; /* Monitoring thread ID */ + /* + * End graphics... + */ + (*cb)(ctx, (const unsigned char *)"\033*r0B", 5); /* - * Process the command-line... + * Formfeed as needed... */ - num_options = load_env_options(&options); - content_type = getenv("CONTENT_TYPE"); - device_uri = getenv("DEVICE_URI"); - output_type = getenv("OUTPUT_TYPE"); - resolutions = getenv("PWG_RASTER_DOCUMENT_RESOLUTION_SUPPORTED"); - sheet_back = getenv("PWG_RASTER_DOCUMENT_SHEET_BACK"); - types = getenv("PWG_RASTER_DOCUMENT_TYPE_SUPPORTED"); + if (!(ras->header.Duplex && (page & 1))) + (*cb)(ctx, (const unsigned char *)"\014", 1); - if ((opt = getenv("SERVER_LOGLEVEL")) != NULL) - { - if (!strcmp(opt, "debug")) - Verbosity = 2; - else if (!strcmp(opt, "info")) - Verbosity = 1; - } + /* + * Free the output buffer... + */ - for (i = 1; i < argc; i ++) - { - if (!strncmp(argv[i], "--", 2)) - { - if (!strcmp(argv[i], "--help")) - { - usage(0); - } - else if (!strcmp(argv[i], "--version")) - { - puts(CUPS_SVERSION); - } - else - { - fprintf(stderr, "ERROR: Unknown option '%s'.\n", argv[i]); - usage(1); - } - } - else if (argv[i][0] == '-') - { - for (opt = argv[i] + 1; *opt; opt ++) - { - switch (*opt) - { - case 'd' : - i ++; - if (i >= argc) - usage(1); + free(ras->out_buffer); + ras->out_buffer = NULL; +} - device_uri = argv[i]; - break; - case 'i' : - i ++; - if (i >= argc) - usage(1); +/* + * 'pcl_init()' - Initialize callbacks for PCL output. + */ - content_type = argv[i]; - break; +static void +pcl_init(xform_ctx_t *ras) /* I - Raster information */ +{ + ras->end_job = pcl_end_job; + ras->end_page = pcl_end_page; + ras->start_job = pcl_start_job; + ras->start_page = pcl_start_page; + ras->write_line = pcl_write_line; +} - case 'm' : - i ++; - if (i >= argc) - usage(1); - output_type = argv[i]; - break; +/* + * 'pcl_printf()' - Write a formatted string. + */ - case 'o' : - i ++; - if (i >= argc) - usage(1); +static void +pcl_printf(_xform_write_cb_t cb, /* I - Write callback */ + void *ctx, /* I - Write context */ + const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Argument pointer */ + char buffer[8192]; /* Buffer */ - num_options = cupsParseOptions(argv[i], num_options, &options); - break; - case 'r' : /* pwg-raster-document-resolution-supported values */ - i ++; - if (i >= argc) - usage(1); + va_start(ap, format); + vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); - resolutions = argv[i]; - break; + (*cb)(ctx, (const unsigned char *)buffer, strlen(buffer)); +} - case 's' : /* pwg-raster-document-sheet-back value */ - i ++; - if (i >= argc) - usage(1); - sheet_back = argv[i]; - break; +/* + * 'pcl_start_job()' - Start a PCL "job". + */ - case 't' : /* pwg-raster-document-type-supported values */ - i ++; - if (i >= argc) - usage(1); +static void +pcl_start_job(xform_ctx_t *ras, /* I - Raster information */ + _xform_write_cb_t cb, /* I - Write callback */ + void *ctx) /* I - Write context */ +{ + (void)ras; - types = argv[i]; - break; + /* + * Send a PCL reset sequence. + */ - case 'v' : /* Be verbose... */ - Verbosity ++; - break; + (*cb)(ctx, (const unsigned char *)"\033E", 2); +} - default : - fprintf(stderr, "ERROR: Unknown option '-%c'.\n", *opt); - usage(1); - break; - } - } - } - else if (!filename) - filename = argv[i]; - else - usage(1); - } +/* + * 'pcl_start_page()' - Start a PCL page. + */ + +static void +pcl_start_page(xform_ctx_t *ras, /* I - Raster information */ + unsigned page, /* I - Current page */ + _xform_write_cb_t cb, /* I - Write callback */ + void *ctx) /* I - Write context */ +{ /* - * Check that we have everything we need... + * Setup margins to be 1/6" top and bottom and 1/4" or .135" on the + * left and right. */ - if (!filename) - usage(1); - - if (!content_type) - { - if ((opt = strrchr(filename, '.')) != NULL) - { - if (!strcmp(opt, ".pdf")) - content_type = "application/pdf"; - else if (!strcmp(opt, ".jpg") || !strcmp(opt, ".jpeg")) - content_type = "image/jpeg"; - } - } + ras->top = ras->header.HWResolution[1] / 6; + ras->bottom = ras->header.cupsHeight - ras->header.HWResolution[1] / 6 - 1; - if (!content_type) + if (ras->header.PageSize[1] == 842) { - fprintf(stderr, "ERROR: Unknown format for \"%s\", please specify with '-i' option.\n", filename); - usage(1); + /* A4 gets special side margins to expose an 8" print area */ + ras->left = (ras->header.cupsWidth - 8 * ras->header.HWResolution[0]) / 2; + ras->right = ras->left + 8 * ras->header.HWResolution[0] - 1; } - else if (strcmp(content_type, "application/pdf") && strcmp(content_type, "image/jpeg")) + else { - fprintf(stderr, "ERROR: Unsupported format \"%s\" for \"%s\".\n", content_type, filename); - usage(1); + /* All other sizes get 1/4" margins */ + ras->left = ras->header.HWResolution[0] / 4; + ras->right = ras->header.cupsWidth - ras->header.HWResolution[0] / 4 - 1; } - if (!output_type) - { - fputs("ERROR: Unknown output format, please specify with '-m' option.\n", stderr); - usage(1); - } - else if (strcmp(output_type, "application/vnd.hp-pcl") && strcmp(output_type, "image/pwg-raster") && strcmp(output_type, "image/urf")) + if (!ras->header.Duplex || (page & 1)) { - fprintf(stderr, "ERROR: Unsupported output format \"%s\".\n", output_type); - usage(1); - } - - if (!resolutions) - resolutions = "300dpi"; - if (!sheet_back) - sheet_back = "normal"; - if (!types) - types = "sgray_8"; - - /* - * If the device URI is specified, open the connection... - */ - - if (device_uri) - { - char scheme[32], /* URI scheme */ - userpass[256], /* URI user:pass */ - host[256], /* URI host */ - service[32]; /* Service port */ - int port; /* URI port number */ - http_addrlist_t *list; /* Address list for socket */ + /* + * Set the media size... + */ - if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK) - { - fprintf(stderr, "ERROR: Invalid device URI \"%s\".\n", device_uri); - usage(1); - } + pcl_printf(cb, ctx, "\033&l12D\033&k12H"); + /* Set 12 LPI, 10 CPI */ + pcl_printf(cb, ctx, "\033&l0O"); /* Set portrait orientation */ - if (strcmp(scheme, "socket") && strcmp(scheme, "ipp") && strcmp(scheme, "ipps")) + switch (ras->header.PageSize[1]) { - fprintf(stderr, "ERROR: Unsupported device URI scheme \"%s\".\n", scheme); - usage(1); - } + case 540 : /* Monarch Envelope */ + pcl_printf(cb, ctx, "\033&l80A"); + break; - snprintf(service, sizeof(service), "%d", port); - if ((list = httpAddrGetList(host, AF_UNSPEC, service)) == NULL) - { - fprintf(stderr, "ERROR: Unable to lookup device URI host \"%s\": %s\n", host, cupsLastErrorString()); - return (1); - } + case 595 : /* A5 */ + pcl_printf(cb, ctx, "\033&l25A"); + break; - if (!strcmp(scheme, "socket")) - { - /* - * AppSocket connection... - */ + case 624 : /* DL Envelope */ + pcl_printf(cb, ctx, "\033&l90A"); + break; - if (!httpAddrConnect2(list, &fd, 30000, NULL)) - { - fprintf(stderr, "ERROR: Unable to connect to \"%s\" on port %d: %s\n", host, port, cupsLastErrorString()); - return (1); - } - } - else - { - http_encryption_t encryption; /* Encryption mode */ - ipp_t *request, /* IPP request */ - *response; /* IPP response */ - ipp_attribute_t *attr; /* operations-supported */ - int create_job = 0; /* Support for Create-Job/Send-Document? */ - const char *job_name; /* Title of job */ - const char *media; /* Value of "media" option */ - const char *sides; /* Value of "sides" option */ + case 649 : /* C5 Envelope */ + pcl_printf(cb, ctx, "\033&l91A"); + break; - /* - * Connect to the IPP/IPPS printer... - */ + case 684 : /* COM-10 Envelope */ + pcl_printf(cb, ctx, "\033&l81A"); + break; - if (port == 443 || !strcmp(scheme, "ipps")) - encryption = HTTP_ENCRYPTION_ALWAYS; - else - encryption = HTTP_ENCRYPTION_IF_REQUESTED; + case 709 : /* B5 Envelope */ + pcl_printf(cb, ctx, "\033&l100A"); + break; - if ((http = httpConnect2(host, port, list, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL) - { - fprintf(stderr, "ERROR: Unable to connect to \"%s\" on port %d: %s\n", host, port, cupsLastErrorString()); - return (1); - } + case 756 : /* Executive */ + pcl_printf(cb, ctx, "\033&l1A"); + break; - /* - * See if it supports Create-Job + Send-Document... - */ + case 792 : /* Letter */ + pcl_printf(cb, ctx, "\033&l2A"); + break; - request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "operations-supported"); + case 842 : /* A4 */ + pcl_printf(cb, ctx, "\033&l26A"); + break; - response = cupsDoRequest(http, request, resource); - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - { - fprintf(stderr, "ERROR: Unable to get printer capabilities: %s\n", cupsLastErrorString()); - return (1); - } + case 1008 : /* Legal */ + pcl_printf(cb, ctx, "\033&l3A"); + break; - if ((attr = ippFindAttribute(response, "operations-supported", IPP_TAG_ENUM)) == NULL) - { - fputs("ERROR: Unable to get list of supported operations from printer.\n", stderr); - return (1); - } + case 1191 : /* A3 */ + pcl_printf(cb, ctx, "\033&l27A"); + break; - create_job = ippContainsInteger(attr, IPP_OP_CREATE_JOB) && ippContainsInteger(attr, IPP_OP_SEND_DOCUMENT); + case 1224 : /* Tabloid */ + pcl_printf(cb, ctx, "\033&l6A"); + break; + } - ippDelete(response); + /* + * Set top margin and turn off perforation skip... + */ - /* - * Create the job and start printing... - */ + pcl_printf(cb, ctx, "\033&l%uE\033&l0L", 12 * ras->top / ras->header.HWResolution[1]); - if ((job_name = getenv("IPP_JOB_NAME")) == NULL) - { - if ((job_name = strrchr(filename, '/')) != NULL) - job_name ++; - else - job_name = filename; - } + if (ras->header.Duplex) + { + int mode = ras->header.Duplex ? 1 + ras->header.Tumble != 0 : 0; - if (create_job) - { - int job_id = 0; /* Job ID */ + pcl_printf(cb, ctx, "\033&l%dS", mode); + /* Set duplex mode */ + } + } + else if (ras->header.Duplex) + pcl_printf(cb, ctx, "\033&a2G"); /* Print on back side */ - request = ippNewRequest(IPP_OP_CREATE_JOB); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, job_name); + /* + * Set graphics mode... + */ - response = cupsDoRequest(http, request, resource); - if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL) - job_id = ippGetInteger(attr, 0); - ippDelete(response); + pcl_printf(cb, ctx, "\033*t%uR", ras->header.HWResolution[0]); + /* Set resolution */ + pcl_printf(cb, ctx, "\033*r%uS", ras->right - ras->left + 1); + /* Set width */ + pcl_printf(cb, ctx, "\033*r%uT", ras->bottom - ras->top + 1); + /* Set height */ + pcl_printf(cb, ctx, "\033&a0H\033&a%uV", 720 * ras->top / ras->header.HWResolution[1]); + /* Set position */ - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) - { - fprintf(stderr, "ERROR: Unable to create print job: %s\n", cupsLastErrorString()); - return (1); - } - else if (job_id <= 0) - { - fputs("ERROR: No job-id for created print job.\n", stderr); - return (1); - } + pcl_printf(cb, ctx, "\033*b2M"); /* Use PackBits compression */ + pcl_printf(cb, ctx, "\033*r1A"); /* Start graphics */ - request = ippNewRequest(IPP_OP_SEND_DOCUMENT); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri); - ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, output_type); - ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1); - } - else - { - request = ippNewRequest(IPP_OP_PRINT_JOB); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, output_type); - } + /* + * Allocate the output buffer... + */ - if ((media = cupsGetOption("media", num_options, options)) != NULL) - ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media", NULL, media); + ras->out_blanks = 0; + ras->out_length = (ras->right - ras->left + 8) / 8; + ras->out_buffer = malloc(ras->out_length); + ras->comp_buffer = malloc(2 * ras->out_length + 2); +} - if ((sides = cupsGetOption("sides", num_options, options)) != NULL) - ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, sides); - if (cupsSendRequest(http, request, resource, 0) != HTTP_STATUS_CONTINUE) - { - fprintf(stderr, "ERROR: Unable to send print data: %s\n", cupsLastErrorString()); - return (1); - } +/* + * 'pcl_write_line()' - Write a line of raster data. + */ - ippDelete(request); +static void +pcl_write_line( + xform_ctx_t *ras, /* I - Raster information */ + unsigned y, /* I - Line number */ + const unsigned char *line, /* I - Pixels on line */ + _xform_write_cb_t cb, /* I - Write callback */ + void *ctx) /* I - Write context */ +{ + unsigned x; /* Column number */ + unsigned char bit, /* Current bit */ + byte, /* Current byte */ + *outptr, /* Pointer into output buffer */ + *outend, /* End of output buffer */ + *start, /* Start of sequence */ + *compptr; /* Pointer into compression buffer */ + unsigned count; /* Count of bytes for output */ - write_cb = (_xform_write_cb_t)httpWrite2; - write_ptr = http; - monitor = _cupsThreadCreate((_cups_thread_func_t)monitor_ipp, (void *)device_uri); - } + if (line[0] == 255 && !memcmp(line, line + 1, ras->right - ras->left)) + { + /* + * Skip blank line... + */ - httpAddrFreeList(list); + ras->out_blanks ++; + return; } /* - * Do transform... + * Dither the line into the output buffer... */ - status = xform_document(filename, content_type, output_type, resolutions, sheet_back, types, num_options, options, write_cb, write_ptr); + y &= 63; - if (http) + for (x = ras->left, bit = 128, byte = 0, outptr = ras->out_buffer; x <= ras->right; x ++, line ++) { - ippDelete(cupsGetResponse(http, resource)); + if (*line <= threshold[x & 63][y]) + byte |= bit; - if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) + if (bit == 1) { - fprintf(stderr, "ERROR: Unable to send print data: %s\n", cupsLastErrorString()); - status = 1; + *outptr++ = byte; + byte = 0; + bit = 128; } - - httpClose(http); + else + bit >>= 1; } - else if (fd != 1) - close(fd); - - if (monitor) - _cupsThreadCancel(monitor); - - return (status); -} - - -/* - * 'load_env_options()' - Load options from the environment. - */ - -extern char **environ; - -static int /* O - Number of options */ -load_env_options( - cups_option_t **options) /* I - Options */ -{ - int i; /* Looping var */ - char name[256], /* Option name */ - *nameptr, /* Pointer into name */ - *envptr; /* Pointer into environment variable */ - int num_options = 0; /* Number of options */ - - *options = NULL; + if (bit != 128) + *outptr++ = byte; /* - * Load all of the IPP_xxx environment variables as options... + * Apply compression... */ - for (i = 0; environ[i]; i ++) - { - envptr = environ[i]; - - if (strncmp(envptr, "IPP_", 4)) - continue; + compptr = ras->comp_buffer; + outend = outptr; + outptr = ras->out_buffer; - for (nameptr = name, envptr += 4; *envptr && *envptr != '='; envptr ++) + while (outptr < outend) + { + if ((outptr + 1) >= outend) { - if (nameptr > (name + sizeof(name) - 1)) - continue; + /* + * Single byte on the end... + */ - if (*envptr == '_') - *nameptr++ = '-'; - else - *nameptr++ = (char)_cups_tolower(*envptr); + *compptr++ = 0x00; + *compptr++ = *outptr++; } + else if (outptr[0] == outptr[1]) + { + /* + * Repeated sequence... + */ - *nameptr = '\0'; - if (*envptr == '=') - envptr ++; - - num_options = cupsAddOption(name, envptr, num_options, options); - } - - return (num_options); -} - + outptr ++; + count = 2; -/* - * 'monitor_ipp()' - Monitor IPP printer status. - */ + while (outptr < (outend - 1) && + outptr[0] == outptr[1] && + count < 127) + { + outptr ++; + count ++; + } -static void * /* O - Thread exit status */ -monitor_ipp(const char *device_uri) /* I - Device URI */ -{ - int i; /* Looping var */ - http_t *http; /* HTTP connection */ - ipp_t *request, /* IPP request */ - *response; /* IPP response */ - ipp_attribute_t *attr; /* IPP response attribute */ - char scheme[32], /* URI scheme */ - userpass[256], /* URI user:pass */ - host[256], /* URI host */ - resource[1024]; /* URI resource */ - int port; /* URI port number */ - http_encryption_t encryption; /* Encryption to use */ - int delay = 1, /* Current delay */ - next_delay, /* Next delay */ - prev_delay = 0; /* Previous delay */ - char pvalues[10][1024]; /* Current printer attribute values */ - static const char * const pattrs[10] =/* Printer attributes we need */ - { - "marker-colors", - "marker-levels", - "marker-low-levels", - "marker-high-levels", - "marker-names", - "marker-types", - "printer-alert", - "printer-state-reasons", - "printer-supply", - "printer-supply-description" - }; + *compptr++ = (unsigned char)(257 - count); + *compptr++ = *outptr++; + } + else + { + /* + * Non-repeated sequence... + */ + start = outptr; + outptr ++; + count = 1; - httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)); + while (outptr < (outend - 1) && + outptr[0] != outptr[1] && + count < 127) + { + outptr ++; + count ++; + } - if (port == 443 || !strcmp(scheme, "ipps")) - encryption = HTTP_ENCRYPTION_ALWAYS; - else - encryption = HTTP_ENCRYPTION_IF_REQUESTED; + *compptr++ = (unsigned char)(count - 1); - while ((http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL) - { - fprintf(stderr, "ERROR: Unable to connect to \"%s\" on port %d: %s\n", host, port, cupsLastErrorString()); - sleep(30); + memcpy(compptr, start, count); + compptr += count; + } } /* - * Report printer state changes until we are canceled... + * Output the line... */ - for (;;) + if (ras->out_blanks > 0) { /* - * Poll for the current state... + * Skip blank lines first... */ - request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); - ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs); - - response = cupsDoRequest(http, request, resource); + pcl_printf(cb, ctx, "\033*b%dY", ras->out_blanks); + ras->out_blanks = 0; + } - /* - * Report any differences... - */ + pcl_printf(cb, ctx, "\033*b%dW", (int)(compptr - ras->comp_buffer)); + (*cb)(ctx, ras->comp_buffer, (size_t)(compptr - ras->comp_buffer)); +} - for (attr = ippFirstAttribute(response); attr; attr = ippNextAttribute(response)) - { - const char *name = ippGetName(attr); - char value[1024]; /* Name and value */ +/* + * 'pdf_end_job()' - End a PDF "job". + */ - if (!name) - continue; +static void +pdf_end_job(xform_ctx_t *ctx) /* I - Transform context */ +{ +} - for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++) - if (!strcmp(name, pattrs[i])) - break; - if (i >= (int)(sizeof(pattrs) / sizeof(pattrs[0]))) - continue; +static void +pdf_end_page(xform_ctx_t *ctx, /* I - Transform context */ + unsigned page) /* I - Page number */ +{ +} - ippAttributeString(attr, value, sizeof(value)); - if (strcmp(value, pvalues[i])) - { - if (!strcmp(name, "printer-state-reasons")) - fprintf(stderr, "STATE: %s\n", value); - else - fprintf(stderr, "ATTR: %s='%s'\n", name, value); +static void +pdf_init(xform_ctx_t *ctx) /* I - Transform context */ +{ +} - strlcpy(pvalues[i], value, sizeof(pvalues[i])); - } - } - ippDelete(response); +static void +pdf_start_job(xform_ctx_t *ctx) /* I - Transform context */ +{ +} - /* - * Sleep until the next update... - */ - sleep((unsigned)delay); +static void +pdf_start_page(xform_ctx_t *ctx, /* I - Transform context */ + unsigned page) /* I - Page number */ +{ +} - next_delay = (delay + prev_delay) % 12; - prev_delay = next_delay < delay ? 0 : delay; - delay = next_delay; - } - return (NULL); +static void +png_end_job(xform_ctx_t *ctx) /* I - Transform context */ +{ } +static void +png_end_page(xform_ctx_t *ctx, /* I - Transform context */ + unsigned page) /* I - Page number */ +{ +} -/* - * 'pcl_end_job()' - End a PCL "job". - */ - static void -pcl_end_job(xform_ctx_t *ras, /* I - Raster information */ - _xform_write_cb_t cb, /* I - Write callback */ - void *ctx) /* I - Write context */ +png_init(xform_ctx_t *ctx) /* I - Transform context */ { - (void)ras; +} - /* - * Send a PCL reset sequence. - */ - (*cb)(ctx, (const unsigned char *)"\033E", 2); +static void +png_start_job(xform_ctx_t *ctx) /* I - Transform context */ +{ } -/* - * 'pcl_end_page()' - End of PCL page. - */ - static void -pcl_end_page(xform_ctx_t *ras, /* I - Raster information */ - unsigned page, /* I - Current page */ - _xform_write_cb_t cb, /* I - Write callback */ - void *ctx) /* I - Write context */ +png_start_page(xform_ctx_t *ctx, /* I - Transform context */ + unsigned page) /* I - Page number */ { - /* - * End graphics... - */ +} - (*cb)(ctx, (const unsigned char *)"\033*r0B", 5); - /* - * Formfeed as needed... - */ +static void +ps_end_job(xform_ctx_t *ctx) /* I - Transform context */ +{ +} - if (!(ras->header.Duplex && (page & 1))) - (*cb)(ctx, (const unsigned char *)"\014", 1); - /* - * Free the output buffer... - */ +static void +ps_end_page(xform_ctx_t *ctx, /* I - Transform context */ + unsigned page) /* I - Page number */ +{ +} - free(ras->out_buffer); - ras->out_buffer = NULL; + +static void +ps_init(xform_ctx_t *ctx) /* I - Transform context */ +{ } -/* - * 'pcl_init()' - Initialize callbacks for PCL output. - */ +static void +ps_start_job(xform_ctx_t *ctx) /* I - Transform context */ +{ +} + static void -pcl_init(xform_ctx_t *ras) /* I - Raster information */ +ps_start_page(xform_ctx_t *ctx, /* I - Transform context */ + unsigned page) /* I - Page number */ { - ras->end_job = pcl_end_job; - ras->end_page = pcl_end_page; - ras->start_job = pcl_start_job; - ras->start_page = pcl_start_page; - ras->write_line = pcl_write_line; } /* - * 'pcl_printf()' - Write a formatted string. + * 'raster_end_job()' - End a raster "job". */ static void -pcl_printf(_xform_write_cb_t cb, /* I - Write callback */ - void *ctx, /* I - Write context */ - const char *format, /* I - Printf-style format string */ - ...) /* I - Additional arguments as needed */ +raster_end_job(xform_ctx_t *ras, /* I - Raster information */ + _xform_write_cb_t cb, /* I - Write callback */ + void *ctx) /* I - Write context */ { - va_list ap; /* Argument pointer */ - char buffer[8192]; /* Buffer */ + (void)cb; + (void)ctx; + cupsRasterClose(ras->ras); +} - va_start(ap, format); - vsnprintf(buffer, sizeof(buffer), format, ap); - va_end(ap); - (*cb)(ctx, (const unsigned char *)buffer, strlen(buffer)); +/* + * 'raster_end_page()' - End of raster page. + */ + +static void +raster_end_page(xform_ctx_t *ras, /* I - Raster information */ + unsigned page, /* I - Current page */ + _xform_write_cb_t cb, /* I - Write callback */ + void *ctx) /* I - Write context */ +{ + (void)page; + (void)cb; + (void)ctx; + + if (ras->header.cupsBitsPerPixel == 1) + { + free(ras->out_buffer); + ras->out_buffer = NULL; + } } /* - * 'pcl_start_job()' - Start a PCL "job". + * 'raster_init()' - Initialize callbacks for raster output. */ static void -pcl_start_job(xform_ctx_t *ras, /* I - Raster information */ - _xform_write_cb_t cb, /* I - Write callback */ - void *ctx) /* I - Write context */ +raster_init(xform_ctx_t *ras) /* I - Raster information */ { - (void)ras; + ras->end_job = raster_end_job; + ras->end_page = raster_end_page; + ras->start_job = raster_start_job; + ras->start_page = raster_start_page; + ras->write_line = raster_write_line; +} - /* - * Send a PCL reset sequence. - */ - (*cb)(ctx, (const unsigned char *)"\033E", 2); +/* + * 'raster_start_job()' - Start a raster "job". + */ + +static void +raster_start_job(xform_ctx_t *ras, /* I - Raster information */ + _xform_write_cb_t cb, /* I - Write callback */ + void *ctx) /* I - Write context */ +{ + ras->ras = cupsRasterOpenIO((cups_raster_iocb_t)cb, ctx, !strcmp(ras->format, "image/pwg-raster") ? CUPS_RASTER_WRITE_PWG : CUPS_RASTER_WRITE_APPLE); } /* - * 'pcl_start_page()' - Start a PCL page. + * 'raster_start_page()' - Start a raster page. */ static void -pcl_start_page(xform_ctx_t *ras, /* I - Raster information */ - unsigned page, /* I - Current page */ - _xform_write_cb_t cb, /* I - Write callback */ - void *ctx) /* I - Write context */ +raster_start_page(xform_ctx_t *ras,/* I - Raster information */ + unsigned page,/* I - Current page */ + _xform_write_cb_t cb, /* I - Write callback */ + void *ctx)/* I - Write context */ { - /* - * Setup margins to be 1/6" top and bottom and 1/4" or .135" on the - * left and right. - */ + (void)cb; + (void)ctx; - ras->top = ras->header.HWResolution[1] / 6; - ras->bottom = ras->header.cupsHeight - ras->header.HWResolution[1] / 6 - 1; + ras->left = 0; + ras->top = 0; + ras->right = ras->header.cupsWidth - 1; + ras->bottom = ras->header.cupsHeight - 1; - if (ras->header.PageSize[1] == 842) - { - /* A4 gets special side margins to expose an 8" print area */ - ras->left = (ras->header.cupsWidth - 8 * ras->header.HWResolution[0]) / 2; - ras->right = ras->left + 8 * ras->header.HWResolution[0] - 1; - } + if (ras->header.Duplex && !(page & 1)) + cupsRasterWriteHeader2(ras->ras, &ras->back_header); else + cupsRasterWriteHeader2(ras->ras, &ras->header); + + if (ras->header.cupsBitsPerPixel == 1) { - /* All other sizes get 1/4" margins */ - ras->left = ras->header.HWResolution[0] / 4; - ras->right = ras->header.cupsWidth - ras->header.HWResolution[0] / 4 - 1; + ras->out_length = ras->header.cupsBytesPerLine; + ras->out_buffer = malloc(ras->header.cupsBytesPerLine); } +} - if (!ras->header.Duplex || (page & 1)) + +/* + * 'raster_write_line()' - Write a line of raster data. + */ + +static void +raster_write_line( + xform_ctx_t *ras, /* I - Raster information */ + unsigned y, /* I - Line number */ + const unsigned char *line, /* I - Pixels on line */ + _xform_write_cb_t cb, /* I - Write callback */ + void *ctx) /* I - Write context */ +{ + (void)cb; + (void)ctx; + + if (ras->header.cupsBitsPerPixel == 1) { /* - * Set the media size... + * Dither the line into the output buffer... */ - pcl_printf(cb, ctx, "\033&l12D\033&k12H"); - /* Set 12 LPI, 10 CPI */ - pcl_printf(cb, ctx, "\033&l0O"); /* Set portrait orientation */ + unsigned x; /* Column number */ + unsigned char bit, /* Current bit */ + byte, /* Current byte */ + *outptr; /* Pointer into output buffer */ - switch (ras->header.PageSize[1]) - { - case 540 : /* Monarch Envelope */ - pcl_printf(cb, ctx, "\033&l80A"); - break; + y &= 63; - case 595 : /* A5 */ - pcl_printf(cb, ctx, "\033&l25A"); - break; + if (ras->header.cupsColorSpace == CUPS_CSPACE_SW) + { + for (x = ras->left, bit = 128, byte = 0, outptr = ras->out_buffer; x <= ras->right; x ++, line ++) + { + if (*line > threshold[x % 25][y]) + byte |= bit; - case 624 : /* DL Envelope */ - pcl_printf(cb, ctx, "\033&l90A"); - break; + if (bit == 1) + { + *outptr++ = byte; + byte = 0; + bit = 128; + } + else + bit >>= 1; + } + } + else + { + for (x = ras->left, bit = 128, byte = 0, outptr = ras->out_buffer; x <= ras->right; x ++, line ++) + { + if (*line <= threshold[x & 63][y]) + byte |= bit; - case 649 : /* C5 Envelope */ - pcl_printf(cb, ctx, "\033&l91A"); - break; + if (bit == 1) + { + *outptr++ = byte; + byte = 0; + bit = 128; + } + else + bit >>= 1; + } + } - case 684 : /* COM-10 Envelope */ - pcl_printf(cb, ctx, "\033&l81A"); - break; + if (bit != 128) + *outptr++ = byte; - case 709 : /* B5 Envelope */ - pcl_printf(cb, ctx, "\033&l100A"); - break; + cupsRasterWritePixels(ras->ras, ras->out_buffer, ras->header.cupsBytesPerLine); + } + else + cupsRasterWritePixels(ras->ras, (unsigned char *)line, ras->header.cupsBytesPerLine); +} - case 756 : /* Executive */ - pcl_printf(cb, ctx, "\033&l1A"); - break; - case 792 : /* Letter */ - pcl_printf(cb, ctx, "\033&l2A"); - break; +/* + * 'xform_log()' - Log a message. + */ - case 842 : /* A4 */ - pcl_printf(cb, ctx, "\033&l26A"); - break; +static void +xform_log(xform_ctx_t *ctx, /* I - Transform context */ + xform_loglevel_t level, /* I - Log level */ + const char *message, /* I - Printf-style message */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Pointer to additional arguments */ + char buffer[2048]; /* Message buffer */ - case 1008 : /* Legal */ - pcl_printf(cb, ctx, "\033&l3A"); - break; - case 1191 : /* A3 */ - pcl_printf(cb, ctx, "\033&l27A"); - break; + va_start(ap, message); + vsnprintf(buffer, sizeof(buffer), message, ap); + va_end(ap); - case 1224 : /* Tabloid */ - pcl_printf(cb, ctx, "\033&l6A"); - break; - } + (ctx->logcb)(ctx->logdata, level, buffer); +} - /* - * Set top margin and turn off perforation skip... - */ - pcl_printf(cb, ctx, "\033&l%uE\033&l0L", 12 * ras->top / ras->header.HWResolution[1]); +#if 0 +/* + * 'main()' - Main entry for transform utility. + */ - if (ras->header.Duplex) - { - int mode = ras->header.Duplex ? 1 + ras->header.Tumble != 0 : 0; +int /* O - Exit status */ +main(int argc, /* I - Number of command-line args */ + char *argv[]) /* I - Command-line arguments */ +{ + int i; /* Looping var */ + const char *filename = NULL, /* File to transform */ + *content_type, /* Source content type */ + *device_uri, /* Destination URI */ + *output_type, /* Destination content type */ + *resolutions, /* pwg-raster-document-resolution-supported */ + *sheet_back, /* pwg-raster-document-sheet-back */ + *types, /* pwg-raster-document-type-supported */ + *opt; /* Option character */ + int num_options; /* Number of options */ + cups_option_t *options; /* Options */ + int fd = 1; /* Output file/socket */ + http_t *http = NULL; /* Output HTTP connection */ + void *write_ptr = &fd; /* Pointer to file/socket/HTTP connection */ + char resource[1024]; /* URI resource path */ + _xform_write_cb_t write_cb = (_xform_write_cb_t)write_fd; + /* Write callback */ + int status = 0; /* Exit status */ + _cups_thread_t monitor = 0; /* Monitoring thread ID */ - pcl_printf(cb, ctx, "\033&l%dS", mode); - /* Set duplex mode */ - } - } - else if (ras->header.Duplex) - pcl_printf(cb, ctx, "\033&a2G"); /* Print on back side */ /* - * Set graphics mode... + * Process the command-line... */ - pcl_printf(cb, ctx, "\033*t%uR", ras->header.HWResolution[0]); - /* Set resolution */ - pcl_printf(cb, ctx, "\033*r%uS", ras->right - ras->left + 1); - /* Set width */ - pcl_printf(cb, ctx, "\033*r%uT", ras->bottom - ras->top + 1); - /* Set height */ - pcl_printf(cb, ctx, "\033&a0H\033&a%uV", 720 * ras->top / ras->header.HWResolution[1]); - /* Set position */ + num_options = load_env_options(&options); + content_type = getenv("CONTENT_TYPE"); + device_uri = getenv("DEVICE_URI"); + output_type = getenv("OUTPUT_TYPE"); + resolutions = getenv("PWG_RASTER_DOCUMENT_RESOLUTION_SUPPORTED"); + sheet_back = getenv("PWG_RASTER_DOCUMENT_SHEET_BACK"); + types = getenv("PWG_RASTER_DOCUMENT_TYPE_SUPPORTED"); - pcl_printf(cb, ctx, "\033*b2M"); /* Use PackBits compression */ - pcl_printf(cb, ctx, "\033*r1A"); /* Start graphics */ + if ((opt = getenv("SERVER_LOGLEVEL")) != NULL) + { + if (!strcmp(opt, "debug")) + Verbosity = 2; + else if (!strcmp(opt, "info")) + Verbosity = 1; + } - /* - * Allocate the output buffer... - */ + for (i = 1; i < argc; i ++) + { + if (!strncmp(argv[i], "--", 2)) + { + if (!strcmp(argv[i], "--help")) + { + usage(0); + } + else if (!strcmp(argv[i], "--version")) + { + puts(CUPS_SVERSION); + } + else + { + fprintf(stderr, "ERROR: Unknown option '%s'.\n", argv[i]); + usage(1); + } + } + else if (argv[i][0] == '-') + { + for (opt = argv[i] + 1; *opt; opt ++) + { + switch (*opt) + { + case 'd' : + i ++; + if (i >= argc) + usage(1); - ras->out_blanks = 0; - ras->out_length = (ras->right - ras->left + 8) / 8; - ras->out_buffer = malloc(ras->out_length); - ras->comp_buffer = malloc(2 * ras->out_length + 2); -} + device_uri = argv[i]; + break; + case 'i' : + i ++; + if (i >= argc) + usage(1); -/* - * 'pcl_write_line()' - Write a line of raster data. - */ + content_type = argv[i]; + break; -static void -pcl_write_line( - xform_ctx_t *ras, /* I - Raster information */ - unsigned y, /* I - Line number */ - const unsigned char *line, /* I - Pixels on line */ - _xform_write_cb_t cb, /* I - Write callback */ - void *ctx) /* I - Write context */ -{ - unsigned x; /* Column number */ - unsigned char bit, /* Current bit */ - byte, /* Current byte */ - *outptr, /* Pointer into output buffer */ - *outend, /* End of output buffer */ - *start, /* Start of sequence */ - *compptr; /* Pointer into compression buffer */ - unsigned count; /* Count of bytes for output */ + case 'm' : + i ++; + if (i >= argc) + usage(1); + output_type = argv[i]; + break; - if (line[0] == 255 && !memcmp(line, line + 1, ras->right - ras->left)) - { - /* - * Skip blank line... - */ + case 'o' : + i ++; + if (i >= argc) + usage(1); - ras->out_blanks ++; - return; + num_options = cupsParseOptions(argv[i], num_options, &options); + break; + + case 'r' : /* pwg-raster-document-resolution-supported values */ + i ++; + if (i >= argc) + usage(1); + + resolutions = argv[i]; + break; + + case 's' : /* pwg-raster-document-sheet-back value */ + i ++; + if (i >= argc) + usage(1); + + sheet_back = argv[i]; + break; + + case 't' : /* pwg-raster-document-type-supported values */ + i ++; + if (i >= argc) + usage(1); + + types = argv[i]; + break; + + case 'v' : /* Be verbose... */ + Verbosity ++; + break; + + default : + fprintf(stderr, "ERROR: Unknown option '-%c'.\n", *opt); + usage(1); + break; + } + } + } + else if (!filename) + filename = argv[i]; + else + usage(1); } /* - * Dither the line into the output buffer... + * Check that we have everything we need... */ - y &= 63; + if (!filename) + usage(1); - for (x = ras->left, bit = 128, byte = 0, outptr = ras->out_buffer; x <= ras->right; x ++, line ++) + if (!content_type) { - if (*line <= threshold[x & 63][y]) - byte |= bit; - - if (bit == 1) + if ((opt = strrchr(filename, '.')) != NULL) { - *outptr++ = byte; - byte = 0; - bit = 128; + if (!strcmp(opt, ".pdf")) + content_type = "application/pdf"; + else if (!strcmp(opt, ".jpg") || !strcmp(opt, ".jpeg")) + content_type = "image/jpeg"; } - else - bit >>= 1; } - if (bit != 128) - *outptr++ = byte; + if (!content_type) + { + fprintf(stderr, "ERROR: Unknown format for \"%s\", please specify with '-i' option.\n", filename); + usage(1); + } + else if (strcmp(content_type, "application/pdf") && strcmp(content_type, "image/jpeg")) + { + fprintf(stderr, "ERROR: Unsupported format \"%s\" for \"%s\".\n", content_type, filename); + usage(1); + } + + if (!output_type) + { + fputs("ERROR: Unknown output format, please specify with '-m' option.\n", stderr); + usage(1); + } + else if (strcmp(output_type, "application/vnd.hp-pcl") && strcmp(output_type, "image/pwg-raster") && strcmp(output_type, "image/urf")) + { + fprintf(stderr, "ERROR: Unsupported output format \"%s\".\n", output_type); + usage(1); + } + + if (!resolutions) + resolutions = "300dpi"; + if (!sheet_back) + sheet_back = "normal"; + if (!types) + types = "sgray_8"; /* - * Apply compression... + * If the device URI is specified, open the connection... */ - compptr = ras->comp_buffer; - outend = outptr; - outptr = ras->out_buffer; - - while (outptr < outend) + if (device_uri) { - if ((outptr + 1) >= outend) + char scheme[32], /* URI scheme */ + userpass[256], /* URI user:pass */ + host[256], /* URI host */ + service[32]; /* Service port */ + int port; /* URI port number */ + http_addrlist_t *list; /* Address list for socket */ + + if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK) + { + fprintf(stderr, "ERROR: Invalid device URI \"%s\".\n", device_uri); + usage(1); + } + + if (strcmp(scheme, "socket") && strcmp(scheme, "ipp") && strcmp(scheme, "ipps")) + { + fprintf(stderr, "ERROR: Unsupported device URI scheme \"%s\".\n", scheme); + usage(1); + } + + snprintf(service, sizeof(service), "%d", port); + if ((list = httpAddrGetList(host, AF_UNSPEC, service)) == NULL) + { + fprintf(stderr, "ERROR: Unable to lookup device URI host \"%s\": %s\n", host, cupsLastErrorString()); + return (1); + } + + if (!strcmp(scheme, "socket")) { /* - * Single byte on the end... + * AppSocket connection... */ - *compptr++ = 0x00; - *compptr++ = *outptr++; + if (!httpAddrConnect2(list, &fd, 30000, NULL)) + { + fprintf(stderr, "ERROR: Unable to connect to \"%s\" on port %d: %s\n", host, port, cupsLastErrorString()); + return (1); + } } - else if (outptr[0] == outptr[1]) + else { + http_encryption_t encryption; /* Encryption mode */ + ipp_t *request, /* IPP request */ + *response; /* IPP response */ + ipp_attribute_t *attr; /* operations-supported */ + int create_job = 0; /* Support for Create-Job/Send-Document? */ + const char *job_name; /* Title of job */ + const char *media; /* Value of "media" option */ + const char *sides; /* Value of "sides" option */ + /* - * Repeated sequence... + * Connect to the IPP/IPPS printer... */ - outptr ++; - count = 2; + if (port == 443 || !strcmp(scheme, "ipps")) + encryption = HTTP_ENCRYPTION_ALWAYS; + else + encryption = HTTP_ENCRYPTION_IF_REQUESTED; - while (outptr < (outend - 1) && - outptr[0] == outptr[1] && - count < 127) + if ((http = httpConnect2(host, port, list, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL) { - outptr ++; - count ++; + fprintf(stderr, "ERROR: Unable to connect to \"%s\" on port %d: %s\n", host, port, cupsLastErrorString()); + return (1); } - *compptr++ = (unsigned char)(257 - count); - *compptr++ = *outptr++; - } - else - { /* - * Non-repeated sequence... + * See if it supports Create-Job + Send-Document... */ - start = outptr; - outptr ++; - count = 1; + request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "operations-supported"); - while (outptr < (outend - 1) && - outptr[0] != outptr[1] && - count < 127) + response = cupsDoRequest(http, request, resource); + if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) { - outptr ++; - count ++; + fprintf(stderr, "ERROR: Unable to get printer capabilities: %s\n", cupsLastErrorString()); + return (1); } - *compptr++ = (unsigned char)(count - 1); + if ((attr = ippFindAttribute(response, "operations-supported", IPP_TAG_ENUM)) == NULL) + { + fputs("ERROR: Unable to get list of supported operations from printer.\n", stderr); + return (1); + } - memcpy(compptr, start, count); - compptr += count; + create_job = ippContainsInteger(attr, IPP_OP_CREATE_JOB) && ippContainsInteger(attr, IPP_OP_SEND_DOCUMENT); + + ippDelete(response); + + /* + * Create the job and start printing... + */ + + if ((job_name = getenv("IPP_JOB_NAME")) == NULL) + { + if ((job_name = strrchr(filename, '/')) != NULL) + job_name ++; + else + job_name = filename; + } + + if (create_job) + { + int job_id = 0; /* Job ID */ + + request = ippNewRequest(IPP_OP_CREATE_JOB); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, job_name); + + response = cupsDoRequest(http, request, resource); + if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL) + job_id = ippGetInteger(attr, 0); + ippDelete(response); + + if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) + { + fprintf(stderr, "ERROR: Unable to create print job: %s\n", cupsLastErrorString()); + return (1); + } + else if (job_id <= 0) + { + fputs("ERROR: No job-id for created print job.\n", stderr); + return (1); + } + + request = ippNewRequest(IPP_OP_SEND_DOCUMENT); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri); + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, output_type); + ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1); + } + else + { + request = ippNewRequest(IPP_OP_PRINT_JOB); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, output_type); + } + + if ((media = cupsGetOption("media", num_options, options)) != NULL) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media", NULL, media); + + if ((sides = cupsGetOption("sides", num_options, options)) != NULL) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, sides); + + if (cupsSendRequest(http, request, resource, 0) != HTTP_STATUS_CONTINUE) + { + fprintf(stderr, "ERROR: Unable to send print data: %s\n", cupsLastErrorString()); + return (1); + } + + ippDelete(request); + + write_cb = (_xform_write_cb_t)httpWrite2; + write_ptr = http; + + monitor = _cupsThreadCreate((_cups_thread_func_t)monitor_ipp, (void *)device_uri); } + + httpAddrFreeList(list); } /* - * Output the line... + * Do transform... */ - if (ras->out_blanks > 0) + status = xform_document(filename, content_type, output_type, resolutions, sheet_back, types, num_options, options, write_cb, write_ptr); + + if (http) { - /* - * Skip blank lines first... - */ + ippDelete(cupsGetResponse(http, resource)); + + if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) + { + fprintf(stderr, "ERROR: Unable to send print data: %s\n", cupsLastErrorString()); + status = 1; + } - pcl_printf(cb, ctx, "\033*b%dY", ras->out_blanks); - ras->out_blanks = 0; + httpClose(http); } + else if (fd != 1) + close(fd); - pcl_printf(cb, ctx, "\033*b%dW", (int)(compptr - ras->comp_buffer)); - (*cb)(ctx, ras->comp_buffer, (size_t)(compptr - ras->comp_buffer)); + if (monitor) + _cupsThreadCancel(monitor); + + return (status); } /* - * 'raster_end_job()' - End a raster "job". + * 'load_env_options()' - Load options from the environment. */ -static void -raster_end_job(xform_ctx_t *ras, /* I - Raster information */ - _xform_write_cb_t cb, /* I - Write callback */ - void *ctx) /* I - Write context */ -{ - (void)cb; - (void)ctx; +extern char **environ; - cupsRasterClose(ras->ras); -} +static int /* O - Number of options */ +load_env_options( + cups_option_t **options) /* I - Options */ +{ + int i; /* Looping var */ + char name[256], /* Option name */ + *nameptr, /* Pointer into name */ + *envptr; /* Pointer into environment variable */ + int num_options = 0; /* Number of options */ -/* - * 'raster_end_page()' - End of raster page. - */ + *options = NULL; -static void -raster_end_page(xform_ctx_t *ras, /* I - Raster information */ - unsigned page, /* I - Current page */ - _xform_write_cb_t cb, /* I - Write callback */ - void *ctx) /* I - Write context */ -{ - (void)page; - (void)cb; - (void)ctx; + /* + * Load all of the IPP_xxx environment variables as options... + */ - if (ras->header.cupsBitsPerPixel == 1) + for (i = 0; environ[i]; i ++) { - free(ras->out_buffer); - ras->out_buffer = NULL; - } -} + envptr = environ[i]; + if (strncmp(envptr, "IPP_", 4)) + continue; -/* - * 'raster_init()' - Initialize callbacks for raster output. - */ + for (nameptr = name, envptr += 4; *envptr && *envptr != '='; envptr ++) + { + if (nameptr > (name + sizeof(name) - 1)) + continue; -static void -raster_init(xform_ctx_t *ras) /* I - Raster information */ -{ - ras->end_job = raster_end_job; - ras->end_page = raster_end_page; - ras->start_job = raster_start_job; - ras->start_page = raster_start_page; - ras->write_line = raster_write_line; -} + if (*envptr == '_') + *nameptr++ = '-'; + else + *nameptr++ = (char)_cups_tolower(*envptr); + } + *nameptr = '\0'; + if (*envptr == '=') + envptr ++; -/* - * 'raster_start_job()' - Start a raster "job". - */ + num_options = cupsAddOption(name, envptr, num_options, options); + } -static void -raster_start_job(xform_ctx_t *ras, /* I - Raster information */ - _xform_write_cb_t cb, /* I - Write callback */ - void *ctx) /* I - Write context */ -{ - ras->ras = cupsRasterOpenIO((cups_raster_iocb_t)cb, ctx, !strcmp(ras->format, "image/pwg-raster") ? CUPS_RASTER_WRITE_PWG : CUPS_RASTER_WRITE_APPLE); + return (num_options); } /* - * 'raster_start_page()' - Start a raster page. + * 'monitor_ipp()' - Monitor IPP printer status. */ -static void -raster_start_page(xform_ctx_t *ras,/* I - Raster information */ - unsigned page,/* I - Current page */ - _xform_write_cb_t cb, /* I - Write callback */ - void *ctx)/* I - Write context */ +static void * /* O - Thread exit status */ +monitor_ipp(const char *device_uri) /* I - Device URI */ { - (void)cb; - (void)ctx; + int i; /* Looping var */ + http_t *http; /* HTTP connection */ + ipp_t *request, /* IPP request */ + *response; /* IPP response */ + ipp_attribute_t *attr; /* IPP response attribute */ + char scheme[32], /* URI scheme */ + userpass[256], /* URI user:pass */ + host[256], /* URI host */ + resource[1024]; /* URI resource */ + int port; /* URI port number */ + http_encryption_t encryption; /* Encryption to use */ + int delay = 1, /* Current delay */ + next_delay, /* Next delay */ + prev_delay = 0; /* Previous delay */ + char pvalues[10][1024]; /* Current printer attribute values */ + static const char * const pattrs[10] =/* Printer attributes we need */ + { + "marker-colors", + "marker-levels", + "marker-low-levels", + "marker-high-levels", + "marker-names", + "marker-types", + "printer-alert", + "printer-state-reasons", + "printer-supply", + "printer-supply-description" + }; - ras->left = 0; - ras->top = 0; - ras->right = ras->header.cupsWidth - 1; - ras->bottom = ras->header.cupsHeight - 1; - if (ras->header.Duplex && !(page & 1)) - cupsRasterWriteHeader2(ras->ras, &ras->back_header); + httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)); + + if (port == 443 || !strcmp(scheme, "ipps")) + encryption = HTTP_ENCRYPTION_ALWAYS; else - cupsRasterWriteHeader2(ras->ras, &ras->header); + encryption = HTTP_ENCRYPTION_IF_REQUESTED; - if (ras->header.cupsBitsPerPixel == 1) + while ((http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL) { - ras->out_length = ras->header.cupsBytesPerLine; - ras->out_buffer = malloc(ras->header.cupsBytesPerLine); + fprintf(stderr, "ERROR: Unable to connect to \"%s\" on port %d: %s\n", host, port, cupsLastErrorString()); + sleep(30); } -} - - -/* - * 'raster_write_line()' - Write a line of raster data. - */ -static void -raster_write_line( - xform_ctx_t *ras, /* I - Raster information */ - unsigned y, /* I - Line number */ - const unsigned char *line, /* I - Pixels on line */ - _xform_write_cb_t cb, /* I - Write callback */ - void *ctx) /* I - Write context */ -{ - (void)cb; - (void)ctx; + /* + * Report printer state changes until we are canceled... + */ - if (ras->header.cupsBitsPerPixel == 1) + for (;;) { /* - * Dither the line into the output buffer... + * Poll for the current state... */ - unsigned x; /* Column number */ - unsigned char bit, /* Current bit */ - byte, /* Current byte */ - *outptr; /* Pointer into output buffer */ + request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs); - y &= 63; + response = cupsDoRequest(http, request, resource); - if (ras->header.cupsColorSpace == CUPS_CSPACE_SW) - { - for (x = ras->left, bit = 128, byte = 0, outptr = ras->out_buffer; x <= ras->right; x ++, line ++) - { - if (*line > threshold[x % 25][y]) - byte |= bit; + /* + * Report any differences... + */ - if (bit == 1) - { - *outptr++ = byte; - byte = 0; - bit = 128; - } - else - bit >>= 1; - } - } - else + for (attr = ippFirstAttribute(response); attr; attr = ippNextAttribute(response)) { - for (x = ras->left, bit = 128, byte = 0, outptr = ras->out_buffer; x <= ras->right; x ++, line ++) - { - if (*line <= threshold[x & 63][y]) - byte |= bit; + const char *name = ippGetName(attr); + char value[1024]; /* Name and value */ - if (bit == 1) - { - *outptr++ = byte; - byte = 0; - bit = 128; - } + + if (!name) + continue; + + for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++) + if (!strcmp(name, pattrs[i])) + break; + + if (i >= (int)(sizeof(pattrs) / sizeof(pattrs[0]))) + continue; + + ippAttributeString(attr, value, sizeof(value)); + + if (strcmp(value, pvalues[i])) + { + if (!strcmp(name, "printer-state-reasons")) + fprintf(stderr, "STATE: %s\n", value); else - bit >>= 1; + fprintf(stderr, "ATTR: %s='%s'\n", name, value); + + strlcpy(pvalues[i], value, sizeof(pvalues[i])); } } - if (bit != 128) - *outptr++ = byte; + ippDelete(response); - cupsRasterWritePixels(ras->ras, ras->out_buffer, ras->header.cupsBytesPerLine); + /* + * Sleep until the next update... + */ + + sleep((unsigned)delay); + + next_delay = (delay + prev_delay) % 12; + prev_delay = next_delay < delay ? 0 : delay; + delay = next_delay; } - else - cupsRasterWritePixels(ras->ras, (unsigned char *)line, ras->header.cupsBytesPerLine); + + return (NULL); }