]> git.ipfire.org Git - thirdparty/cups-filters.git/commitdiff
libcupsfilters, pclmtoraster: Introduced pclmtoraster() filter function 275/head
authorVikrant <vikrantmalik051@gmail.com>
Sun, 9 Aug 2020 09:20:21 +0000 (14:50 +0530)
committerVikrant <vikrantmalik051@gmail.com>
Tue, 18 Aug 2020 10:54:10 +0000 (16:24 +0530)
Makefile.am
cupsfilters/bitmap.h
cupsfilters/filter.h
cupsfilters/pclmtoraster.cxx [new file with mode: 0644]
filter/pclmtoraster.c [new file with mode: 0644]
filter/pclmtoraster.cxx [deleted file]

index 42b56d9e0831927cee17002bc1225cd8bf65a021..a2f932c3c5f00afc26e118575b11b3c0bf7dea2b 100644 (file)
@@ -291,6 +291,7 @@ libcupsfilters_la_SOURCES = \
        cupsfilters/ipp.c \
        cupsfilters/lut.c \
        cupsfilters/pack.c \
+       cupsfilters/pclmtoraster.cxx \
        cupsfilters/pdf.cxx \
        cupsfilters/pdftoippprinter.c \
        cupsfilters/pdftops.c \
@@ -869,20 +870,13 @@ urftopdf_LDADD = \
        $(LIBQPDF_LIBS)
 
 pclmtoraster_SOURCES = \
-       filter/pclmtoraster.cxx \
-       cupsfilters/bitmap.h \
-       cupsfilters/raster.h \
-       filter/unirast.h
+       filter/pclmtoraster.c
 pclmtoraster_CXXFLAGS = \
        -I$(srcdir)/cupsfilters/ \
-       -I$(srcdir)/ppd/ \
-       $(CUPS_CFLAGS) \
-       $(LIBQPDF_CFLAGS)
+       $(CUPS_CFLAGS)
 pclmtoraster_LDADD = \
        libcupsfilters.la \
-       libppd.la \
-       $(CUPS_LIBS) \
-       $(LIBQPDF_LIBS)
+       $(CUPS_LIBS)
 
 rastertopdf_SOURCES = \
        filter/rastertopdf.cpp
index 8079c06d42128e9518a6c49e6ec31438ce5ed460..6a4d9256f695daeb360b23d91aa61ce0a0953711 100644 (file)
@@ -33,7 +33,7 @@ extern "C" {
 #include <cups/raster.h>
 
 unsigned char  *convertbits            (unsigned char *src, unsigned char *dst,
-                                       unsigned int x, unsigned int y, unsigned int cupsNumColors,unsigned int m);
+                                       unsigned int x, unsigned int y, unsigned int cupsNumColors,unsigned int bits);
 void           writepixel              (unsigned char *dst, unsigned int plane, unsigned int pixeli, unsigned char *pixelBuf,
                                        unsigned int cupsNumColors, unsigned int bits, cups_order_t colororder);
 unsigned char  *reverseOneBitLine      (unsigned char *src, unsigned char *dst, unsigned int pixels, unsigned int size);
index cbd4c26d0345d3810b52b0d77b7a42e15a9df628..9cddc316967f36295eba588d867dd0fbc9ce43cf 100644 (file)
@@ -7,8 +7,8 @@
  *   which should have been included with this file.
  */
 
-#ifndef _CUPS_FILTERS_RASTER_H_
-#  define _CUPS_FILTERS_RASTER_H_
+#ifndef _CUPS_FILTERS_FILTER_H_
+#  define _CUPS_FILTERS_FILTER_H_
 
 #  ifdef __cplusplus
 extern "C" {
@@ -88,6 +88,13 @@ extern int filterCUPSWrapper(int argc,
                             void *parameters,
                             int *JobCanceled);
 
+extern int pclmtoraster(int inputfd,
+                       int outputfd,
+                       int inputseekable,
+                       int *jobcanceled,
+                       filter_data_t *data,
+                       void *parameters);
+
 extern int pdftops(int inputfd,
                  int outputfd,
                  int inputseekable,
@@ -128,7 +135,7 @@ extern void filterUpdatePageVars(int Orientation,
 }
 #  endif /* __cplusplus */
 
-#endif /* !_CUPS_FILTERS_RASTER_H_ */
+#endif /* !_CUPS_FILTERS_FILTER_H_ */
 
 /*
  * End
diff --git a/cupsfilters/pclmtoraster.cxx b/cupsfilters/pclmtoraster.cxx
new file mode 100644 (file)
index 0000000..cd934f8
--- /dev/null
@@ -0,0 +1,1124 @@
+/**
+ * This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @brief pclmtoraster filter function
+ * @file pclmtoraster.cxx
+ * @author Vikrant Malik <vikrantmalik051@gmail.com> (c) 2020
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "filter.h"
+#include <cups/raster.h>
+#include <cups/cups.h>
+#include <ppd/ppd.h>
+#include <errno.h>
+#include <qpdf/QPDF.hh>
+#include <qpdf/QPDFObjectHandle.hh>
+#include "image.h"
+#include "bitmap.h"
+#include "raster.h"
+#include <cupsfilters/filter.h>
+
+
+#if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 6)
+#define HAVE_CUPS_1_7 1
+#endif
+#define MAX_BYTES_PER_PIXEL 32
+
+typedef struct pclmtoraster_data_s
+{
+  int pwgraster = 0;
+  int numcolors = 0;
+  int rowsize = 0;
+  cups_page_header2_t header;
+  ppd_file_t *ppd = 0;
+  char pageSizeRequested[64];
+  int bi_level = 0;
+  /* image swapping */
+  bool swap_image_x = false;
+  bool swap_image_y = false;
+  /* margin swapping */
+  bool swap_margin_x = false;
+  bool swap_margin_y = false;
+  unsigned int nplanes;
+  unsigned int nbands;
+  unsigned int bytesPerLine; /* number of bytes per line */
+  /* Note: When CUPS_ORDER_BANDED, cupsBytesPerLine = bytesPerLine*cupsNumColors */
+  std::string colorspace; /* Colorspace of raster data */
+} pclmtoraster_data_t;
+
+typedef unsigned char *(*ConvertCSpace)(unsigned char *src, unsigned char *dst, unsigned int row,
+                                       unsigned int pixels, pclmtoraster_data_t *data);
+typedef unsigned char *(*ConvertLine)  (unsigned char *src, unsigned char *dst, unsigned char *buf,
+                                       unsigned int row, unsigned int plane, pclmtoraster_data_t *data,
+                                       ConvertCSpace convertcspace);
+
+typedef struct conversion_function_s
+{
+  ConvertCSpace convertcspace; /* Function for conversion of colorspaces */
+  ConvertLine convertline;     /* Function tom modify raster data of a line */
+} conversion_function_t;
+
+static void
+parseOpts(filter_data_t *data,
+         pclmtoraster_data_t *pclmtoraster_data)
+{
+  int                  num_options = 0;
+  cups_option_t*       options = NULL;
+  const char*          t = NULL;
+  ppd_attr_t*          attr;
+  const char           *val;
+  filter_logfunc_t     log = data->logfunc;
+  void                 *ld = data->logdata;
+  ppd_file_t           *ppd = pclmtoraster_data->ppd;
+  cups_page_header2_t  *header = &(pclmtoraster_data->header);
+
+#ifdef HAVE_CUPS_1_7
+  t = getenv("FINAL_CONTENT_TYPE");
+  if (t && strcasestr(t, "pwg"))
+    pclmtoraster_data->pwgraster = 1;
+#endif /* HAVE_CUPS_1_7 */
+ /*
+  * CUPS option list
+  */
+
+  num_options = data->num_options;
+  options = data->options;
+
+ /*
+  * Load PPD file if needed...
+  */
+
+  if (data->ppdfile == NULL && data->ppd == NULL)
+  {
+    char *p = getenv("PPD");
+    if (p)
+      data->ppdfile = strdup(p);
+    else
+      data->ppdfile = NULL;
+  }
+
+  if (data->ppd == NULL && data->ppdfile)
+    data->ppd = ppdOpenFile(data->ppdfile);
+
+  ppd = data->ppd;
+
+  if (ppd == NULL && log)
+      log(ld, FILTER_LOGLEVEL_DEBUG,
+         "pclmtoraster: PPD file is not specified.\n");
+  if (ppd)
+    ppdMarkDefaults(ppd);
+
+  if (ppd)
+  {
+    ppdMarkDefaults(ppd);
+    ppdMarkOptions(ppd,num_options,options);
+    ppdRasterInterpretPPD(header,ppd,num_options,options,0);
+    if (header->Duplex)
+    {
+      /* analyze options relevant to Duplex */
+      const char *backside = "";
+      /* APDuplexRequiresFlippedMargin */
+      enum {
+        FM_NO, FM_FALSE, FM_TRUE
+      } flippedMargin = FM_NO;
+
+      attr = ppdFindAttr(ppd,"cupsBackSide",NULL);
+      if (attr != NULL && attr->value != NULL)
+      {
+        ppd->flip_duplex = 0;
+        backside = attr->value;
+      }
+      else if (ppd->flip_duplex)
+      {
+        backside = "Rotated"; /* compatible with Max OS and GS 8.71 */
+      }
+
+      attr = ppdFindAttr(ppd,"APDuplexRequiresFlippedMargin",NULL);
+      if (attr != NULL && attr->value != NULL)
+      {
+        if (strcasecmp(attr->value,"true") == 0)
+       {
+          flippedMargin = FM_TRUE;
+        }
+       else
+       {
+          flippedMargin = FM_FALSE;
+        }
+      }
+      if (strcasecmp(backside,"ManualTumble") == 0 && header->Tumble)
+      {
+        pclmtoraster_data->swap_image_x = pclmtoraster_data->swap_image_y = true;
+        pclmtoraster_data->swap_margin_x = pclmtoraster_data->swap_margin_y = true;
+        if (flippedMargin == FM_TRUE)
+       {
+          pclmtoraster_data->swap_margin_y = false;
+        }
+      }
+      else if (strcasecmp(backside,"Rotated") == 0 && !header->Tumble)
+      {
+        pclmtoraster_data->swap_image_x = pclmtoraster_data->swap_image_y = true;
+        pclmtoraster_data->swap_margin_x = pclmtoraster_data->swap_margin_y = true;
+        if (flippedMargin == FM_TRUE)
+       {
+          pclmtoraster_data->swap_margin_y = false;
+        }
+      }
+      else if (strcasecmp(backside,"Flipped") == 0)
+      {
+        if (header->Tumble)
+       {
+          pclmtoraster_data->swap_image_x = true;
+          pclmtoraster_data->swap_margin_x = pclmtoraster_data->swap_margin_y = true;
+        }
+       else
+       {
+          pclmtoraster_data->swap_image_y = true;
+        }
+        if (flippedMargin == FM_FALSE)
+       {
+          pclmtoraster_data->swap_margin_y = !(pclmtoraster_data->swap_margin_y);
+        }
+      }
+    }
+
+#ifdef HAVE_CUPS_1_7
+    if ((attr = ppdFindAttr(ppd,"PWGRaster",0)) != 0 &&
+        (!strcasecmp(attr->value, "true")
+         || !strcasecmp(attr->value, "on") ||
+         !strcasecmp(attr->value, "yes")))
+      pclmtoraster_data->pwgraster = 1;
+    if (pclmtoraster_data->pwgraster == 1)
+      cupsRasterParseIPPOptions(header, num_options, options, pclmtoraster_data->pwgraster, 0);
+#endif /* HAVE_CUPS_1_7 */
+  } else {
+#ifdef HAVE_CUPS_1_7
+    pclmtoraster_data->pwgraster = 1;
+    t = cupsGetOption("media-class", num_options, options);
+    if (t == NULL)
+      t = cupsGetOption("MediaClass", num_options, options);
+    if (t != NULL)
+    {
+      if (strcasestr(t, "pwg"))
+        pclmtoraster_data->pwgraster = 1;
+      else
+        pclmtoraster_data->pwgraster = 0;
+    }
+    cupsRasterParseIPPOptions(header,num_options,options,pclmtoraster_data->pwgraster,1);
+#else
+    if (log) log(ld, FILTER_LOGLEVEL_ERROR,
+               "pclmtoraster: No PPD file specified: %s\n", strerror(errno));
+    exit(1);
+#endif /* HAVE_CUPS_1_7 */
+  }
+  if ((val = cupsGetOption("print-color-mode", num_options, options)) != NULL
+                           && !strncasecmp(val, "bi-level", 8))
+    pclmtoraster_data->bi_level = 1;
+
+  strncpy(pclmtoraster_data->pageSizeRequested, header->cupsPageSizeName, 64);
+  if (log) log(ld, FILTER_LOGLEVEL_DEBUG,
+               "pclmtoraster: Page size requested: %s.\n", header->cupsPageSizeName);
+}
+
+static bool
+mediaboxlookup(QPDFObjectHandle object,
+              float rect[4])
+{
+  // preliminary checks
+  if (!object.isDictionary() || !object.hasKey("/MediaBox"))
+    return false;
+
+  // assign mediabox values to rect
+  std::vector<QPDFObjectHandle> mediabox = object.getKey("/MediaBox").getArrayAsVector();
+  for (int i = 0; i < 4; ++i)
+  {
+    rect[i] = mediabox[i].getNumericValue();
+  }
+
+  return mediabox.size() == 4;
+}
+
+/*
+ * 'rotatebitmap()' - Function to rotate a bitmap
+ *                    (assumed that bits-per-component of the bitmap is 8).
+ */
+
+static unsigned char *             /* O - Output Bitmap */
+rotatebitmap(unsigned char *src,    /* I - Input string */
+            unsigned char *dst,    /* O - Destination string */
+            unsigned int rotate,   /* I - Rotate value (0, 90, 180, 270) */
+            unsigned int height,   /* I - Height of raster image in pixels */
+            unsigned int width,    /* I - Width of raster image in pixels */
+            int rowsize,           /* I - Length of one row of pixels */
+            std::string colorspace,/* I - Colorspace of input bitmap */
+            filter_logfunc_t log,  /* I - Log function */
+            void *ld)              /* I - Aux. data for log function */
+{
+  unsigned char *bp = src;
+  unsigned char *dp = dst;
+  unsigned char *temp = dst;
+
+  if (rotate == 0)
+  {
+    return src;
+  }
+  else if (rotate == 180)
+  {
+    if (colorspace == "/DeviceGray")
+    {
+      bp = src + height * rowsize - 1;
+      dp = dst;
+      for (unsigned int h = 0; h < height; h++)
+      {
+        for (unsigned int w = 0; w < width; w++, bp --, dp ++)
+       {
+          *dp = *bp;
+        }
+      }
+    }
+    else if (colorspace == "/DeviceCMYK")
+    {
+      bp = src + height * rowsize - 4;
+      dp = dst;
+      for (unsigned int h = 0; h < height; h++)
+      {
+        for (unsigned int w = 0; w < width; w++, bp -= 4, dp += 4)
+       {
+          dp[0] = bp[0];
+          dp[1] = bp[1];
+          dp[2] = bp[2];
+          dp[3] = bp[3];
+        }
+      }
+    }
+    else if (colorspace == "/DeviceRGB")
+    {
+      bp = src + height * rowsize - 3;
+      dp = dst;
+      for (unsigned int h = 0; h < height; h++)
+      {
+        for (unsigned int w = 0; w < width; w++, bp -= 3, dp += 3)
+       {
+          dp[0] = bp[0];
+          dp[1] = bp[1];
+          dp[2] = bp[2];
+        }
+      }
+    }
+  }
+  else if (rotate == 270)
+  {
+    if (colorspace == "/DeviceGray")
+    {
+      bp = src;
+      dp = dst;
+      for (unsigned int h = 0; h < height; h++)
+      {
+        bp = src + (height - h) - 1;
+        for (unsigned int w = 0; w < width; w++, bp += height , dp ++)
+       {
+          *dp = *bp;
+        }
+      }
+    }
+    else if (colorspace == "/DeviceCMYK")
+    {
+      for (unsigned int h = 0; h < height; h++)
+      {
+        bp = src + (height - h)*4 - 4;
+        for (unsigned int i = 0; i < width; i++, bp += height*4 , dp += 4)
+       {
+          dp[0] = bp[0];
+          dp[1] = bp[1];
+          dp[2] = bp[2];
+          dp[3] = bp[3];
+        }
+      }
+    }
+    else if (colorspace == "/DeviceRGB")
+    {
+      bp = src;
+      dp = dst;
+      for (unsigned int h = 0; h < height; h++)
+      {
+        bp = src + (height - h)*3 - 3;
+        for (unsigned int i = 0; i < width; i++, bp += height*3 , dp += 3)
+       {
+          dp[0] = bp[0];
+          dp[1] = bp[1];
+          dp[2] = bp[2];
+        }
+      }
+    }
+  }
+  else if (rotate == 90)
+  {
+    if (colorspace == "/DeviceGray")
+    {
+      for (unsigned int h = 0; h < height; h++)
+      {
+        bp = src + (width - 1) * height + h;
+        for (unsigned int i = 0; i < width; i++, bp -= height , dp ++)
+       {
+          *dp = *bp;
+        }
+      }
+    }
+    else if (colorspace == "/DeviceCMYK")
+    {
+      for (unsigned int h = 0; h < height; h++)
+      {
+        bp = src + (width - 1) * height * 4 + 4*h;
+        for (unsigned int i = 0; i < width; i++, bp -= height*4 , dp += 4)
+       {
+          dp[0] = bp[0];
+          dp[1] = bp[1];
+          dp[2] = bp[2];
+          dp[3] = bp[3];
+        }
+      }
+    }
+    else if (colorspace == "/DeviceRGB")
+    {
+      for (unsigned int h = 0; h < height; h++)
+      {
+       bp = src + (width - 1) * height * 3 + 3*h;
+        for (unsigned int i = 0; i < width; i++, bp -= height*3 , dp += 3)
+       {
+          dp[0] = bp[0];
+          dp[1] = bp[1];
+          dp[2] = bp[2];
+        }
+      }
+    }
+  }
+  else
+  {
+    if (log) log(ld, FILTER_LOGLEVEL_ERROR,
+                "pclmtoraster: Incorrect Rotate Value %d\n", rotate);
+    exit(1);
+  }
+
+  return temp;
+}
+
+static unsigned char *
+RGBtoCMYKLine(unsigned char *src,
+             unsigned char *dst,
+             unsigned int row,
+             unsigned int pixels,
+             pclmtoraster_data_t *data)
+{
+  cupsImageRGBToCMYK(src,dst,pixels);
+  return dst;
+}
+
+static unsigned char *
+RGBtoCMYLine(unsigned char *src,
+            unsigned char *dst,
+            unsigned int row,
+            unsigned int pixels,
+            pclmtoraster_data_t *data)
+{
+  cupsImageRGBToCMY(src,dst,pixels);
+  return dst;
+}
+
+static unsigned char *
+RGBtoWhiteLine(unsigned char *src,
+              unsigned char *dst,
+              unsigned int row,
+              unsigned int pixels,
+              pclmtoraster_data_t *data)
+{
+  if (data->header.cupsBitsPerColor != 1) {
+    cupsImageRGBToWhite(src,dst,pixels);
+  } else {
+    cupsImageRGBToWhite(src,src,pixels);
+    oneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level);
+  }
+
+  return dst;
+}
+
+static unsigned char *
+RGBtoBlackLine(unsigned char *src,
+              unsigned char *dst,
+              unsigned int row,
+              unsigned int pixels,
+              pclmtoraster_data_t *data)
+{
+  if (data->header.cupsBitsPerColor != 1) {
+    cupsImageRGBToBlack(src,dst,pixels);
+  } else {
+    cupsImageRGBToBlack(src,src,pixels);
+    oneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level);
+  }
+  return dst;
+}
+
+static unsigned char *
+CMYKtoRGBLine(unsigned char *src,
+             unsigned char *dst,
+             unsigned int row,
+             unsigned int pixels,
+             pclmtoraster_data_t *data)
+{
+  cupsImageCMYKToRGB(src,dst,pixels);
+  return dst;
+}
+
+static unsigned char *
+CMYKtoCMYLine(unsigned char *src,
+             unsigned char *dst,
+             unsigned int row,
+             unsigned int pixels,
+             pclmtoraster_data_t *data)
+{
+  // Converted first to rgb and then to cmy for better outputs.
+  cupsImageCMYKToRGB(src,src,pixels);
+  cupsImageRGBToCMY(src,dst,pixels);
+  return dst;
+}
+
+static unsigned char *
+CMYKtoWhiteLine(unsigned char *src,
+               unsigned char *dst,
+               unsigned int row,
+               unsigned int pixels,
+               pclmtoraster_data_t *data)
+{
+  if (data->header.cupsBitsPerColor != 1) {
+    cupsImageCMYKToWhite(src,dst,pixels);
+  } else {
+    cupsImageCMYKToWhite(src,src,pixels);
+    oneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level);
+  }
+  return dst;
+}
+
+static unsigned char *
+CMYKtoBlackLine(unsigned char *src,
+               unsigned char *dst,
+               unsigned int row,
+               unsigned int pixels,
+               pclmtoraster_data_t *data)
+{
+  if (data->header.cupsBitsPerColor != 1) {
+    cupsImageCMYKToBlack(src,dst,pixels);
+  } else {
+    cupsImageCMYKToBlack(src,src,pixels);
+    oneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level);
+  }
+  return dst;
+}
+
+static unsigned char *
+GraytoRGBLine(unsigned char *src,
+             unsigned char *dst,
+             unsigned int row,
+             unsigned int pixels,
+             pclmtoraster_data_t *data)
+{
+  cupsImageWhiteToRGB(src,dst,pixels);
+  return dst;
+}
+
+static unsigned char *
+GraytoCMYKLine(unsigned char *src,
+              unsigned char *dst,
+              unsigned int row,
+              unsigned int pixels,
+             pclmtoraster_data_t *data)
+{
+  cupsImageWhiteToCMYK(src,dst,pixels);
+  return dst;
+}
+
+static unsigned char *
+GraytoCMYLine(unsigned char *src,
+             unsigned char *dst,
+             unsigned int row,
+             unsigned int pixels,
+             pclmtoraster_data_t *data)
+{
+  cupsImageWhiteToCMY(src,dst,pixels);
+  return dst;
+}
+
+static unsigned char *
+GraytoBlackLine(unsigned char *src,
+               unsigned char *dst,
+               unsigned int row,
+               unsigned int pixels,
+               pclmtoraster_data_t *data)
+{
+  if (data->header.cupsBitsPerColor != 1) {
+    cupsImageWhiteToBlack(src, dst, pixels);
+  } else {
+    cupsImageWhiteToBlack(src, src, pixels);
+    oneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level);
+  }
+  return dst;
+}
+
+static unsigned char *
+convertcspaceNoop(unsigned char *src,
+                 unsigned char *dst,
+                 unsigned int row,
+                 unsigned int pixels,
+                 pclmtoraster_data_t *data)
+{
+  return src;
+}
+
+/*
+ * 'convertLine()' - Function to convert colorspace and bits-per-pixel
+ *                   of a single line of raster data.
+ */
+
+static unsigned char *                 /* O - Output string */
+convertLine(unsigned char      *src,   /* I - Input line */
+           unsigned char       *dst,   /* O - Destination string */
+           unsigned char       *buf,   /* I - Buffer string */
+           unsigned int        row,    /* I - Current Row */
+           unsigned int        plane,  /* I - Plane/Band */
+           pclmtoraster_data_t *data,
+           ConvertCSpace       convertcspace)
+{
+  /*
+   Use only convertcspace if conversion of bits and conversion of color order
+   is not required, or if dithering is required, for faster processing of raster output.
+   */
+  unsigned int pixels = data->header.cupsWidth;
+  if ((data->header.cupsBitsPerColor == 1
+       && data->header.cupsNumColors == 1)
+       || (data->header.cupsBitsPerColor == 8
+       && data->header.cupsColorOrder == CUPS_ORDER_CHUNKED))
+  {
+    dst = convertcspace(src, dst, row, pixels, data);
+  }
+  else
+  {
+    for (unsigned int i = 0;i < pixels;i++)
+    {
+      unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL];
+      unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL];
+      unsigned char *pb;
+      pb = convertcspace(src + i*(data->numcolors), pixelBuf1, row, 1, data);
+      pb = convertbits(pb, pixelBuf2, i, row, data->header.cupsNumColors, data->header.cupsBitsPerColor);
+      writepixel(dst, plane, i, pb, data->header.cupsNumColors, data->header.cupsBitsPerColor, data->header.cupsColorOrder);
+    }
+  }
+  return dst;
+}
+
+/*
+ * 'convertReverseLine()' - Function to convert colorspace and bits-per-pixel
+ *                          of a single line of raster data and reverse the line.
+ */
+
+static unsigned char *                                 /* O - Output string */
+convertReverseLine(unsigned char       *src,           /* I - Input line */
+                  unsigned char        *dst,           /* O - Destination string */
+                  unsigned char        *buf,           /* I - Buffer string */
+                  unsigned int         row,            /* I - Current Row */
+                  unsigned int         plane,          /* I - Plane/Band */
+                  pclmtoraster_data_t *data,           /* I - pclmtoraster filter data */
+                  ConvertCSpace        convertcspace)  /* I - Function for conversion of colorspace */
+{
+  /*
+   Use only convertcspace if conversion of bits and conversion of color order
+   is not required, or if dithering is required, for faster processing of raster output.
+  */
+  unsigned int pixels = data->header.cupsWidth;
+  if (data->header.cupsBitsPerColor == 1 && data->header.cupsNumColors == 1)
+  {
+    buf = convertcspace(src, buf, row, pixels, data);
+    dst = reverseOneBitLine(buf, dst, pixels, data->bytesPerLine);
+  }
+  else if (data->header.cupsBitsPerColor == 8 && data->header.cupsColorOrder == CUPS_ORDER_CHUNKED)
+  {
+    unsigned char *dp = dst;
+    // Assign each pixel of buf to dst in the reverse order.
+    buf = convertcspace(src, buf, row, pixels, data) + (data->header.cupsWidth - 1)*data->header.cupsNumColors;
+    for (unsigned int i = 0; i < pixels; i++, buf-=data->header.cupsNumColors, dp+=data->header.cupsNumColors)
+    {
+      for (unsigned int j = 0; j < data->header.cupsNumColors; j++)
+      {
+       dp[j] = buf[j];
+      }
+    }
+  }
+  else
+  {
+    for (unsigned int i = 0;i < pixels;i++)
+    {
+      unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL];
+      unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL];
+      unsigned char *pb;
+      pb = convertcspace(src + (pixels - i - 1)*(data->numcolors), pixelBuf1, row, 1, data);
+      pb = convertbits(pb, pixelBuf2, i, row, data->header.cupsNumColors, data->header.cupsBitsPerColor);
+      writepixel(dst, plane, i, pb, data->header.cupsNumColors, data->header.cupsBitsPerColor, data->header.cupsColorOrder);
+    }
+  }
+  return dst;
+}
+
+static void                                     /* O - Exit status */
+selectConvertFunc(int                  pgno,    /* I - Page number */
+                 filter_logfunc_t      log,     /* I - Log function */
+                 void                  *ld,     /* I - Aux. data for log function */
+                 pclmtoraster_data_t   *data,   /* I - pclmtoraster filter data */
+                 conversion_function_t *convert)/* I - Conversion functions */
+{
+  /* Set rowsize and numcolors based on colorspace of raster data */
+  cups_page_header2_t header = data->header;
+  std::string colorspace = data->colorspace;
+  if (colorspace == "/DeviceRGB")
+  {
+    data->rowsize = header.cupsWidth*3;
+    data->numcolors = 3;
+  }
+  else if (colorspace == "/DeviceCMYK")
+  {
+    data->rowsize = header.cupsWidth*4;
+    data->numcolors = 4;
+  }
+  else if (colorspace == "/DeviceGray")
+  {
+    data->rowsize = header.cupsWidth;
+    data->numcolors = 1;
+  }
+  else
+  {
+    if (log) log(ld, FILTER_LOGLEVEL_ERROR,
+                "pclmtoraster: Colorspace %s not supported\n", colorspace.c_str());
+    exit(1);
+  }
+
+  convert->convertcspace = convertcspaceNoop; //Default function
+  /* Select convertcspace function */
+  switch (header.cupsColorSpace)
+  {
+    case CUPS_CSPACE_K:
+     if (colorspace == "/DeviceRGB") convert->convertcspace = RGBtoBlackLine;
+     else if (colorspace == "/DeviceCMYK") convert->convertcspace = CMYKtoBlackLine;
+     else if (colorspace == "/DeviceGray") convert->convertcspace = GraytoBlackLine;
+     break;
+    case CUPS_CSPACE_W:
+    case CUPS_CSPACE_SW:
+     if (colorspace == "/DeviceRGB") convert->convertcspace = RGBtoWhiteLine;
+     else if (colorspace == "/DeviceCMYK") convert->convertcspace = CMYKtoWhiteLine;
+     break;
+    case CUPS_CSPACE_CMY:
+     if (colorspace == "/DeviceRGB") convert->convertcspace = RGBtoCMYLine;
+     else if (colorspace == "/DeviceCMYK") convert->convertcspace = CMYKtoCMYLine;
+     else if (colorspace == "/DeviceGray") convert->convertcspace = GraytoCMYLine;
+     break;
+    case CUPS_CSPACE_CMYK:
+     if (colorspace == "/DeviceRGB") convert->convertcspace = RGBtoCMYKLine;
+     else if (colorspace == "/DeviceGray") convert->convertcspace = GraytoCMYKLine;
+     break;
+    case CUPS_CSPACE_RGB:
+    case CUPS_CSPACE_ADOBERGB:
+    case CUPS_CSPACE_SRGB:
+    default:
+     if (colorspace == "/DeviceCMYK") convert->convertcspace = CMYKtoRGBLine;
+     else if (colorspace == "/DeviceGray") convert->convertcspace = GraytoRGBLine;
+     break;
+   }
+
+  /* Select convertline function */
+  if (header.Duplex && (pgno & 1) && data->swap_image_x)
+  {
+    convert->convertline = convertReverseLine;
+  }
+  else
+  {
+    convert->convertline = convertLine;
+  }
+
+}
+
+/*
+ * 'outPage()' - Function to convert a single page of raster-only PDF/PCLm input to
+ *              CUPS/PWG Raster.
+ */
+
+static void                            /* O - Exit status */
+outPage(cups_raster_t*  raster,        /* I - Raster stream */
+       QPDFObjectHandle page,          /* I - QPDF Page Object */
+       int              pgno,          /* I - Page number */
+       filter_logfunc_t log,           /* I - Log function */
+       void*            ld,            /* I - Aux. data for log function */
+       pclmtoraster_data_t *data,      /* I - pclmtoraster filter data */
+       conversion_function_t *convert) /* I - Conversion functions */
+{
+  long long            rotate = 0,
+                       height,
+                       width;
+  double               paperdimensions[2], margins[4], l, swap;
+  int                  bufsize = 0, pixel_count = 0,
+                       temp = 0;
+  float                mediaBox[4];
+  unsigned char        *bitmap = NULL,
+                       *colordata = NULL,
+                       *lineBuf = NULL,
+                       *line = NULL,
+                       *dp = NULL;
+  QPDFObjectHandle     image;
+  QPDFObjectHandle     imgdict;
+  QPDFObjectHandle     colorspace_obj;
+  ppd_file_t           *ppd = data->ppd;
+
+  // Check if page is rotated.
+  if (page.getKey("/Rotate").isInteger())
+    rotate = page.getKey("/Rotate").getIntValueAsInt();
+
+  // Get pagesize by the mediabox key of the page.
+  if (!mediaboxlookup(page, mediaBox))
+  {
+    if (log) log(ld, FILTER_LOGLEVEL_ERROR,
+                "pclmtoraster: PDF page %d doesn't contain a valid mediaBox\n", pgno + 1);
+    return;
+  }
+  else
+  {
+    if (log) log(ld, FILTER_LOGLEVEL_DEBUG,
+                "pclmtoraster: mediaBox = [%f %f %f %f]: \n", mediaBox[0], mediaBox[1], mediaBox[2], mediaBox[3]);
+    l = mediaBox[2] - mediaBox[0];
+    if (l < 0) l = -l;
+    if (rotate == 90 || rotate == 270)
+      data->header.PageSize[1] = (unsigned)l;
+    else
+      data->header.PageSize[0] = (unsigned)l;
+    l = mediaBox[3] - mediaBox[1];
+    if (l < 0) l = -l;
+    if (rotate == 90 || rotate == 270)
+      data->header.PageSize[0] = (unsigned)l;
+    else
+      data->header.PageSize[1] = (unsigned)l;
+  }
+
+  // Adjust header page size and margins according to the ppd file.
+  if (ppd)
+  {
+    ppdRasterMatchPPDSize(&(data->header), ppd, margins, paperdimensions, NULL, NULL);
+    if (data->pwgraster == 1)
+      memset(margins, 0, sizeof(margins));
+  }
+  else
+  {
+    for (int i = 0; i < 2; i ++)
+      paperdimensions[i] = data->header.PageSize[i];
+    if (data->header.cupsImagingBBox[3] > 0.0)
+    {
+      /* Set margins if we have a bounding box defined ... */
+      if (data->pwgraster == 0)
+      {
+       margins[0] = data->header.cupsImagingBBox[0];
+       margins[1] = data->header.cupsImagingBBox[1];
+       margins[2] = paperdimensions[0] - data->header.cupsImagingBBox[2];
+       margins[3] = paperdimensions[1] - data->header.cupsImagingBBox[3];
+      }
+    }
+    else
+      /* ... otherwise use zero margins */
+      for (int i = 0; i < 4; i ++)
+       margins[i] = 0.0;
+  }
+
+  if (data->header.Duplex && (pgno & 1))
+  {
+    /* backside: change margin if needed */
+    if (data->swap_margin_x)
+    {
+      swap = margins[2]; margins[2] = margins[0]; margins[0] = swap;
+    }
+    if (data->swap_margin_y)
+    {
+      swap = margins[3]; margins[3] = margins[1]; margins[1] = swap;
+    }
+  }
+
+  /* write page header */
+  for (int i = 0; i < 2; i ++)
+  {
+    data->header.cupsPageSize[i] = paperdimensions[i];
+    data->header.PageSize[i] = (unsigned int)(data->header.cupsPageSize[i] + 0.5);
+    if (data->pwgraster == 0)
+      data->header.Margins[i] = margins[i] + 0.5;
+    else
+      data->header.Margins[i] = 0;
+  }
+  if (data->pwgraster == 0)
+  {
+    data->header.cupsImagingBBox[0] = margins[0];
+    data->header.cupsImagingBBox[1] = margins[1];
+    data->header.cupsImagingBBox[2] = paperdimensions[0] - margins[2];
+    data->header.cupsImagingBBox[3] = paperdimensions[1] - margins[3];
+    for (int i = 0; i < 4; i ++)
+      data->header.ImagingBoundingBox[i] = (unsigned int)(data->header.cupsImagingBBox[i] + 0.5);
+  }
+  else
+  {
+    for (int i = 0; i < 4; i ++)
+    {
+      data->header.cupsImagingBBox[i] = 0.0;
+      data->header.ImagingBoundingBox[i] = 0;
+    }
+  }
+
+  data->header.cupsWidth = 0;
+  data->header.cupsHeight = 0;
+
+  /* Loop over all raster images in a page and store them in bitmap. */
+  std::map<std::string, QPDFObjectHandle> images = page.getPageImages();
+  for (auto const& iter: images)
+  {
+    image = iter.second;
+    imgdict = image.getDict(); //XObject dictionary
+
+    PointerHolder<Buffer> actual_data = image.getStreamData(qpdf_dl_all);
+    width = imgdict.getKey("/Width").getIntValue();
+    height = imgdict.getKey("/Height").getIntValue();
+    colorspace_obj = imgdict.getKey("/ColorSpace");
+    data->header.cupsHeight += height;
+    bufsize = actual_data->getSize();
+
+    if(!pixel_count)
+    {
+      bitmap = (unsigned char *) malloc(bufsize);
+    }
+    else
+    {
+      bitmap = (unsigned char *) realloc(bitmap, pixel_count + bufsize);
+    }
+    memcpy(bitmap + pixel_count, actual_data->getBuffer(), bufsize);
+    pixel_count += bufsize;
+
+    if (width > data->header.cupsWidth)
+      data->header.cupsWidth = width;
+  }
+
+  // Swap width and height in landscape images
+  if (rotate == 270 || rotate == 90)
+  {
+    temp = data->header.cupsHeight;
+    data->header.cupsHeight = data->header.cupsWidth;
+    data->header.cupsWidth = temp;
+  }
+
+  data->bytesPerLine = data->header.cupsBytesPerLine = (data->header.cupsBitsPerPixel * data->header.cupsWidth + 7) / 8;
+  if (data->header.cupsColorOrder == CUPS_ORDER_BANDED)
+    data->header.cupsBytesPerLine *= data->header.cupsNumColors;
+
+  if (!cupsRasterWriteHeader2(raster,&(data->header)))
+  {
+    if (log) log(ld, FILTER_LOGLEVEL_ERROR,
+                "pclmtoraster: Can't write page %d header\n", pgno + 1);
+    exit(1);
+  }
+
+  data->colorspace = (colorspace_obj.isName() ? colorspace_obj.getName() : "/DeviceRGB"); // Default for pclm files in DeviceRGB
+
+  /* Select convertline and convertscpace function */
+  selectConvertFunc(pgno, log, ld, data, convert);
+
+  // If page is to be swapped in both x and y, rotate it by 180 degress
+  if (data->header.Duplex && (pgno & 1) && data->swap_image_y && data->swap_image_x)
+  {
+    rotate = (rotate + 180) % 360;
+    data->swap_image_y = false;
+    data->swap_image_x = false;
+  }
+
+  /* Rotate Bitmap */
+  if (rotate)
+  {
+    unsigned char *bitmap2 = (unsigned char *) malloc(pixel_count);
+    bitmap2 = rotatebitmap(bitmap, bitmap2, rotate, data->header.cupsHeight, data->header.cupsWidth, data->rowsize, data->colorspace, log, ld);
+    free(bitmap);
+    bitmap = bitmap2;
+  }
+
+  colordata = bitmap;
+
+  /* Write page image */
+  lineBuf = new unsigned char [data->bytesPerLine];
+  line = new unsigned char [data->bytesPerLine];
+  if (data->header.Duplex && (pgno & 1) && data->swap_image_y)
+  {
+    for (unsigned int plane = 0; plane < data->nplanes ; plane++)
+    {
+      unsigned char *bp = colordata + (data->header.cupsHeight - 1) * (data->rowsize);
+      for (unsigned int h = data->header.cupsHeight; h > 0; h--)
+      {
+        for (unsigned int band = 0; band < data->nbands; band++)
+       {
+          dp = convert->convertline(bp, line, lineBuf, h - 1, plane + band,
+                                   data, convert->convertcspace);
+          cupsRasterWritePixels(raster, dp, data->bytesPerLine);
+        }
+        bp -= data->rowsize;
+      }
+    }
+  }
+  else
+  {
+    for (unsigned int plane = 0; plane < data->nplanes ; plane++)
+    {
+      unsigned char *bp = colordata;
+      for (unsigned int h = 0; h < data->header.cupsHeight; h++)
+      {
+        for (unsigned int band = 0; band < data->nbands; band++)
+       {
+          dp = convert->convertline(bp, line, lineBuf, h, plane + band,
+                                   data, convert->convertcspace);
+          cupsRasterWritePixels(raster, dp, data->bytesPerLine);
+        }
+        bp += data->rowsize;
+      }
+    }
+  }
+  delete[] lineBuf;
+  delete[] line;
+  free(bitmap);
+}
+
+/*
+ * 'pclmtoraster()' - Filter function to convert raster-only PDF/PCLm input to
+ *                   CUPS/PWG Raster output.
+ */
+
+int                              /* O - Error status */
+pclmtoraster(int inputfd,         /* I - File descriptor input stream */
+            int outputfd,        /* I - File descriptor output stream */
+            int inputseekable,   /* I - Is input stream seekable? (unused) */
+            int *job_canceled,   /* I - Pointer to integer marking
+                                        whether job is canceled (unused) */
+            filter_data_t *data, /* I - Job and printer data */
+            void *parameters)    /* I - Filter-specific parameters (unused) */
+{
+  FILE                 *inputfp;               /* Input file pointer */
+  int                  fd = 0;                 /* Copy file descriptor */
+  char                 *filename,              /* PDF file to convert */
+                       tempfile[1024];         /* Temporary file */
+  char                 buffer[8192];           /* Copy buffer */
+  int                  bytes;                  /* Bytes copied */
+  filter_logfunc_t     log = data->logfunc;
+  void                 *ld = data->logdata;
+  int                  npages = 0;
+  QPDF                 *pdf = new QPDF();
+  cups_raster_t                *raster;
+  pclmtoraster_data_t  pclmtoraster_data;
+  conversion_function_t convert;
+
+ /*
+  * Open the input data stream specified by the inputfd...
+  */
+
+  if ((inputfp = fdopen(inputfd, "r")) == NULL)
+  {
+    if (!*job_canceled)
+    {
+      if (log) log(ld, FILTER_LOGLEVEL_DEBUG,
+                  "pclmtoraster: Unable to open input data stream.\n");
+    }
+
+    return (1);
+  }
+
+  if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
+  {
+    if (log) log(ld, FILTER_LOGLEVEL_ERROR,
+                "pclmtoraster: Unable to copy PDF file: %s\n", strerror(errno));
+    return (1);
+  }
+
+  if (log) log(ld, FILTER_LOGLEVEL_DEBUG,
+              "pclmtoraster: Copying input to temp file \"%s\"\n",
+              tempfile);
+
+  while ((bytes = fread(buffer, 1, sizeof(buffer), inputfp)) > 0)
+    bytes = write(fd, buffer, bytes);
+
+  fclose(inputfp);
+  close(fd);
+
+  filename = tempfile;
+  pdf->processFile(filename);
+
+  parseOpts(data, &pclmtoraster_data);
+
+  if (pclmtoraster_data.header.cupsBitsPerColor != 1
+     && pclmtoraster_data.header.cupsBitsPerColor != 2
+     && pclmtoraster_data.header.cupsBitsPerColor != 4
+     && pclmtoraster_data.header.cupsBitsPerColor != 8
+     && pclmtoraster_data.header.cupsBitsPerColor != 16)
+  {
+    if(log) log(ld, FILTER_LOGLEVEL_ERROR,
+               "pclmtoraster: Specified color format is not supported: %s\n", strerror(errno));
+    exit(1);
+  }
+
+  if (pclmtoraster_data.header.cupsColorOrder == CUPS_ORDER_PLANAR)
+  {
+    pclmtoraster_data.nplanes = pclmtoraster_data.header.cupsNumColors;
+  }
+  else
+  {
+    pclmtoraster_data.nplanes = 1;
+  }
+  if (pclmtoraster_data.header.cupsColorOrder == CUPS_ORDER_BANDED)
+  {
+    pclmtoraster_data.nbands = pclmtoraster_data.header.cupsNumColors;
+  }
+  else
+  {
+    pclmtoraster_data.nbands = 1;
+  }
+
+  if ((raster = cupsRasterOpen(outputfd, pclmtoraster_data.pwgraster ? CUPS_RASTER_WRITE_PWG :
+                               CUPS_RASTER_WRITE)) == 0)
+  {
+       if(log) log(ld, FILTER_LOGLEVEL_ERROR,
+                       "pclmtoraster: Can't open raster stream: %s\n", strerror(errno));
+        exit(1);
+  }
+
+  std::vector<QPDFObjectHandle> pages = pdf->getAllPages();
+  npages = pages.size();
+
+  for (int i = 0; i < npages; ++i)
+  {
+      if (log) log(ld, FILTER_LOGLEVEL_INFO,
+                  "pclmtoraster: Starting page %d.\n", i+1);
+       outPage(raster, pages[i], i, log, ld, &pclmtoraster_data, &convert);
+  }
+
+  cupsRasterClose(raster);
+  delete pdf;
+  if (pclmtoraster_data.ppd != NULL)  ppdClose(pclmtoraster_data.ppd);
+  return 0;
+}
+
+void operator delete[](void *p) throw ()
+{
+  free(p);
+}
diff --git a/filter/pclmtoraster.c b/filter/pclmtoraster.c
new file mode 100644 (file)
index 0000000..4f0182d
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @brief Decode PCLm to a Raster file
+ * @file pclmtoraster.c
+ * @author Vikrant Malik <vikrantmalik051@gmail.com> (c) 2020
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cupsfilters/filter.h>
+
+/*
+ * Local globals...
+ */
+
+static int     JobCanceled = 0;/* Set to 1 on SIGTERM */
+
+int main(int argc, char **argv)
+{
+  int ret;
+
+ /*
+  * Fire up the pclmtoraster() filter function
+  */
+
+  ret = filterCUPSWrapper(argc, argv, pclmtoraster, NULL, &JobCanceled);
+
+  if (ret)
+    fprintf(stderr, "ERROR: pclmtoraster filter function failed.\n");
+
+  return ret;
+}
diff --git a/filter/pclmtoraster.cxx b/filter/pclmtoraster.cxx
deleted file mode 100644 (file)
index dd88fe9..0000000
+++ /dev/null
@@ -1,804 +0,0 @@
-/**
- * This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @brief Decode PCLm to a Raster file
- * @file pclmtoraster.cxx
- * @author Vikrant Malik <vikrantmalik051@gmail.com> (c) 2020
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <cups/raster.h>
-#include <cups/cups.h>
-#include <ppd/ppd.h>
-
-#include <qpdf/QPDF.hh>
-#include <qpdf/QPDFObjectHandle.hh>
-#include <cupsfilters/raster.h>
-#include <cupsfilters/image.h>
-#include <cupsfilters/bitmap.h>
-
-#if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 6)
-#define HAVE_CUPS_1_7 1
-#endif
-#define MAX_BYTES_PER_PIXEL 32
-
-namespace {
-  typedef unsigned char *(*ConvertCSpace)(unsigned char *src, unsigned char *dst, unsigned int row,
-                                       unsigned int pixels);
-  typedef unsigned char *(*ConvertLine)(unsigned char *src, unsigned char *dst, unsigned char *buf,
-                                       unsigned int row, unsigned int plane, unsigned int pixels);
-  ConvertCSpace convertcspace;
-  ConvertLine convertline;
-  int pwgraster = 0;
-  int numcolors = 0;
-  int rowsize = 0;
-  cups_page_header2_t header;
-  ppd_file_t *ppd = 0;
-  char pageSizeRequested[64];
-  int bi_level = 0;
-  /* image swapping */
-  bool swap_image_x = false;
-  bool swap_image_y = false;
-  /* margin swapping */
-  bool swap_margin_x = false;
-  bool swap_margin_y = false;
-  unsigned int nplanes;
-  unsigned int nbands;
-  unsigned int bytesPerLine; /* number of bytes per line */
-                        /* Note: When CUPS_ORDER_BANDED,
-                           cupsBytesPerLine = bytesPerLine*cupsNumColors */
-}
-
-static void parseOpts(int argc, char **argv)
-{
-  int           num_options = 0;
-  cups_option_t*options = NULL;
-  const char*   t = NULL;
-  ppd_attr_t*   attr;
-  const char   *val;
-
-#ifdef HAVE_CUPS_1_7
-  t = getenv("FINAL_CONTENT_TYPE");
-  if (t && strcasestr(t, "pwg"))
-    pwgraster = 1;
-#endif /* HAVE_CUPS_1_7 */
-
-  ppd = ppdOpenFile(getenv("PPD"));
-  if (ppd == NULL)
-    fprintf(stderr, "DEBUG: PPD file is not specified.\n");
-  if (ppd)
-    ppdMarkDefaults(ppd);
-  //Parse IPP options from command lines
-  num_options = cupsParseOptions(argv[5],0,&options);
-  if (ppd) {
-    ppdMarkDefaults(ppd);
-    ppdMarkOptions(ppd,num_options,options);
-//     handleRqeuiresPageRegion();
-    ppdRasterInterpretPPD(&header,ppd,num_options,options,0);
-    if (header.Duplex) {
-      /* analyze options relevant to Duplex */
-      const char *backside = "";
-      /* APDuplexRequiresFlippedMargin */
-      enum {
-        FM_NO, FM_FALSE, FM_TRUE
-      } flippedMargin = FM_NO;
-
-      attr = ppdFindAttr(ppd,"cupsBackSide",NULL);
-      if (attr != NULL && attr->value != NULL) {
-        ppd->flip_duplex = 0;
-        backside = attr->value;
-      } else if (ppd->flip_duplex) {
-        backside = "Rotated"; /* compatible with Max OS and GS 8.71 */
-      }
-
-      attr = ppdFindAttr(ppd,"APDuplexRequiresFlippedMargin",NULL);
-      if (attr != NULL && attr->value != NULL) {
-        if (strcasecmp(attr->value,"true") == 0) {
-          flippedMargin = FM_TRUE;
-        } else {
-          flippedMargin = FM_FALSE;
-        }
-      }
-      if (strcasecmp(backside,"ManualTumble") == 0 && header.Tumble) {
-        swap_image_x = swap_image_y = true;
-        swap_margin_x = swap_margin_y = true;
-        if (flippedMargin == FM_TRUE) {
-          swap_margin_y = false;
-        }
-      } else if (strcasecmp(backside,"Rotated") == 0 && !header.Tumble) {
-        swap_image_x = swap_image_y = true;
-        swap_margin_x = swap_margin_y = true;
-        if (flippedMargin == FM_TRUE) {
-          swap_margin_y = false;
-        }
-      } else if (strcasecmp(backside,"Flipped") == 0) {
-        if (header.Tumble) {
-          swap_image_x = true;
-          swap_margin_x = swap_margin_y = true;
-        } else {
-          swap_image_y = true;
-        }
-        if (flippedMargin == FM_FALSE) {
-          swap_margin_y = !swap_margin_y;
-        }
-      }
-    }
-
-#ifdef HAVE_CUPS_1_7
-    if ((attr = ppdFindAttr(ppd,"PWGRaster",0)) != 0 &&
-        (!strcasecmp(attr->value, "true")
-         || !strcasecmp(attr->value, "on") ||
-         !strcasecmp(attr->value, "yes")))
-      pwgraster = 1;
-    if (pwgraster == 1)
-      cupsRasterParseIPPOptions(&header, num_options, options, pwgraster, 0);
-#endif /* HAVE_CUPS_1_7 */
-  } else {
-#ifdef HAVE_CUPS_1_7
-    pwgraster = 1;
-    t = cupsGetOption("media-class", num_options, options);
-    if (t == NULL)
-      t = cupsGetOption("MediaClass", num_options, options);
-    if (t != NULL)
-    {
-      if (strcasestr(t, "pwg"))
-        pwgraster = 1;
-      else
-        pwgraster = 0;
-    }
-    cupsRasterParseIPPOptions(&header,num_options,options,pwgraster,1);
-#else
-    fprintf(stderr, "ERROR: No PPD file specified.\n");
-    exit(1);
-#endif /* HAVE_CUPS_1_7 */
-  }
-  if ((val = cupsGetOption("print-color-mode", num_options, options)) != NULL
-                           && !strncasecmp(val, "bi-level", 8))
-    bi_level = 1;
-
-  strncpy(pageSizeRequested, header.cupsPageSizeName, 64);
-  fprintf(stderr, "DEBUG: Page size requested: %s\n", header.cupsPageSizeName);
-}
-
-static bool mediaboxlookup(QPDFObjectHandle object, float rect[4])
-{
-  // preliminary checks
-  if (!object.isDictionary() || !object.hasKey("/MediaBox"))
-    return false;
-
-  // assign mediabox values to rect
-  std::vector<QPDFObjectHandle> mediabox = object.getKey("/MediaBox").getArrayAsVector();
-  for (int i = 0; i < 4; ++i) {
-    rect[i] = mediabox[i].getNumericValue();
-  }
-
-  return mediabox.size() == 4;
-}
-
-static unsigned char *rotatebitmap(unsigned char *src, unsigned char *dst,
-     unsigned int rotate, unsigned int height, unsigned int width, int rowsize, std::string colorspace)
-{
-  unsigned char *bp = src;
-  unsigned char *dp = dst;
-  unsigned char *temp = dst;
-
-  if (rotate == 0) {
-    return src;
-  } else if (rotate == 180) {
-    if (colorspace == "/DeviceGray") {
-      bp = src + height * rowsize - 1;
-      dp = dst;
-      for (unsigned int h = 0; h < height; h++) {
-        for (unsigned int w = 0; w < width; w++, bp --, dp ++) {
-          *dp = *bp;
-        }
-      }
-    } else if (colorspace == "/DeviceCMYK") {
-      bp = src + height * rowsize - 4;
-      dp = dst;
-      for (unsigned int h = 0; h < height; h++) {
-        for (unsigned int w = 0; w < width; w++, bp -= 4, dp += 4) {
-          dp[0] = bp[0];
-          dp[1] = bp[1];
-          dp[2] = bp[2];
-          dp[3] = bp[3];
-        }
-      }
-    } else if (colorspace == "/DeviceRGB") {
-      bp = src + height * rowsize - 3;
-      dp = dst;
-      for (unsigned int h = 0; h < height; h++) {
-        for (unsigned int w = 0; w < width; w++, bp -= 3, dp += 3) {
-          dp[0] = bp[0];
-          dp[1] = bp[1];
-          dp[2] = bp[2];
-        }
-      }
-    }
-  }
-  else if (rotate == 270) {
-    if (colorspace == "/DeviceGray") {
-      bp = src;
-      dp = dst;
-      for (unsigned int h = 0; h < height; h++) {
-        bp = src + (height - h) - 1;
-        for (unsigned int w = 0; w < width; w++, bp += height , dp ++) {
-          *dp = *bp;
-        }
-      }
-    } else if (colorspace == "/DeviceCMYK") {
-      for (unsigned int h = 0; h < height; h++) {
-        bp = src + (height - h)*4 - 4;
-        for (unsigned int i = 0; i < width; i++, bp += height*4 , dp += 4) {
-          dp[0] = bp[0];
-          dp[1] = bp[1];
-          dp[2] = bp[2];
-          dp[3] = bp[3];
-        }
-      }
-    } else if (colorspace == "/DeviceRGB") {
-      bp = src;
-      dp = dst;
-      for (unsigned int h = 0; h < height; h++) {
-        bp = src + (height - h)*3 - 3;
-        for (unsigned int i = 0; i < width; i++, bp += height*3 , dp += 3) {
-          dp[0] = bp[0];
-          dp[1] = bp[1];
-          dp[2] = bp[2];
-        }
-      }
-    }
-  }
-  else if (rotate == 90) {
-    if (colorspace == "/DeviceGray") {
-      for (unsigned int h = 0; h < height; h++) {
-        bp = src + (width - 1) * height + h;
-        for (unsigned int i = 0; i < width; i++, bp -= height , dp ++) {
-          *dp = *bp;
-        }
-      }
-    } else if (colorspace == "/DeviceCMYK") {
-      for (unsigned int h = 0; h < height; h++) {
-        bp = src + (width - 1) * height * 4 + 4*h;
-        for (unsigned int i = 0; i < width; i++, bp -= height*4 , dp += 4) {
-          dp[0] = bp[0];
-          dp[1] = bp[1];
-          dp[2] = bp[2];
-          dp[3] = bp[3];
-        }
-      }
-    } else if (colorspace == "/DeviceRGB") {
-      for (unsigned int h = 0; h < height; h++) {
-       bp = src + (width - 1) * height * 3 + 3*h;
-        for (unsigned int i = 0; i < width; i++, bp -= height*3 , dp += 3) {
-          dp[0] = bp[0];
-          dp[1] = bp[1];
-          dp[2] = bp[2];
-        }
-      }
-    }
-  }
-  else {
-    fprintf(stderr, "ERROR: Incorrect Rotate Value %d\n", rotate);
-    exit(1);
-  }
-
-  return temp;
-}
-
-static unsigned char *RGBtoCMYKLine(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  cupsImageRGBToCMYK(src,dst,pixels);
-  return dst;
-}
-
-static unsigned char *RGBtoCMYLine(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  cupsImageRGBToCMY(src,dst,pixels);
-  return dst;
-}
-
-static unsigned char *RGBtoWhiteLine(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  if (header.cupsBitsPerColor != 1) {
-    cupsImageRGBToWhite(src,dst,pixels);
-  } else {
-    cupsImageRGBToWhite(src,src,pixels);
-    oneBitLine(src, dst, header.cupsWidth, row, bi_level);
-  }
-
-  return dst;
-}
-
-static unsigned char *RGBtoBlackLine(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  if (header.cupsBitsPerColor != 1) {
-    cupsImageRGBToBlack(src,dst,pixels);
-  } else {
-    cupsImageRGBToBlack(src,src,pixels);
-    oneBitLine(src, dst, header.cupsWidth, row, bi_level);
-  }
-  return dst;
-}
-
-static unsigned char *CMYKtoRGBLine(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  cupsImageCMYKToRGB(src,dst,pixels);
-  return dst;
-}
-
-static unsigned char *CMYKtoCMYLine(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  // Converted first to rgb and then to cmy for better outputs.
-  cupsImageCMYKToRGB(src,src,pixels);
-  cupsImageRGBToCMY(src,dst,pixels);
-  return dst;
-}
-
-static unsigned char *CMYKtoWhiteLine(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  if (header.cupsBitsPerColor != 1) {
-    cupsImageCMYKToWhite(src,dst,pixels);
-  } else {
-    cupsImageCMYKToWhite(src,src,pixels);
-    oneBitLine(src, dst, header.cupsWidth, row, bi_level);
-  }
-  return dst;
-}
-
-static unsigned char *CMYKtoBlackLine(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  if (header.cupsBitsPerColor != 1) {
-    cupsImageCMYKToBlack(src,dst,pixels);
-  } else {
-    cupsImageCMYKToBlack(src,src,pixels);
-    oneBitLine(src, dst, header.cupsWidth, row, bi_level);
-  }
-  return dst;
-}
-
-static unsigned char *GraytoRGBLine(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  cupsImageWhiteToRGB(src,dst,pixels);
-  return dst;
-}
-
-static unsigned char *GraytoCMYKLine(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  cupsImageWhiteToCMYK(src,dst,pixels);
-  return dst;
-}
-
-static unsigned char *GraytoCMYLine(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  cupsImageWhiteToCMY(src,dst,pixels);
-  return dst;
-}
-
-static unsigned char *GraytoBlackLine(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  if (header.cupsBitsPerColor != 1) {
-    cupsImageWhiteToBlack(src, dst, pixels);
-  } else {
-    cupsImageWhiteToBlack(src, src, pixels);
-    oneBitLine(src, dst, header.cupsWidth, row, bi_level);
-  }
-  return dst;
-}
-
-static unsigned char *convertcspaceNoop(unsigned char *src, unsigned char *dst, unsigned int row, unsigned int pixels)
-{
-  return src;
-}
-
-static unsigned char *convertLine(unsigned char *src, unsigned char *dst,
-     unsigned char *buf, unsigned int row, unsigned int plane, unsigned int pixels)
-{
-  /*
-   Use only convertcspace if conversion of bits and conversion of color order
-   is not required, or if dithering is required, for faster processing of raster output.
-   */
-  if ((header.cupsBitsPerColor == 1
-       && header.cupsNumColors == 1)
-       || (header.cupsBitsPerColor == 8
-       && header.cupsColorOrder == CUPS_ORDER_CHUNKED)) {
-    dst = convertcspace(src, dst, row, pixels);
-  } else {
-    for (unsigned int i = 0;i < pixels;i++) {
-      unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL];
-      unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL];
-      unsigned char *pb;
-      pb = convertcspace(src + i*numcolors, pixelBuf1, row, 1);
-      pb = convertbits(pb, pixelBuf2, i, row, header.cupsNumColors, header.cupsBitsPerColor);
-      writepixel(dst, plane, i, pb, header.cupsNumColors, header.cupsBitsPerColor, header.cupsColorOrder);
-    }
-  }
-  return dst;
-}
-
-static unsigned char *convertReverseLine(unsigned char *src, unsigned char *dst,
-     unsigned char *buf, unsigned int row, unsigned int plane, unsigned int pixels)
-{
-  // Use only convertcspace if conversion of bits and conversion of color order
-  // is not required, or if dithering is required, for faster processing of raster output.
-  if (header.cupsBitsPerColor == 1 && header.cupsNumColors == 1) {
-    buf = convertcspace(src, buf, row, pixels);
-    dst = reverseOneBitLine(buf, dst, pixels, bytesPerLine);
-  } else if (header.cupsBitsPerColor == 8 && header.cupsColorOrder == CUPS_ORDER_CHUNKED) {
-    unsigned char *dp = dst;
-    // Assign each pixel of buf to dst in the reverse order.
-    buf = convertcspace(src, buf, row, pixels) + (header.cupsWidth - 1)*header.cupsNumColors;
-    for (unsigned int i = 0; i < pixels; i++, buf-=header.cupsNumColors, dp+=header.cupsNumColors) {
-      for (unsigned int j = 0; j < header.cupsNumColors; j++) {
-       dp[j] = buf[j];
-      }
-    }
-  } else {
-    for (unsigned int i = 0;i < pixels;i++) {
-      unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL];
-      unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL];
-      unsigned char *pb;
-      pb = convertcspace(src + (pixels - i - 1)*numcolors, pixelBuf1, row, 1);
-      pb = convertbits(pb, pixelBuf2, i, row, header.cupsNumColors, header.cupsBitsPerColor);
-      writepixel(dst, plane, i, pb, header.cupsNumColors, header.cupsBitsPerColor, header.cupsColorOrder);
-    }
-  }
-  return dst;
-}
-
-static void selectConvertFunc (std::string colorspace, int pgno) {
-
-  /* Set rowsize and numcolors based on colorspace of raster data */
-  if (colorspace == "/DeviceRGB") {
-    rowsize = header.cupsWidth*3;
-    numcolors = 3;
-  } else if (colorspace == "/DeviceCMYK") {
-    rowsize = header.cupsWidth*4;
-    numcolors = 4;
-  } else if (colorspace == "/DeviceGray") {
-    rowsize = header.cupsWidth;
-    numcolors = 1;
-  } else {
-    fprintf(stderr, "ERROR: Colorspace %s not supported\n", colorspace.c_str());
-    exit(1);
-  }
-
-  convertcspace = convertcspaceNoop; //Default function
-  /* Select convertcspace function */
-  switch (header.cupsColorSpace) {
-    case CUPS_CSPACE_K:
-     if (colorspace == "/DeviceRGB") convertcspace = RGBtoBlackLine;
-     else if (colorspace == "/DeviceCMYK") convertcspace = CMYKtoBlackLine;
-     else if (colorspace == "/DeviceGray") convertcspace = GraytoBlackLine;
-     break;
-    case CUPS_CSPACE_W:
-    case CUPS_CSPACE_SW:
-     if (colorspace == "/DeviceRGB") convertcspace = RGBtoWhiteLine;
-     else if (colorspace == "/DeviceCMYK") convertcspace = CMYKtoWhiteLine;
-     break;
-    case CUPS_CSPACE_CMY:
-     if (colorspace == "/DeviceRGB") convertcspace = RGBtoCMYLine;
-     else if (colorspace == "/DeviceCMYK") convertcspace = CMYKtoCMYLine;
-     else if (colorspace == "/DeviceGray") convertcspace = GraytoCMYLine;
-     break;
-    case CUPS_CSPACE_CMYK:
-     if (colorspace == "/DeviceRGB") convertcspace = RGBtoCMYKLine;
-     else if (colorspace == "/DeviceGray") convertcspace = GraytoCMYKLine;
-     break;
-    case CUPS_CSPACE_RGB:
-    case CUPS_CSPACE_ADOBERGB:
-    case CUPS_CSPACE_SRGB:
-    default:
-     if (colorspace == "/DeviceCMYK") convertcspace = CMYKtoRGBLine;
-     else if (colorspace == "/DeviceGray") convertcspace = GraytoRGBLine;
-     break;
-   }
-
-  /* Select convertline function */
-  if (header.Duplex && (pgno & 1) && swap_image_x) {
-    convertline = convertReverseLine;
-  } else {
-    convertline = convertLine;
-  }
-
-}
-
-static void outPage(cups_raster_t *raster, QPDFObjectHandle page, int pgno) {
-  long long            rotate = 0,
-                       height,
-                       width;
-  double               paperdimensions[2], margins[4], l, swap;
-  int                  bufsize = 0, pixel_count = 0,
-                       temp = 0;
-  float                mediaBox[4];
-  unsigned char        *bitmap = NULL,
-                       *colordata = NULL,
-                       *lineBuf = NULL,
-                       *line = NULL,
-                       *dp = NULL;
-  std::string          colorspace;
-  QPDFObjectHandle     image;
-  QPDFObjectHandle     imgdict;
-  QPDFObjectHandle     colorspace_obj;
-
-  // Check if page is rotated.
-  if (page.getKey("/Rotate").isInteger())
-    rotate = page.getKey("/Rotate").getIntValueAsInt();
-
-  // Get pagesize by the mediabox key of the page.
-  if (!mediaboxlookup(page, mediaBox)){
-    fprintf(stderr, "ERROR: pdf page %d doesn't contain a valid mediaBox\n", pgno + 1);
-    return;
-  } else {
-    fprintf(stderr, "DEBUG: mediaBox = [%f %f %f %f];\n", mediaBox[0], mediaBox[1], mediaBox[2], mediaBox[3]);
-    l = mediaBox[2] - mediaBox[0];
-    if (l < 0) l = -l;
-    if (rotate == 90 || rotate == 270)
-      header.PageSize[1] = (unsigned)l;
-    else
-      header.PageSize[0] = (unsigned)l;
-    l = mediaBox[3] - mediaBox[1];
-    if (l < 0) l = -l;
-    if (rotate == 90 || rotate == 270)
-      header.PageSize[0] = (unsigned)l;
-    else
-      header.PageSize[1] = (unsigned)l;
-  }
-
-  // Adjust header page size and margins according to the ppd file.
-  if (ppd) {
-    ppdRasterMatchPPDSize(&header, ppd, margins, paperdimensions, NULL, NULL);
-    if (pwgraster == 1)
-      memset(margins, 0, sizeof(margins));
-  } else {
-    for (int i = 0; i < 2; i ++)
-      paperdimensions[i] = header.PageSize[i];
-    if (header.cupsImagingBBox[3] > 0.0) {
-      /* Set margins if we have a bounding box defined ... */
-      if (pwgraster == 0) {
-       margins[0] = header.cupsImagingBBox[0];
-       margins[1] = header.cupsImagingBBox[1];
-       margins[2] = paperdimensions[0] - header.cupsImagingBBox[2];
-       margins[3] = paperdimensions[1] - header.cupsImagingBBox[3];
-      }
-    } else
-      /* ... otherwise use zero margins */
-      for (int i = 0; i < 4; i ++)
-       margins[i] = 0.0;
-  }
-
-  if (header.Duplex && (pgno & 1)) {
-    /* backside: change margin if needed */
-    if (swap_margin_x) {
-      swap = margins[2]; margins[2] = margins[0]; margins[0] = swap;
-    }
-    if (swap_margin_y) {
-      swap = margins[3]; margins[3] = margins[1]; margins[1] = swap;
-    }
-  }
-
-  /* write page header */
-  for (int i = 0; i < 2; i ++) {
-    header.cupsPageSize[i] = paperdimensions[i];
-    header.PageSize[i] = (unsigned int)(header.cupsPageSize[i] + 0.5);
-    if (pwgraster == 0)
-      header.Margins[i] = margins[i] + 0.5;
-    else
-      header.Margins[i] = 0;
-  }
-  if (pwgraster == 0) {
-    header.cupsImagingBBox[0] = margins[0];
-    header.cupsImagingBBox[1] = margins[1];
-    header.cupsImagingBBox[2] = paperdimensions[0] - margins[2];
-    header.cupsImagingBBox[3] = paperdimensions[1] - margins[3];
-    for (int i = 0; i < 4; i ++)
-      header.ImagingBoundingBox[i] = (unsigned int)(header.cupsImagingBBox[i] + 0.5);
-  } else
-    for (int i = 0; i < 4; i ++) {
-      header.cupsImagingBBox[i] = 0.0;
-      header.ImagingBoundingBox[i] = 0;
-    }
-
-  header.cupsWidth = 0;
-  header.cupsHeight = 0;
-
-  /* Loop over all raster images in a page and store them in bitmap. */
-  std::map<std::string, QPDFObjectHandle> images = page.getPageImages();
-  for (auto const& iter: images) {
-    image = iter.second;
-    imgdict = image.getDict(); //XObject dictionary
-
-    PointerHolder<Buffer> actual_data = image.getStreamData(qpdf_dl_all);
-    width = imgdict.getKey("/Width").getIntValue();
-    height = imgdict.getKey("/Height").getIntValue();
-    colorspace_obj = imgdict.getKey("/ColorSpace");
-    header.cupsHeight += height;
-    bufsize = actual_data->getSize();
-
-    if(!pixel_count) {
-      bitmap = (unsigned char *) malloc(bufsize);
-    }
-    else {
-      bitmap = (unsigned char *) realloc(bitmap, pixel_count + bufsize);
-    }
-    memcpy(bitmap + pixel_count, actual_data->getBuffer(), bufsize);
-    pixel_count += bufsize;
-
-    if (width > header.cupsWidth) header.cupsWidth = width;
-  }
-
-  // Swap width and height in landscape images
-  if (rotate == 270 || rotate == 90) {
-    temp = header.cupsHeight;
-    header.cupsHeight = header.cupsWidth;
-    header.cupsWidth = temp;
-  }
-
-  bytesPerLine = header.cupsBytesPerLine = (header.cupsBitsPerPixel * header.cupsWidth + 7) / 8;
-  if (header.cupsColorOrder == CUPS_ORDER_BANDED) {
-    header.cupsBytesPerLine *= header.cupsNumColors;
-  }
-
-  if (!cupsRasterWriteHeader2(raster,&header)) {
-    fprintf(stderr, "ERROR: Can't write page %d header\n", pgno + 1);
-    exit(1);
-  }
-
-  colorspace = (colorspace_obj.isName() ? colorspace_obj.getName() : "/DeviceRGB"); // Default for pclm files in DeviceRGB
-
-  // If page is to be swapped in both x and y, rotate it by 180 degress
-  if (header.Duplex && (pgno & 1) && swap_image_y && swap_image_x) {
-    rotate = (rotate + 180) % 360;
-    swap_image_y = false;
-    swap_image_x = false;
-  }
-
-  /* Rotate Bitmap */
-  if (rotate) {
-    unsigned char *bitmap2 = (unsigned char *) malloc(pixel_count);
-    bitmap2 = rotatebitmap(bitmap, bitmap2, rotate, header.cupsHeight, header.cupsWidth, rowsize, colorspace);
-    free(bitmap);
-    bitmap = bitmap2;
-  }
-
-  colordata = bitmap;
-
-  /* Select convertline and convertscpace function */
-  selectConvertFunc(colorspace, pgno);
-
-  /* Write page image */
-  lineBuf = new unsigned char [bytesPerLine];
-  line = new unsigned char [bytesPerLine];
-  if (header.Duplex && (pgno & 1) && swap_image_y) {
-    for (unsigned int plane = 0; plane < nplanes ; plane++) {
-      unsigned char *bp = colordata + (header.cupsHeight - 1) * rowsize;
-      for (unsigned int h = header.cupsHeight; h > 0; h--) {
-        for (unsigned int band = 0; band < nbands; band++) {
-         dp = convertline(bp, line, lineBuf, h - 1, plane + band, header.cupsWidth);
-         cupsRasterWritePixels(raster, dp, bytesPerLine);
-        }
-       bp -= rowsize;
-      }
-    }
-  } else {
-    for (unsigned int plane = 0; plane < nplanes ; plane++) {
-      unsigned char *bp = colordata;
-      for (unsigned int h = 0; h < header.cupsHeight; h++) {
-        for (unsigned int band = 0; band < nbands; band++) {
-         dp = convertline(bp, line, lineBuf, h, plane + band, header.cupsWidth);
-         cupsRasterWritePixels(raster, dp, bytesPerLine);
-        }
-       bp += rowsize;
-      }
-    }
-  }
-  delete[] lineBuf;
-  delete[] line;
-  free(bitmap);
-}
-
-int main(int argc, char **argv)
-{
-  int          npages=0;
-  QPDF         *pdf = new QPDF();
-  cups_raster_t *raster;
-
-
-  if (argc == 6) {
-    /* stdin input, copy to temporary file */
-    int fd;
-    char name[BUFSIZ];
-    char buf[BUFSIZ];
-    int n;
-
-    fd = cupsTempFd(name,sizeof(name));
-    if (fd < 0) {
-      fprintf(stderr, "ERROR: Can't create temporary file\n");
-      exit(1);
-    }
-
-    /* copy stdin to the tmp file */
-    while ((n = read(0,buf,BUFSIZ)) > 0) {
-      if (write(fd,buf,n) != n) {
-        fprintf(stderr, "ERROR: Can't copy stdin to temporary file\n" );
-        close(fd);
-       exit(1);
-      }
-    }
-    close(fd);
-    pdf->processFile(name);
-    /* remove name */
-    unlink(name);
-  } else if (argc == 7) {
-    FILE *fp;
-    if ((fp = fopen(argv[6],"rb")) == 0) {
-        fprintf(stderr, "ERROR: Can't open input file %s\n",argv[6]);
-        exit(1);
-    }
-    pdf->processFile(argv[6]);
-    fclose(fp);
-  } else {
-    fprintf(stderr, "ERROR: Usage: %s job-id user title copies options [file]\n",
-            argv[0]);
-    exit(1);
-  }
-
-  parseOpts(argc, argv);
-
-  if (header.cupsBitsPerColor != 1
-     && header.cupsBitsPerColor != 2
-     && header.cupsBitsPerColor != 4
-     && header.cupsBitsPerColor != 8
-     && header.cupsBitsPerColor != 16) {
-    fprintf(stderr, "ERROR: Specified color format is not supported\n");
-    exit(1);
-  }
-
-  if (header.cupsColorOrder == CUPS_ORDER_PLANAR) {
-    nplanes = header.cupsNumColors;
-  } else {
-    nplanes = 1;
-  }
-  if (header.cupsColorOrder == CUPS_ORDER_BANDED) {
-    nbands = header.cupsNumColors;
-  } else {
-    nbands = 1;
-  }
-
-  if ((raster = cupsRasterOpen(1, pwgraster ? CUPS_RASTER_WRITE_PWG :
-                               CUPS_RASTER_WRITE)) == 0) {
-        fprintf(stderr, "ERROR: Can't open raster stream\n");
-        exit(1);
-  }
-
-  std::vector<QPDFObjectHandle> pages = pdf->getAllPages();
-  npages = pages.size();
-
-  for (int i = 0;
-       i < npages; ++i) {
-       fprintf(stderr, "INFO: Starting page %d.\n", i + 1);
-       outPage(raster, pages[i], i);
-    }
-
-  cupsRasterClose(raster);
-  delete pdf;
-  if (ppd != NULL)  ppdClose(ppd);
-  return 0;
-}
-
-void operator delete[](void *p) throw ()
-{
-  free(p);
-}