]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Save work.
authorMichael R Sweet <michael.r.sweet@gmail.com>
Fri, 18 May 2018 15:36:39 +0000 (08:36 -0700)
committerMichael R Sweet <michael.r.sweet@gmail.com>
Fri, 18 May 2018 15:36:39 +0000 (08:36 -0700)
cups/xform-private.h
cups/xform.c

index 405f3dd12f7f2d6664ef5c99b611972b12d117c4..b0becc09d6e208752022b3c3c769fe705ff2db30 100644 (file)
@@ -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"
index 4535d1ca764ae7bdb0bd64c28b5198584cd633ec..056237033cc39944028fddceb1eead5ff24a2574 100644 (file)
@@ -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);
 }