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);
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
/*
- * '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);
}