about summary refs log tree commit diff
path: root/converter/other/pnmtopclxl.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/other/pnmtopclxl.c')
-rw-r--r--converter/other/pnmtopclxl.c1204
1 files changed, 1204 insertions, 0 deletions
diff --git a/converter/other/pnmtopclxl.c b/converter/other/pnmtopclxl.c
new file mode 100644
index 00000000..79ee092c
--- /dev/null
+++ b/converter/other/pnmtopclxl.c
@@ -0,0 +1,1204 @@
+/*
+ * -------------------------------------------------------------
+ *
+ *  (C) 2002 Jochen Karrer, Linuxdata GbR
+ *
+ *      convert a pnm to PCL-XL image 
+ *
+ * -------------------------------------------------------------
+ */
+
+/* Engineering note: One PCL-XL printer prints an error message like
+   this when it doesn't like the PCL it sees:
+
+   PCL XL error
+      Subsystem:  IMAGE
+      Error:      IllegalAttributeValue
+      Operator:   ReadImage
+      Position:   8
+
+   "Position" is the sequence number of the PCL operator it was trying
+   to execute.
+*/
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "pam.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+#include "pclxl.h"
+
+#define PAPERWIDTH(format) (xlPaperFormats[format].width)
+#define PAPERHEIGHT(format) (xlPaperFormats[format].height)
+
+
+
+typedef struct InputSource {
+    const char *         name; 
+    struct InputSource * next;
+} InputSource;
+
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    InputSource * sourceP;
+    int dpi;
+    enum MediaSize format;
+    unsigned int feederSpec;
+    int feeder;
+    unsigned int outtraySpec;
+    int outtray;
+    unsigned int duplexSpec;
+    enum DuplexPageMode duplex;
+    unsigned int copiesSpec;
+    int copies;
+    unsigned int center;
+    float xoffs;
+    float yoffs;
+    unsigned int colorok;
+    unsigned int verbose;
+    const char * jobsetup;  /* -jobsetup option value.  NULL if none */
+    unsigned int rendergray;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def = malloc( 100*sizeof( optEntry ) );
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    char *formatOpt;
+    char *duplexOpt;
+    unsigned int dpiSpec, xoffsSpec, yoffsSpec, formatSpec, jobsetupSpec;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "dpi",       OPT_UINT,    &cmdlineP->dpi,
+            &dpiSpec,         0);
+    OPTENT3(0, "xoffs",     OPT_FLOAT,   &cmdlineP->xoffs, 
+            &xoffsSpec,        0);
+    OPTENT3(0, "yoffs",     OPT_FLOAT,   &cmdlineP->yoffs, 
+            &yoffsSpec,        0);
+    OPTENT3(0, "format",    OPT_STRING,  &formatOpt, 
+            &formatSpec,        0);
+    OPTENT3(0, "duplex",    OPT_STRING,  &duplexOpt, 
+            &cmdlineP->duplexSpec,        0);
+    OPTENT3(0, "copies",    OPT_UINT,    &cmdlineP->copies,
+            &cmdlineP->copiesSpec,        0);
+    OPTENT3(0, "colorok",   OPT_FLAG,    NULL,                  
+            &cmdlineP->colorok, 0);
+    OPTENT3(0, "center",    OPT_FLAG,    NULL,                  
+            &cmdlineP->center, 0 );
+    OPTENT3(0, "feeder",    OPT_STRING,  &cmdlineP->feeder,
+            &cmdlineP->feederSpec,        0);
+    OPTENT3(0, "outtray",   OPT_STRING,  &cmdlineP->outtray,
+            &cmdlineP->outtraySpec,       0);
+    OPTENT3(0, "verbose",   OPT_FLAG,    NULL,                  
+            &cmdlineP->verbose, 0);
+    OPTENT3(0, "jobsetup",  OPT_STRING,  &cmdlineP->jobsetup,
+            &jobsetupSpec,      0);
+    OPTENT3(0, "rendergray", OPT_FLAG,    NULL,
+            &cmdlineP->rendergray, 0 );
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!dpiSpec)
+        cmdlineP->dpi = 300;
+    if (!xoffsSpec)
+        cmdlineP->xoffs = 0.0;
+    if (!yoffsSpec)
+        cmdlineP->yoffs = 0.0;
+
+    if (cmdlineP->duplexSpec) {
+        if (strncmp(duplexOpt, "vertical", strlen(duplexOpt)) == 0)
+            cmdlineP->duplex = eDuplexVerticalBinding;
+        else if (strncmp(duplexOpt, "horizontal", strlen(duplexOpt)) == 0)
+            cmdlineP->duplex = eDuplexHorizontalBinding;
+        else
+            pm_error("Invalid value '%s' for -duplex option", duplexOpt);
+    }
+
+    if (formatSpec) {
+        bool found;
+        int i;
+        for (i = 0, found=FALSE; xlPaperFormats[i].name && !found; ++i) {
+            if (STREQ(xlPaperFormats[i].name, formatOpt)) {
+                found = TRUE;
+                cmdlineP->format = xlPaperFormats[i].xl_nr;
+            }
+        }
+        if (!found) {
+            int i;
+            pm_message("Valid -format values:");
+            for (i = 0; xlPaperFormats[i].name; ++i) {
+                if (xlPaperFormats[i].width > 0)
+                    pm_message("   %s", xlPaperFormats[i].name);
+            }
+            pm_error("Invalid -format option '%s' specified.", formatOpt);
+        }
+    } else
+        cmdlineP->format = eLetterPaper;
+
+    if (!jobsetupSpec)
+        cmdlineP->jobsetup = NULL;
+
+    if (argc-1 < 1) {
+        MALLOCVAR(cmdlineP->sourceP);
+        cmdlineP->sourceP->name = "-";
+    } else {
+        int i;
+        InputSource ** nextLinkP;
+
+        nextLinkP = &cmdlineP->sourceP;
+        for (i = 1; i < argc; ++i) {
+            InputSource * sourceP;
+            MALLOCVAR(sourceP);
+            sourceP->name = argv[i];
+            *nextLinkP = sourceP;
+            nextLinkP = &sourceP->next;
+            *nextLinkP = NULL;
+        }
+    }
+}
+
+
+
+#define XY_RLE_FBUFSIZE (1024)
+typedef struct XY_rle {
+    int error;
+    unsigned char buf[128];
+    int bpos;
+    int state;  
+    unsigned char *fbuf;
+    int fbpos;
+    int fbufsize;
+    int fd;
+} XY_rle;
+
+
+static void 
+XY_RLEreset(XY_rle *rle) 
+{   
+    rle->state = eSTART;
+    rle->bpos = 0;
+    rle->fbpos=0;
+    rle->error=0;
+}
+static XY_rle * 
+XY_RLEnew(int size) {
+    XY_rle *rle;
+
+    MALLOCVAR(rle);
+    if(rle==NULL)
+        return rle;
+    if(size<1024)
+        size=1024;
+    rle->fbuf=malloc(size);
+    rle->fbufsize=size;
+    if(!rle->fbuf) {
+        free(rle);
+        return NULL;
+    }
+    return rle;
+}
+static void
+XY_RLEdelete(XY_rle *rle) {
+    free(rle->fbuf);
+    free(rle);
+}
+
+static int 
+out(XY_rle *rle,int count) {
+    if(rle->state==eRLE) {
+        rle->fbuf[rle->fbpos++]=-count+1;
+        rle->fbuf[rle->fbpos++]=rle->buf[0];
+    } else if(rle->bpos>0) {
+        rle->fbuf[rle->fbpos++]=count-1;
+        memcpy(rle->fbuf+rle->fbpos,rle->buf,count);
+        rle->fbpos+=count;
+    }
+    if(rle->fbpos+129>rle->fbufsize) {
+        rle->fbufsize*=1.2; 
+        rle->fbuf=realloc(rle->fbuf,rle->fbufsize);
+        if(rle->fbuf==NULL) {
+            rle->error=-1;
+            rle->fbpos=0;
+            return -1;
+        }
+    }
+    rle->bpos=0;
+    rle->state=eSTART;
+    return 0;
+}
+static int
+XY_RLEfinish (XY_rle *rle) {
+    out(rle,rle->bpos);
+    if(rle->error<0) 
+        return rle->error;
+    else
+        return rle->fbpos;
+}
+static  void
+rle_putbyte(XY_rle *rle,unsigned char u) 
+{
+    switch (rle->state) {
+        case eRLE:
+            if(u!=rle->buf[0]) {
+                out(rle,rle->bpos);
+            }   
+            break;
+        case eLIT:
+            if((u==rle->buf[rle->bpos-1])&&(u==rle->buf[rle->bpos-2])) {
+                out(rle,rle->bpos-2);
+                rle->buf[0]=u;
+                rle->bpos+=2;
+                rle->state=eRLE;
+            }   
+            break;
+        case eSTART:
+            if(rle->bpos==1) {
+                if(u==rle->buf[rle->bpos-1]) {
+                    rle->state=eRLE;
+                } else {
+                    rle->state=eLIT;
+                }   
+            }
+            break;
+    
+    }
+    rle->buf[rle->bpos++]=u;
+    if(rle->bpos==128) {
+        out(rle,rle->bpos);
+    }
+}
+
+
+
+static void
+XY_RLEput(XY_rle *rle,const unsigned char buf[],int count) 
+{
+    int i;
+    for(i=0;i<count;i++) {
+        rle_putbyte(rle,buf[i]);
+    }
+    
+}
+
+
+static int
+XY_Write(int fd, const void *buf,int cnt) {
+        int len=0;
+        while(len<cnt) {
+                int n = write(fd,(char*)buf+len,cnt-len);
+                if(n<=0)
+                        return n;
+                len+=n;
+        }
+        return len;
+}
+#define XY_Puts(fd,str)  XY_Write(fd,str,strlen(str))
+
+typedef struct pclGenerator {
+    enum ColorDepth colorDepth;
+    enum Colorspace colorSpace;
+    int width,height;
+    int linelen; /* bytes per line */
+    unsigned char *data;
+    void (*getnextrow)(const struct pclGenerator *, struct pam *);
+} pclGenerator;
+
+struct tPrinter { 
+    const char *name;
+    float topmargin;
+    float bottommargin;
+    float leftmargin;
+    float rightmargin;
+} xlPrinters[] = {
+    { "lj2200",0,0,0,0 }
+};
+
+
+
+static int
+out_ubyte(int fd,unsigned char data) {
+    return XY_Write(fd,&data,1);
+}
+static  int 
+XL_Operator(int fd,enum Operator const data)  {
+    return out_ubyte(fd,data);
+}
+static int
+out_uint16(int fd,unsigned short data ) {
+    unsigned char c[2];
+    c[0]=data&0xff; c[1]=data>>8;
+    return XY_Write(fd,c,2);
+}
+static int
+out_uint32(int fd,unsigned int data ) {
+    unsigned char c[4];
+    c[0] = data&0xff; c[1]=(data>>8)&0xff; c[2]=(data>>16)&0xff; c[3]=data>>24;
+    return XY_Write(fd,c,4);
+}
+static int
+out_sint16(int fd,signed short sdata ) {
+    unsigned short data=(unsigned short)sdata;    
+    unsigned char c[2];
+    c[0]=data&0xff; c[1]=data>>8;
+    return XY_Write(fd,c,2);
+}
+#if 0
+static int
+out_sint32(int fd,signed int sdata ) {
+    unsigned int data=(unsigned int)sdata;
+    unsigned char c[4];
+    c[0] = data&0xff; c[1]=(data>>8)&0xff; c[2]=(data>>16)&0xff; c[3]=data>>24;
+    return XY_Write(fd,c,4);
+}
+#endif
+
+static int
+xl_ubyte(int fd,unsigned char data) {
+    unsigned char const tag=0xc0;
+    XY_Write(fd,&tag,1);
+    return out_ubyte(fd,data);
+}
+static int
+xl_uint16(int fd,unsigned short data ) {
+    unsigned char const tag=0xc1;
+    XY_Write(fd,&tag,1);
+    return out_uint16(fd,data);
+}
+#if 0
+static int
+xl_uint32(int fd,unsigned int data ) {
+    unsigned char const c=0xc2;
+    XY_Write(fd,&c,1);
+    return out_uint32(fd,data);
+}
+static int
+xl_sint16(int fd,signed short data ) {
+    unsigned char const c=0xc3;
+    XY_Write(fd,&c,1);
+    return out_sint16(fd,data);
+}
+static int
+xl_sint32(int fd,signed int data ) {
+    unsigned char const c=0xc4;
+    XY_Write(fd,&c,1);
+    return out_sint32(fd,data);
+}
+#endif
+
+
+
+static int
+xl_ubyte_array(int                   const fd,
+               const unsigned char * const data,
+               int                   const len) {
+
+    unsigned int i;
+    unsigned char head[4];
+    
+    head[0] = 0xc8;
+    head[1] = 0xc1;
+    head[2] = len&0xff;
+    head[3] = (len>>8)&0xff;
+
+    XY_Write(fd, head, 4);
+
+    for (i = 0; i < len; ++i)
+        out_ubyte(fd, data[i]);
+
+    return 0;
+}
+
+
+
+#if 0
+static int
+xl_uint16_array(int fd,unsigned short *data,int len) {
+    int i;
+    unsigned char head[4];
+    head[0]=0xc9;head[1]=0xc1;head[2]=len&0xff;head[3]=(len>>8)&0xff;
+    XY_Write(fd,head,4);
+    for(i=0;i<len;i++) {
+        out_uint16(fd,data[i]);
+    }
+    return 0;
+}
+static int
+xl_uint32_array(int fd,unsigned int *data,int len) {
+    int i;
+    unsigned char head[4];
+    head[0]=0xca;head[1]=0xc1;head[2]=len&0xff;head[3]=(len>>8)&0xff;
+    XY_Write(fd,head,4);
+    for(i=0;i<len;i++) {
+        out_uint32(fd,data[i]);
+    }
+    return 0;
+}
+static int
+xl_sint16_array(int fd,signed short *data,int len) {
+    int i;
+    unsigned char head[4];
+    head[0]=0xcb;head[1]=0xc1;head[2]=len&0xff;head[3]=(len>>8)&0xff;
+    XY_Write(fd,head,4);
+    for(i=0;i<len;i++) {
+        out_sint16(fd,data[i]);
+    }
+    return 0;
+}
+static int
+xl_sint32_array(int fd,signed int *data,int len) {
+    int i;
+    unsigned char head[4];
+    head[0]=0xcc;head[1]=0xc1;head[2]=len&0xff;head[3]=(len>>8)&0xff;
+    XY_Write(fd,head,4);
+    for(i=0;i<len;i++) {
+        out_sint32(fd,data[i]);
+    }
+    return 0;
+}
+
+static int
+xl_ubyte_xy(int fd,unsigned char xdata,unsigned char ydata) {
+    unsigned char const tag=0xd0;
+    XY_Write(fd,&data,1);
+    out_ubyte(fd,ydata);
+    return out_ubyte(fd,xdata);
+}
+#endif
+static int
+xl_uint16_xy(int fd,unsigned short xdata,unsigned short ydata ) {
+    unsigned char const tag=0xd1;
+    XY_Write(fd,&tag,1);
+    out_uint16(fd,xdata);
+    return out_uint16(fd,ydata);
+}
+#if 0
+static int
+xl_uint32_xy(int fd,unsigned int xdata,unsigned int ydata ) {
+    unsigned char const tag=0xd2;
+    XY_Write(fd,&tag,1);
+    out_uint32(fd,xdata);
+    return out_uint32(fd,ydata);
+}
+#endif
+static int
+xl_sint16_xy(int fd,signed short xdata,signed short ydata ) {
+    unsigned char const tag=0xd3;
+    XY_Write(fd,&tag,1);
+    out_sint16(fd,xdata);
+    return out_sint16(fd,ydata);
+}
+
+#if 0
+static int
+xl_sint32_xy(int fd,signed int xdata,signed int ydata ) {
+    unsigned char const tag=0xd4;
+    XY_Write(fd,&tag,1);
+    out_sint32(fd,xdata);
+    return out_sint32(fd,ydata);
+}
+#endif
+
+static int
+xl_attr_ubyte(int fd,enum Attribute const data) {
+    unsigned char const tag=0xf8;
+    XY_Write(fd,&tag,1);
+    return out_ubyte(fd,data);
+}
+#if 0
+static int
+xl_attr_uint16(int fd,enum Attribute const data ) {
+    unsigned char const tag=0xf9;
+    XY_Write(fd,&tag,1);
+    return out_uint16(fd,data);
+}
+#endif
+static int
+xl_dataLength(int fd,unsigned int dataLength ) {
+    unsigned char const tag=0xfa;
+    XY_Write(fd,&tag,1);
+    return out_uint32(fd,dataLength);
+}
+
+#if 0
+static int
+xl_dataLengthbytes(int fd,unsigned char dataLengthBytes) {
+    unsigned char const tag=0xfb;
+    XY_Write(fd,&tag,1);
+    return out_ubyte(fd,dataLengthBytes);
+}
+#endif 
+
+
+
+static void
+copyFile(const char * const sourceFileName,
+         int          const destFd) {
+
+    FILE * sourceFileP;
+
+    sourceFileP = pm_openr(sourceFileName);
+
+    while (!feof(sourceFileP)) {
+        char buffer[1024];
+        size_t bytesRead;
+        size_t totalBytesWritten;
+
+        bytesRead = fread(buffer, 1, sizeof(buffer), sourceFileP);
+
+        if (ferror(sourceFileP))
+            pm_error("Read from file failed.  errno=%d (%s)",
+                     errno, strerror(errno));
+        
+        totalBytesWritten = 0;
+        
+        while (totalBytesWritten < bytesRead) {
+            ssize_t rc;
+
+            rc = write(destFd, buffer, bytesRead);
+
+            if (rc < 0)
+                pm_error("Write to file failed. errno=%d (%s)",
+                         errno, strerror(errno));
+            else
+                totalBytesWritten += rc;
+        }
+    }
+    pm_close(sourceFileP);
+}
+
+
+
+static void
+jobHead(int          const outFd,
+        bool         const renderGray,
+        const char * const userJobSetupFileName) {
+/*----------------------------------------------------------------------------
+   Start a PJL job.
+
+   Switch printer to PCL-XL mode.  This is called "entering a printer
+   language" in PCL terms.  In particular, we enter the PCL-XL language,
+   as opposed to e.g. Postscript.
+-----------------------------------------------------------------------------*/
+    /* Reset */
+    XY_Puts(outFd,"\033%-12345X");  
+
+    if (userJobSetupFileName)
+        copyFile(userJobSetupFileName, outFd);
+
+    if (renderGray)
+        XY_Puts(outFd,"@PJL SET RENDERMODE=GRAYSCALE\n");  
+
+    XY_Puts(outFd,"@PJL ENTER LANGUAGE=PCLXL\n");  
+    XY_Puts(outFd,") HP-PCL XL;1;1;Generated by Netpbm Pnmtopclxl\n");  
+}
+
+
+
+static void
+jobEnd(int const outFd) {
+/*----------------------------------------------------------------------------
+   End a PJL job.
+
+   Reset printer to quiescent mode.  Exit the printer language.
+-----------------------------------------------------------------------------*/
+    XY_Puts(outFd,"\033%-12345X");  
+}
+
+
+
+static void
+beginPage(int                 const outFd,
+          bool                const doDuplex,
+          enum DuplexPageMode const duplex,
+          bool                const doMediaSource,
+          int                 const mediaSource,
+          bool                const doMediaDestination,
+          int                 const mediaDestination,
+          enum MediaSize      const format) {
+/*----------------------------------------------------------------------------
+   Emit a BeginPage printer command.
+-----------------------------------------------------------------------------*/
+    if (doDuplex) {
+        xl_ubyte(outFd, duplex);  xl_attr_ubyte(outFd, aDuplexPageMode);
+    }
+
+    if (doMediaSource) {
+        /* if not included same as last time in same session is selected */
+        xl_ubyte(outFd, mediaSource);  xl_attr_ubyte(outFd, aMediaSource);
+    }
+
+    if (doMediaDestination) {
+        xl_ubyte(outFd, mediaDestination);  
+        xl_attr_ubyte(outFd, aMediaDestination);
+    }
+
+    xl_ubyte(outFd, ePortraitOrientation); xl_attr_ubyte(outFd, aOrientation);
+    xl_ubyte(outFd, format); xl_attr_ubyte(outFd, aMediaSize);
+
+    XL_Operator(outFd, oBeginPage);
+}
+
+
+
+static void
+openDataSource(int             const outFd,
+               enum DataOrg    const dataOrg,
+               enum DataSource const dataSource) {
+
+    xl_ubyte(outFd, dataOrg); xl_attr_ubyte(outFd,aDataOrg);
+    xl_ubyte(outFd, dataSource); xl_attr_ubyte(outFd,aSourceType);
+    XL_Operator(outFd,oOpenDataSource);
+}
+
+
+
+static void
+setColorSpace(int                   const outFd,
+              enum Colorspace       const colorSpace,
+              const unsigned char * const palette,
+              unsigned int          const paletteSize,
+              enum ColorDepth       const paletteDepth) {
+/*----------------------------------------------------------------------------
+   Emit printer control to set the color space.
+
+   'palette' == NULL means no palette (raster contains colors, not indexes
+   into a palette).
+
+   'paletteSize' is the number of bytes in the palette (undefined if
+   'palette' is NULL)
+
+   'paletteDepth' is the color depth of the entries in the palette.
+   e8Bit means the palette contains 8 bit values.
+
+   The palette is a "direct color" palette: A separate table for each
+   color component (i.e. one table for grayscale; three tables for
+   RGB).  Each table is indexed by a value from the raster and yields
+   a byte of color component value.
+
+   The palette has to be the right size to fit the number of color
+   components and raster color depth (bits per component in the raster).
+
+   E.g. with raster color depth of 1 bit (e1Bit) and RGB color (eRGB),
+   'paletteSize' would have to be 6 -- a table for each of R, G, and
+   B, of two elements each.
+
+   It is not clear from the documentation what the situation is when
+   paletteDepth is not e8Bit.  Is each palette entry still a byte and only
+   some of the byte gets used?  Or are there multiple entries per byte?
+-----------------------------------------------------------------------------*/
+    xl_ubyte(outFd, colorSpace); xl_attr_ubyte(outFd, aColorSpace);   
+    if (palette) {
+        xl_ubyte(outFd, paletteDepth); 
+        xl_attr_ubyte(outFd, aPaletteDepth);   
+        xl_ubyte_array(outFd, palette, paletteSize); 
+        xl_attr_ubyte(outFd, aPaletteData);
+    }
+    XL_Operator(outFd, oSetColorSpace);
+}
+
+
+
+static void
+positionCursor(int            const outFd,
+               bool           const center,
+               float          const xoffs,
+               float          const yoffs,
+               int            const imageWidth,
+               int            const imageHeight,
+               int            const dpi,
+               enum MediaSize const format) {
+/*----------------------------------------------------------------------------
+   Emit printer control to position the cursor to start the page.
+-----------------------------------------------------------------------------*/
+    float xpos, ypos;
+
+    if (center) {
+        float const width  = 1.0 * imageWidth/dpi;  
+        float const height = 1.0 * imageHeight/dpi;    
+        xpos = (PAPERWIDTH(format) - width)/2;
+        ypos = (PAPERHEIGHT(format) - height)/2;
+    } else {
+        xpos = xoffs;
+        ypos = yoffs;
+    }
+    /* cursor positioning */
+    xl_sint16_xy(outFd, xpos * dpi, ypos * dpi); xl_attr_ubyte(outFd, aPoint);
+    XL_Operator(outFd, oSetCursor);
+}
+
+
+
+static void
+convertAndWriteRleBlock(int                  const outFd,
+                        const pclGenerator * const pclGeneratorP,
+                        struct pam *         const pamP,
+                        int                  const firstLine,
+                        int                  const nlines,
+                        XY_rle *             const rle) {
+
+    unsigned char const pad[4] = {0,0,0,0};
+    unsigned int const paddedLinelen = ((pclGeneratorP->linelen+3)/4)*4;
+    int rlelen;
+    unsigned int line;
+    
+    XY_RLEreset(rle);
+
+    for (line = firstLine; line < firstLine + nlines; ++line) {
+        pclGeneratorP->getnextrow(pclGeneratorP, pamP);
+        XY_RLEput(rle, pclGeneratorP->data, pclGeneratorP->linelen);
+        XY_RLEput(rle, pad, paddedLinelen - pclGeneratorP->linelen);
+    }
+    rlelen = XY_RLEfinish(rle);
+    if (rlelen<0) 
+        pm_error("Error on Making rle");
+
+    xl_dataLength(outFd, rlelen); 
+    XY_Write(outFd, rle->fbuf, rlelen);
+}
+
+
+
+/*
+ * ------------------------------------------------------------
+ * XL_WriteImage
+ *  Write a PCL-XL image to the datastream 
+ * ------------------------------------------------------------
+ */
+static void 
+convertAndWriteImage(int                  const outFd,
+                     const pclGenerator * const pclGenP,
+                     struct pam *         const pamP) {
+
+    int blockStartLine;
+    XY_rle * rle;
+
+    xl_ubyte(outFd, eDirectPixel); xl_attr_ubyte(outFd, aColorMapping);
+    xl_ubyte(outFd, pclGenP->colorDepth); xl_attr_ubyte(outFd, aColorDepth);
+    xl_uint16(outFd, pclGenP->width); xl_attr_ubyte(outFd, aSourceWidth);  
+    xl_uint16(outFd, pclGenP->height); xl_attr_ubyte(outFd, aSourceHeight);    
+    xl_uint16_xy(outFd, pclGenP->width*1, pclGenP->height*1); 
+    xl_attr_ubyte(outFd, aDestinationSize);   
+    XL_Operator(outFd, oBeginImage);
+
+    rle = XY_RLEnew(pclGenP->linelen*20);
+    if (!rle) 
+        pm_error("Unable to allocate %d bytes for the RLE buffer",
+                 pclGenP->linelen * 20);
+
+    blockStartLine = 0;
+    while (blockStartLine < pclGenP->height) {
+        unsigned int const blockHeight =
+            MIN(20, pclGenP->height-blockStartLine);
+        xl_uint16(outFd, blockStartLine); xl_attr_ubyte(outFd, aStartLine); 
+        xl_uint16(outFd, blockHeight); xl_attr_ubyte(outFd, aBlockHeight);
+        xl_ubyte(outFd, eRLECompression); xl_attr_ubyte(outFd, aCompressMode);
+        /* In modern PCL-XL, we could use a PadBytesMultiple attribute
+           here to avoid having to pad the data to a multiple of 4
+           bytes.  But PCL-XL 1.1 didn't have PadBytesMultiple.
+           xl_ubyte(outFd, 1); xl_attr_ubyte(outFd, aPadBytesMultiple); 
+        */
+        XL_Operator(outFd, oReadImage);
+        convertAndWriteRleBlock(outFd, pclGenP, pamP,
+                                blockStartLine, blockHeight, rle);
+        blockStartLine += blockHeight;
+    }
+    XY_RLEdelete(rle);
+    XL_Operator(outFd, oEndImage);
+}
+
+
+
+static void
+endPage(int          const outFd,
+        bool         const doCopies,
+        unsigned int const copies) {
+/*----------------------------------------------------------------------------
+   Emit an EndPage printer command.
+-----------------------------------------------------------------------------*/
+    if (doCopies) {
+        /* wrong in example in PCL-XL manual. Type is uint16 ! */
+        xl_uint16(outFd, copies); xl_attr_ubyte(outFd, aPageCopies);
+    }
+    XL_Operator(outFd, oEndPage);
+}
+
+
+
+static void
+convertAndPrintPage(int                  const outFd,
+                    const pclGenerator * const pclGeneratorP,
+                    struct pam *         const pamP,
+                    enum MediaSize       const format,
+                    int                  const dpi,
+                    bool                 const center,
+                    float                const xoffs,
+                    float                const yoffs,
+                    bool                 const doDuplex,
+                    enum DuplexPageMode  const duplex,
+                    bool                 const doCopies,
+                    unsigned int         const copies,
+                    bool                 const doMediaSource,
+                    int                  const mediaSource,
+                    bool                 const doMediaDestination,
+                    int                  const mediaDestination) {
+
+    beginPage(outFd, doDuplex, duplex, doMediaSource, mediaSource,
+              doMediaDestination, mediaDestination, format);
+
+    /* Before Netpbm 10.27 (March 2005), we always set up a two-byte 8
+       bit deep palette: {0, 255}.  I don't know why, because this
+       works only for e1Bit color depth an eGray color space, and in
+       that case does the same thing as having no palette at all.  But
+       in other cases, it doesn't work.  E.g. with eRGB, e8Bit, we got
+       an IllegalArraySize error from the printer on the SetColorSpace
+       command.
+
+       So we don't use a palette at all now.  
+    */
+    setColorSpace(outFd, pclGeneratorP->colorSpace, NULL, 0, 0);
+
+    positionCursor(outFd, center, xoffs, yoffs, 
+                   pclGeneratorP->width, pclGeneratorP->height, dpi, format);
+
+    convertAndWriteImage(outFd, pclGeneratorP, pamP);
+
+    endPage(outFd, doCopies, copies);
+}
+
+
+
+static void
+beginSession(int              const outFd,
+             unsigned int     const xdpi,
+             unsigned int     const ydpi,
+             enum Measure     const measure,
+             bool             const noReporting,
+             enum ErrorReport const errorReport) {
+
+    xl_uint16_xy(outFd, xdpi, ydpi); xl_attr_ubyte(outFd, aUnitsPerMeasure); 
+    xl_ubyte(outFd, measure);  xl_attr_ubyte(outFd, aMeasure);
+    /* xl_ubyte(outFd,eNoReporting); xl_attr_ubyte(outFd,aErrorReport); */
+    xl_ubyte(outFd,errorReport); xl_attr_ubyte(outFd,aErrorReport);
+    XL_Operator(outFd,oBeginSession);
+}
+
+
+             
+static void 
+endSession(int outFd) {
+    XL_Operator(outFd,oEndSession);
+}
+
+
+
+static void
+pnmToPcllinePackbits(const pclGenerator * const pclGeneratorP,
+                     struct pam *         const pamP) {
+
+    tuple * tuplerow;
+    unsigned int pcl_cursor;
+    unsigned char accum;
+    unsigned char bitmask;
+    unsigned int col;
+        
+    tuplerow = pnm_allocpamrow(pamP);
+
+    pnm_readpamrow(pamP, tuplerow);
+
+    pcl_cursor = 0; bitmask = 0x80; accum = 0x00;
+    for (col = 0; col < pamP->width; ++col) {
+        if (tuplerow[col][0] == PAM_PBM_WHITE)
+            accum |= bitmask;
+        bitmask >>= 1;
+        if (bitmask == 0) {
+            pclGeneratorP->data[pcl_cursor++] = accum;
+            bitmask = 0x80; accum = 0x0;
+        } 
+    }
+    if (bitmask != 0x80)
+        pclGeneratorP->data[pcl_cursor++] = accum;
+
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+createPclGeneratorPackbits(struct pam *    const pamP,
+                           pclGenerator ** const pclGeneratorPP) {
+
+    /* Samples are black or white and packed 8 to a byte */
+
+    pclGenerator * pclGeneratorP;
+
+    MALLOCVAR_NOFAIL(pclGeneratorP);
+
+    pclGeneratorP->colorDepth = e1Bit;
+    pclGeneratorP->colorSpace = eGray;
+    pclGeneratorP->linelen = (pamP->width+7)/8;
+    pclGeneratorP->height = pamP->height;
+    pclGeneratorP->width = (pamP->width);
+
+    pclGeneratorP->data = malloc(pclGeneratorP->linelen);
+    
+    if (pclGeneratorP->data == NULL)
+        pm_error("Unable to allocate row buffer.");
+
+    pclGeneratorP->getnextrow = pnmToPcllinePackbits;
+
+    *pclGeneratorPP = pclGeneratorP;
+}
+
+
+
+static void
+pnmToPcllineWholebytes(const pclGenerator * const pclGeneratorP,
+                       struct pam *         const pamP) {
+
+    tuple * tuplerow;
+    unsigned int pcl_cursor;
+    unsigned int col;
+
+    tuplerow = pnm_allocpamrow(pamP);
+
+    pnm_readpamrow(pamP, tuplerow);
+
+    pcl_cursor = 0; /* initial value */
+    
+    for (col = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane) {
+            pclGeneratorP->data[pcl_cursor++] = 
+                pnm_scalesample(tuplerow[col][plane], pamP->maxval, 255);
+        }
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+createPclGeneratorWholebytes(struct pam *    const pamP,
+                             pclGenerator ** const pclGenPP) {
+    /* One sample per byte */
+
+    pclGenerator * pclGenP;
+
+    MALLOCVAR_NOFAIL(pclGenP);
+    
+    if (pamP->depth <  3)
+        pclGenP->colorSpace = eGray;
+    else
+        pclGenP->colorSpace = eRGB;
+
+    pclGenP->colorDepth = e8Bit;
+    pclGenP->height     = pamP->height;
+    pclGenP->width      = pamP->width;
+    pclGenP->linelen    = pamP->width * pamP->depth;
+
+    if (UINT_MAX / pamP->width < pamP->depth)
+        pm_error("Image to big to process");
+    else
+        pclGenP->data = malloc(pamP->width * pamP->depth);
+
+    if (pclGenP->data == NULL)
+        pm_error("Unable to allocate row buffer.");
+    
+    pclGenP->getnextrow = pnmToPcllineWholebytes;
+
+    *pclGenPP = pclGenP;
+}
+
+
+
+static void
+destroyPclGenerator(pclGenerator * const pclGenP) {
+
+    free(pclGenP->data);
+
+    free(pclGenP);
+}
+
+
+
+static void 
+createPclGenerator(struct pam *        const pamP,
+                   pclGenerator **     const pclGeneratorPP,
+                   bool                const colorok) {
+
+    if (pamP->depth > 1 && !colorok)
+        pm_message("WARNING: generating a color print stream because the "
+                   "input image is PPM.  "
+                   "To generate a black and white print stream, run the input "
+                   "through Ppmtopgm.  To suppress this warning, use the "
+                   "-colorok option.");
+
+    if (pamP->depth == 1 && pamP->maxval == 1) 
+        createPclGeneratorPackbits(pamP, pclGeneratorPP);
+    else 
+        createPclGeneratorWholebytes(pamP, pclGeneratorPP);
+}
+
+
+
+
+static void
+printPages(int                 const outFd,
+           InputSource *       const firstSourceP,
+           enum MediaSize      const format,
+           int                 const dpi,
+           bool                const center,
+           float               const xoffs,
+           float               const yoffs,
+           bool                const doDuplex,
+           enum DuplexPageMode const duplex,
+           bool                const doCopies,
+           unsigned int        const copies,
+           bool                const doMediaSource,
+           int                 const mediaSource,
+           bool                const doMediaDestination,
+           int                 const mediaDestination,
+           bool                const colorok) {
+/*----------------------------------------------------------------------------
+  Loop over all input files, and each file, all images.
+-----------------------------------------------------------------------------*/
+    InputSource * sourceP;
+    unsigned int sourceNum;
+
+    sourceP = firstSourceP;    
+
+    openDataSource(outFd, eBinaryLowByteFirst, eDefaultSource);
+
+    sourceNum = 0;   /* initial value */
+
+    while (sourceP) {
+        FILE * in_file;
+        struct pam pam;
+        bool eof;
+        unsigned int pageNum;
+
+        in_file = pm_openr(sourceP->name);
+
+        ++sourceNum;
+
+        pageNum = 0;  /* initial value */
+
+        eof = FALSE;
+        while(!eof) {
+            pnm_nextimage(in_file, &eof);
+            if (!eof) {
+                pclGenerator * pclGeneratorP;
+
+                ++pageNum;
+                pm_message("Processing File %u, Page %u", sourceNum, pageNum);
+
+                pnm_readpaminit(in_file, &pam, PAM_STRUCT_SIZE(tuple_type));
+                
+                createPclGenerator(&pam, &pclGeneratorP, colorok);
+                
+                convertAndPrintPage(
+                    outFd, pclGeneratorP, &pam,
+                    format, dpi, center, xoffs, yoffs, doDuplex, duplex,
+                    doCopies, copies, doMediaSource, mediaSource,
+                    doMediaDestination, mediaDestination);
+
+                destroyPclGenerator(pclGeneratorP);
+            }
+        }
+        pm_close(in_file);
+        sourceP = sourceP->next; 
+    }
+    XL_Operator(outFd, oCloseDataSource);
+}
+
+
+
+static void
+freeSource(InputSource * const firstSourceP) {
+    
+    InputSource * sourceP;
+
+    sourceP = firstSourceP;
+    while(sourceP) {
+        InputSource * const nextP = sourceP->next;
+        free(sourceP);
+        sourceP = nextP;
+    }
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    int const outFd = STDOUT_FILENO;
+
+    struct cmdlineInfo cmdline;
+    
+    /* In case you're wondering why we do direct file descriptor I/O
+       instead of stream (FILE *), it's because Jochen originally 
+       wrote this code for an embedded system with diet-libc.  Without
+       the stream library, the statically linked binary was only about
+       5K big.
+    */
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    jobHead(outFd, cmdline.rendergray, cmdline.jobsetup);
+
+    beginSession(outFd, cmdline.dpi, cmdline.dpi, eInch, 
+                 FALSE, eBackChAndErrPage);
+
+    printPages(outFd,cmdline.sourceP,
+               cmdline.format, cmdline.dpi, cmdline.center,
+               cmdline.xoffs, cmdline.yoffs,
+               cmdline.duplexSpec, cmdline.duplex,
+               cmdline.copiesSpec, cmdline.copies,
+               cmdline.feederSpec, cmdline.feeder,
+               cmdline.outtraySpec, cmdline.outtray,
+               cmdline.colorok
+        );
+    endSession(outFd);
+
+    jobEnd(outFd);
+
+    freeSource(cmdline.sourceP);
+
+    return 0;
+}