/*
 * -------------------------------------------------------------
 *
 *  (C) 2002 Jochen Karrer, Linuxdata GbR
 *
 * xy_pnm2pclxl.c
 *      convert a pnm to PCL-XL image 
 *
 * -------------------------------------------------------------
 */

#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"



static int
readtonewline(FILE *file,char *buf,int maxlen) {
    int len=0;
    char c;
    while(len<maxlen-1) {
        int ret=fread(&c,1,1,file);
        if(ret==0) {
            buf[len]=0;
            return len;
        } else if(ret<0) {
            buf[len]=0;
            return ret;
        }
        if(c=='\n') {
            buf[len]=0;
            return len;
        }
        if(c!='\r') {
            buf[len]=c;
            len++;
        }
    }   
    buf[len]=0;
    while(fread(&c,1,1,file)==1) {
        if(c=='\n')
            break;
    }
    return len; 
}

static int
scanuint(char *str,unsigned int *value) 
{
        int i=0,base = 10;
        *value = 0;
        if(str==NULL)
                return 0;
    while(isspace(str[i])) {
        i++;
    }
        for(;   ((str[i]>='0') && (str[i]<='9')) ||
                (str[i] == 'x') ||
                ((base > 10)& (str[i] >= 'a') && (str[i] <= 'f')) ;i++) {

                if(str[i] == 'x') {
                        base = 16;
                } else if ( (str[i] >= 'a') && (str[i] <= 'f') )  {
                        *value = *value * base + str[i] - 'a' + 10;
                } else {
                        *value = *value * base + str[i] - '0';
                }
        }
        return i;
}

#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;


enum {
    eSTART,
    eRLE,
    eLIT 
} RLEstates;

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=(XY_rle*)malloc(sizeof(*rle));
    if(rle==0)
        return rle;
    if(size<1024)
        size=1024;
    XY_RLEreset(rle);
    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,unsigned char *buf,int count) 
{
    int i;
    for(i=0;i<count;i++) {
        rle_putbyte(rle,buf[i]);
    }
    
}


static int
XY_Write(int fd, 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))

enum {
    eClockWise,
    eCounterClockWise
} ArcDirection;

enum {
    eNoSubstitution,
    eVerticalSubstitution
} CharSubModeArray;

enum {
    eInterior,
    eExterior
} ClipRegion;

enum  {
    e1Bit,
    e4Bit,
    e8Bit
} ColorDepth;

enum {
    eDirectPixel,
    eIndexedPixel
} ColorMapping;

enum {
    eGray=1,
    eRGB=2
} Colorspace;

enum {
    eNoCompression,
    eRLECompression,
    eJPEGCompression
} Compression;

enum {
    eBinaryHighByteFirst,
    eBinaryLowByteFirst
} DataOrg;

enum {
    eDefault
} DataSource;

enum {
    eUByte,
    eSByte,
    eUint16,
    eSint16
} DataType;

enum {  
    eDeviceBest
} DitherMatrix; 

enum {
    eDuplexHorizontalBinding,
    eDuplexVerticalBinding
} DuplexPageMode;

enum {
    eFrontMediaSide,
    eBackMediaSide
} DuplexPageSide;

enum {
    eNoReporting,   
    eBackChannel,
    eErrorPage,
    eBackChAndErrPage,
    eNWBackChannel,
    eNWErrorPage,
    eNWBackChAndErrPage
} ErrorReport;

enum {
    eNonZeroWinding,
    eEvenOdd
} FillMode;

enum {
    eButtCap,
    eRoundCap,
    eSquareCap,
    eTriangleCap
} LineCap;

enum {
    eMiterJoin,
    eRoundJoin,
    eBevelJoin,
    eNoJoin
} LineJoin;

enum {
    eInch,
    eMillimeter,
    eTenthsOfAMillimeter    
} Measure;

enum {
    eLetterPaper,
    eLegalPaper,
    eA4Paper,
    eExecPaper,
    eLedgerPaper,
    eA3Paper,
    eCOM10Envelope,
    eMonarchEnvelope,
    eC5Envelope,
    eDLEnvelope,
    eJB4Paper,
    eJB5Paper,
    etralala,
    eB5Envelope,
    eJPostcard,
    eJDoublePostcard,
    eA5Paper,
    eA6Paper,
    eJB6Paper
} MediaSize;
enum {
    eDefaultSource,
    eAutoSelect,
    eManualFeed,
    eMultiPurposeTray,
    eUpperCassette,
    eEnvelopeTray,
    eThirdCassette
} MediaSource;

enum {
    eDefaultDestination,
    eFaceDownBin,
    eFaceUpBin,
    eJobOffsetBin
} MediaDestination;
enum {
    ePortraitOrientation,
    eLandscapeOrientation,
    eReversePortrait,
    eReverseLandscape   
} Orientation;

enum {
    eTempPatter,
    ePagePatter,
    eSessionPattern
} PatternPeristence;

enum {
    eSimplexFrontSide
} SimplexPageMode;

enum {
    eOpaque,
    eTransparent
} TxMode;

enum {
    eHorizontal,
    eVertical
} WritingMode;
#define PaletteDepth        (2)
#define ColorSpace      (3)
#define NullBrush       (4)
#define NullPen         (5)
#define PaletteData     (6)
#define PatternSelectID     (8)
#define GrayLevel       (9)
#define RGBColor        (11)
#define PatternOrigin       (12)
#define NewDestinationSize  (13)
#define PrimaryArray        (14)
#define PrimaryDepth        (15)
#define DeviceMatrix        (33)
#define DitherMatrixDataType    (34)
#define DitherOrigin        (35)
#define MediaSize       (37)
#define MediaSource     (38)
#define MediaType       (39)
#define Orientation     (40)
#define PageAngle       (41)
#define PageOrigin      (42)
#define PageScale       (43)
#define ROP3            (44)
#define TxMode          (45)
#define CustomMediaSize     (47)
#define CustomMediaUnits    (48)
#define PageCopies      (49)
#define DitherMatrixSize    (50)
#define DitherMatrixDepth   (51)
#define SimplexPageMode     (52)
#define DuplexPageMode      (53)
#define DuplexPageSide      (54)
#define ArcDirection        (65)
#define BoundingBox     (66)
#define DashOffset      (67)
#define EllipseDimension    (68)
#define EndPoint        (69)
#define FillMode        (70)
#define LineCapStyle        (71)
#define LineJoinStyle       (72)
#define MiterLength     (73)
#define LineDashStyle       (74)
#define PenWidth        (75)
#define Point           (76)
#define NumberOfPoints      (77)
#define SolidLine       (78)
#define StartPoint      (79)
#define PointType       (80)
#define ControlPoint1       (81)
#define ControlPoint2       (82)
#define ClipRegion      (83)
#define ClipMode        (84)
#define ColorDepth      (98)
#define BlockHeight     (99)
#define ColorMapping        (100)
#define CompressMode        (101)
#define DestinationBox      (102)
#define DestinationSize     (103)
#define PatternPersistence  (104)
#define PatternDefineID     (105)
#define SourceHeight        (107)
#define SourceWidth     (108)
#define StartLine       (109)
#define PadBytesMultiple    (110)
#define BlockByteLength     (111)
#define NumberOfScanLines   (115)
#define CommentData     (129)
#define DataOrg         (130)
#define Measure         (134)
#define SourceType      (136)
#define UnitsPerMeasure     (137)
#define StreamName      (139)
#define StreamDataLength    (140)
#define ErrorReport     (143)
#define CharAngle       (161)
#define CharCode        (162)
#define CharDataSize        (163)
#define CharScale       (164)
#define CharShear       (165)
#define CharSize        (166)
#define FontHeaderLength    (167)
#define FontName        (168)
#define FontFormat      (169)
#define SymbolSet       (170)
#define TextData        (171)
#define CharSubModeArray    (172)
#define XSpacingData        (175)
#define YSpacingData        (176)
#define CharBoldValue       (177)

/* operators */
#define BeginSession        (0x41)
#define EndSession          (0x42)
#define BeginPage           (0x43)
#define EndPage         (0x44)
#define Comment         (0x47)
#define OpenDataSource      (0x48)
#define CloseDataSource     (0x49)

#define BeginFontHeader     (0x4f)
#define ReadFontHeader      (0x50)
#define EndFontHeader       (0x51)
#define BeginChar       (0x52)
#define ReadChar        (0x53)
#define EndChar         (0x54)
#define RemoveFont      (0x55)
#define SetCharAttributes   (0x56)
#define BeginStream     (0x5b)
#define ReadStream      (0x5c)
#define EndStream       (0x5d)
#define ExecStream      (0x5e)
#define RemoveStream        (0x5f)
#define PopGS           (0x60)
#define PushGS          (0x61)
#define SetClipReplace      (0x62)
#define SetBrushSource      (0x63)
#define SetCharAngle        (0x64)
#define SetCharScale        (0x65)
#define SetCharShear        (0x66)
#define SetClipIntersect    (0x67)
#define SetClipRectangle    (0x68)
#define SetClipToPage       (0x69)
#define SetColorSpace       (0x6a)
#define SetCursor       (0x6b)
#define SetCursorRel        (0x6c)
#define SetHalftoneMethod   (0x6d)
#define SetFillMode     (0x6e)
#define SetFont         (0x6f)
#define SetLineDash     (0x70)
#define SetLineCap      (0x71)
#define SetLineJoin     (0x72)
#define SetMiterLimit       (0x73)
#define SetPageDefaultCTM   (0x74)
#define SetPageOrigin       (0x75)
#define SetPageRotation     (0x76)
#define SetPageScale        (0x77)
#define SetPatternTxMode    (0x78)
#define SetPenSource        (0x79)
#define SetPenWidth     (0x7a)
#define SetROP          (0x7b)
#define SetSourceTxMode     (0x7c)
#define SetCharBoldValue    (0x7d)
#define SetClipMode     (0x7f)
#define SetPathToClip       (0x80)
#define SetCharSubMode      (0x81)
#define CloseSubPath        (0x84)
#define NewPath         (0x85)
#define PaintPath       (0x86)
#define ArcPath         (0x91)
#define BezierPath      (0x93)
#define BezierRelPath       (0x95)
#define Chord           (0x96)
#define ChordPath       (0x97)
#define Ellipse         (0x98)
#define EllipsePath     (0x99)
#define LinePath        (0x9b)
#define LineRelPath     (0x9d)
#define Pie         (0x9e)
#define PiePath         (0x9f)
#define Rectangle       (0xa0)
#define RectanglePath       (0xa1)
#define RoundRectangle      (0xa2)  
#define RoundRectanglePath  (0xa3)
#define Text            (0xa8)
#define TextPath        (0xa9)
#define BeginImage      (0xb0)
#define ReadImage       (0xb1)
#define EndImage        (0xb2)
#define BeginRastPattern    (0xb3)
#define ReadRastPattern     (0xb4)
#define EndRastPattern      (0xb5)
#define BeginScan       (0xb6)
#define EndScan         (0xb8)
#define ScanLineRel     (0xb9)



typedef struct pcl_img {
    unsigned char colorDepth;
    unsigned char colorSpace;
    int width,height;
    int linelen; /* bytes per line */
    unsigned char *data;
} XL_pcl_img;

struct sPaperFormat {
    char *name;
    int xl_nr;
    float width,height; /* inch */
} xlPaperFormats[] = {
    {"letter",eLetterPaper,8.5,11.0},
    {"legal",eLegalPaper,8.5,14},
    {"a4",eA4Paper,8.26389,11.6944},
    {"exec",eExecPaper,190/2.54,254/2.54},
    {"ledger",eLedgerPaper,279/2.54,432/2.54},
    {"a3",eA3Paper,11.6944,16.5278},
    {"com10envelope",eCOM10Envelope,105/2.54,241/2.54},
    {"monarchenvelope",eMonarchEnvelope,98/2.54,191/2.54},
    {"c5envelope",eC5Envelope,162/2.54,229/2.54},
    {"dlenvelope",eDLEnvelope,110/2.54,220/2.54},
    {"jb4",eJB4Paper,257/2.54,364/2.54},
    {"jb5",eJB5Paper,182/2.54,257/2.54},
    {"b5envelope",eB5Envelope,176/2.54,250/2.54},
    {"",-1,0,0},
    {"jpostcard",eJPostcard,148/2.54,200/2.54},
    {"jdoublepostcard",eJDoublePostcard,100/2.54,148/2.54},
    {"a5",eA5Paper, 5.84722,8.26389},
    {"a6",eA6Paper,4.125,5.84722},
    {"jb6",eJB6Paper,0,0},
    {NULL,0,0,0}
};
#define PAPERWIDTH(format) (xlPaperFormats[format].width)
#define PAPERHEIGHT(format) (xlPaperFormats[format].height)

struct tPrinter 
{ 
    char *name;
    float topmargin;
    float bottommargin;
    float leftmargin;
    float rightmargin;
} xlPrinters[] = {
    { "lj2200",0,0,0,0 }
};

typedef struct InputSource {
    char *name; 
    FILE *in_file;
    struct InputSource *next;   
}InputSource;

struct jobinfo {
    int dpi;    
    int format;
    int mediaSource;
    int mediaDestination;
    int duplex;
    int copies;
    int center;
    float xoffs,yoffs;
    int colorok;
    struct InputSource *source;
};

static  int
out_ubyte(int fd,unsigned char data) {
    return XY_Write(fd,&data,1);
}
static  int 
XL_Operator(int fd,unsigned char 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 tag=0xc0;
    XY_Write(fd,&tag,1);
    return out_ubyte(fd,data);
}
static int
xl_uint16(int fd,unsigned short data ) {
    unsigned char 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 c=0xc2;
    XY_Write(fd,&c,1);
    return out_uint32(fd,data);
}
static int
xl_sint16(int fd,signed short data ) {
    unsigned char c=0xc3;
    XY_Write(fd,&c,1);
    return out_sint16(fd,data);
}
static int
xl_sint32(int fd,signed int data ) {
    unsigned char c=0xc4;
    XY_Write(fd,&c,1);
    return out_sint32(fd,data);
}
#endif
static int
xl_ubyte_array(int fd,unsigned char *data,int len) {
    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 data=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 c=0xd1;
    XY_Write(fd,&c,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 c=0xd2;
    XY_Write(fd,&c,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 c=0xd3;
    XY_Write(fd,&c,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 c=0xd4;
    XY_Write(fd,&c,1);
    out_sint32(fd,xdata);
    return out_sint32(fd,ydata);
}
#endif

static int
xl_attr_ubyte(int fd,unsigned char data) {
    unsigned char tag=0xf8;
    XY_Write(fd,&tag,1);
    return out_ubyte(fd,data);
}
#if 0
static int
xl_attr_uint16(int fd,unsigned short data ) {
    unsigned char tag=0xf9;
    XY_Write(fd,&tag,1);
    return out_uint16(fd,data);
}
#endif
static int
xl_dataLength(int fd,unsigned int dataLength ) {
    unsigned char c=0xfa;
    XY_Write(fd,&c,1);
    return out_uint32(fd,dataLength);
}

#if 0
static int
xl_dataLengthbytes(int fd,unsigned char dataLengthBytes) {
    unsigned char tag=0xfb;
    XY_Write(fd,&tag,1);
    return out_ubyte(fd,dataLengthBytes);
}
#endif 
/*
 * ---------------------------------------------------- 
 * job_head/job_end:  
 *  switch printer to PCL-XL mode / reset 
 * ---------------------------------------------------- 
 */
static int 
job_head(int fd) {
    XY_Puts(fd,"\033%-12345X@PJL ENTER LANGUAGE=PCLXL\n");  
    XY_Puts(fd,") HP-PCL XL;1;1;Comment Hello World\n");  
    return 0;
}
static int 
job_end(int fd) {
    XY_Puts(fd,"\033%-12345X");  
    return 0;
}
/*
 * ------------------------------------------------------------
 * XL_WriteImage
 *  Write a PCL-XL image to the datastream 
 * ------------------------------------------------------------
 */
static void 
XL_WriteImage(int s,XL_pcl_img *img) 
{
    int i;
    int rlelen;
    XY_rle *rle;

    xl_ubyte(s,eDirectPixel); xl_attr_ubyte(s,ColorMapping);
    xl_ubyte(s,img->colorDepth); xl_attr_ubyte(s,ColorDepth);
    xl_uint16(s,img->width); xl_attr_ubyte(s,SourceWidth);  
    xl_uint16(s,img->height); xl_attr_ubyte(s,SourceHeight);    
    xl_uint16_xy(s,img->width*1,img->height*1); 
    xl_attr_ubyte(s,DestinationSize);   
    XL_Operator(s,BeginImage);

    rle = XY_RLEnew(img->linelen*20);
    if(!rle) {
        XY_Puts(2,"can't alloc rle\n");
        exit(4);
    }
    for(i=0;i<img->height;i+=20) {
        int nlines=img->height-i;
        if(nlines>20) nlines=20;
        
        xl_uint16(s,i); xl_attr_ubyte(s,StartLine); 
        xl_uint16(s,nlines); xl_attr_ubyte(s,BlockHeight);
        xl_ubyte(s,eRLECompression); xl_attr_ubyte(s,CompressMode);
        xl_ubyte(s,1); xl_attr_ubyte(s,PadBytesMultiple);
        XL_Operator(s,ReadImage);
        XY_RLEput(rle,img->data+img->linelen*i,img->linelen*nlines);
        rlelen=XY_RLEfinish(rle);
        if(rlelen<0) {
            XY_Puts(2,"Error on Making rle\n");
            exit(3);
        }
        xl_dataLength(s,rlelen); 
        XY_Write(s,rle->fbuf,rlelen);
        XY_RLEreset(rle);
    }
    XY_RLEdelete(rle);
    XL_Operator(s,EndImage);
}
static void
print_page(int s,XL_pcl_img *img,struct jobinfo *job) 
{
    unsigned char palette[2]={0,255};
    float xpos=job->xoffs,ypos=job->yoffs;
    if(job->duplex>=0) {
        xl_ubyte(s,job->duplex);  xl_attr_ubyte(s,DuplexPageMode);
    }
    if(job->mediaSource >=0) { 
        /* if not included same as last time in same session is selected */
        xl_ubyte(s,job->mediaSource);  xl_attr_ubyte(s,MediaSource);
    }
    if(job->mediaDestination >=0) {
        xl_ubyte(s,job->mediaDestination);  xl_attr_ubyte(s,MediaDestination);
    }
    xl_ubyte(s,ePortraitOrientation); xl_attr_ubyte(s,Orientation);
    xl_ubyte(s,job->format); xl_attr_ubyte(s,MediaSize);
    XL_Operator(s,BeginPage);
        xl_ubyte(s,img->colorSpace); xl_attr_ubyte(s,ColorSpace);   
        xl_ubyte(s,e8Bit); xl_attr_ubyte(s,PaletteDepth);   
        xl_ubyte_array(s,palette,2); xl_attr_ubyte(s,PaletteData);  
        XL_Operator(s,SetColorSpace);
        
        if(job->center) {
            float width = 1.0*img->width/job->dpi;  
            float height = 1.0*img->height/job->dpi;    
            xpos = (PAPERWIDTH(job->format) - width)/2;
            ypos = (PAPERHEIGHT(job->format) - height)/2;
        } 
        /* cursor positioning */
        xl_sint16_xy(s,xpos*job->dpi,ypos*job->dpi); xl_attr_ubyte(s,Point);
        XL_Operator(s,SetCursor);
        XL_WriteImage(s,img);   
    if(job->copies>=0) {
        /* wrong in example in PCL-XL manual. Type is uint16 ! */
        xl_uint16(s,job->copies); xl_attr_ubyte(s,PageCopies);
    }
    XL_Operator(s,EndPage);
}
/*
 * ---------------------------------------------- 
 * StartSession:  
 *  Prepare for print
 * ---------------------------------------------- 
 */
static void
start_session(int s,struct jobinfo *job)   { 
    xl_uint16_xy(s,job->dpi,job->dpi); 
    xl_attr_ubyte(s,UnitsPerMeasure); 
    xl_ubyte(s,eInch); 
    xl_attr_ubyte(s,Measure);
    /* xl_ubyte(s,eNoReporting); xl_attr_ubyte(s,ErrorReport); */
    xl_ubyte(s,eBackChAndErrPage); 
    xl_attr_ubyte(s,ErrorReport);
    XL_Operator(s,BeginSession);
}        



static void 
end_session(int s) {
    XL_Operator(s,EndSession);
}



static void
pnm_to_pclimg_packbits(struct pam * pamP,XL_pcl_img * img) {
    /* Samples are black or white and packed 8 to a byte */

    tuple * tuplerow;
        
    img->colorDepth=e1Bit;
    img->colorSpace=eGray;
    img->linelen=((pamP->width+7)/8);
    img->height=pamP->height;
    img->width=(pamP->width+7)&~7;

    tuplerow=pnm_allocpamrow(pamP);
    {
        int row;
        unsigned int pcl_cursor;

        pcl_cursor=0;
        for (row=0;row<pamP->height;++row) {
            unsigned char accum;
            unsigned char bitmask;
            int col;

            pnm_readpamrow(pamP, tuplerow);

            bitmask=0x80; accum=0;
            for (col=0;col<pamP->width;++col) {
                if (tuplerow[col][0]==PAM_PBM_WHITE)
                    accum |= bitmask;
                bitmask>>=1;
                if (bitmask==0) {
                    img->data[pcl_cursor++]=accum;
                    bitmask=0x80; accum=0;
                } 
            }
        }
    }
    pnm_freepamrow(tuplerow);
}



static void
pnm_to_pclimg_wholebytes(struct pam * pamP,XL_pcl_img *img) {
    /* One sample per byte */
    
    tuple * tuplerow;
        
    if (pamP->depth==1)
        img->colorSpace=eGray;
    else
        img->colorSpace=eRGB;

    img->colorDepth=e8Bit;
    img->height=pamP->height;
    img->width=pamP->width;
    img->linelen=pamP->width*pamP->depth;

    img->data=(unsigned char *)malloc(pamP->width*pamP->height*pamP->depth);
    tuplerow=pnm_allocpamrow(pamP);
    {
        int row;
        unsigned int pcl_cursor;

        pcl_cursor=0;
        for (row=0;row<pamP->height;++row) {
            int col;
            pnm_readpamrow(pamP, tuplerow);
            for (col=0;col<pamP->width;++col) {
                int plane;
                for (plane=0;plane<pamP->depth;++plane)
                    img->data[pcl_cursor++] = 
                        pnm_scalesample(tuplerow[col][plane], 
                                        pamP->maxval, 255);
            }
        }
    }
    pnm_freepamrow(tuplerow);
}



static void 
pnm_to_pclimg(struct pam * pamP,XL_pcl_img *img,struct jobinfo *job) {

    if (pamP->depth > 1 && !job->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) 
        pnm_to_pclimg_packbits(pamP,img);
    else 
        pnm_to_pclimg_wholebytes(pamP,img);
}



static void
usage(char *pname) {
    XY_Puts(2,"\n");
    XY_Puts(2,pname);
    XY_Puts(2," [options] [-o outfile.pcl] [infile1.pnm] [infile2.pnm] ...\n");
    XY_Puts(2," [-dpi #]                 Select resolution of image (not printer!) (1-65535)\n");
    XY_Puts(2," [-xoffs #]               x-position of upper left corner of\n");
    XY_Puts(2,"                          image on page (default 0 inches)\n");
    XY_Puts(2," [-yoffs #]               y-pos of upper left corner of \n");
    XY_Puts(2,"                          image on page (default 0 inches)\n");
    XY_Puts(2," [-center]                Center image on Paper\n");
    XY_Puts(2,"                          (default: upper left of physical page)\n");
    XY_Puts(2," [-duplex vertical|horizontal]\n");
    XY_Puts(2," [-format <paperformat>]  Select media size.\n");
    XY_Puts(2," [-feeder #]              Media Source\n");
    XY_Puts(2," [-copies #]              print # copies\n");
 	XY_Puts(2," [-colorok]               don't warn about color image\n");
 	XY_Puts(2,"                          default is conversion to gray\n");
    XY_Puts(2,"\n");
}
/*
 * -------------------------------------------------------------------
 * print_pages
 *  Loop over all input files, and each file, all images.
 * -------------------------------------------------------------------
 */

static int
print_pages(int s,struct jobinfo *job) {
    InputSource *source;
    source =job->source;    
    xl_ubyte(s,eBinaryLowByteFirst); xl_attr_ubyte(s,DataOrg);
    xl_ubyte(s,eDefaultSource); xl_attr_ubyte(s,SourceType);
    XL_Operator(s,OpenDataSource);
    while(source) {
        FILE *in_file;
        struct pam pam;
        bool eof;

        in_file = pm_openr(source->name);

        eof = FALSE;
        while(!eof) {
            pnm_nextimage(in_file, &eof);
            if (!eof) {
                XL_pcl_img pcl_img;

                pnm_readpaminit(in_file, &pam, sizeof(pam));
                
                pnm_to_pclimg(&pam,&pcl_img,job);

                print_page(s,&pcl_img,job);

                free(pcl_img.data);
            }
        }
        pm_close(in_file);
        source=source->next; 
    }
    XL_Operator(s,CloseDataSource);
    return 0;
}



int
main(int argc,char *argv[]) {
    InputSource *last=NULL;
    int out_fd=1;
    int i;
    struct jobinfo job;
    char *pname=argv[0];

    pnm_init(&argc, argv);

    memset(&job,0,sizeof(job));
    job.format = eA4Paper;
    job.center=0;
    job.xoffs=0;
    job.yoffs=0;
    job.duplex=-1;
    job.copies=-1;
    job.mediaSource=-1;
    job.mediaDestination=-1;

    while(argc > 1) {
        if(argv[1][0] == '-') {
            if(!strcmp(argv[1],"-dpi")&&(argc>2)) {
                if(sscanf(argv[2],"%d",&job.dpi)!=1) {
                    usage(pname);
                    exit(8);
                }
                argc-=2;argv+=2;
            } else if(!strcmp(argv[1],"-xoffs")&&(argc>2)) {
                if(sscanf(argv[2],"%f",&job.xoffs)!=1) {
                    usage(pname);
                    exit(8);
                }
                argc-=2;argv+=2;
            } else if(!strcmp(argv[1],"-yoffs")&&(argc>2)) {
                if(sscanf(argv[2],"%f",&job.yoffs)!=1) {
                    usage(pname);
                    exit(8);
                }
                argc-=2;argv+=2;
            } else if(!strcmp(argv[1],"-copies")&&(argc>2)) {
                if(sscanf(argv[2],"%d",&job.copies)!=1) {
                    usage(pname);
                    exit(8);
                }
                argc-=2;argv+=2;
            } else if(!strcmp(argv[1],"-outtray")&&(argc>2)) {
                if(sscanf(argv[2],"%d",&job.mediaDestination)!=1) {
                    usage(pname);
                    exit(8);
                }
                argc-=2;argv+=2;
            } else if(!strcmp(argv[1],"-feeder")&&(argc>2)) {
                if(sscanf(argv[2],"%d",&job.mediaSource)!=1) {
                    usage(pname);
                    exit(8);
                }
                argc-=2;argv+=2;
            } else if(!strcmp(argv[1],"-format")&&(argc>2)) {
                char *name;
                for(i=0;(name=xlPaperFormats[i].name);i++) {
                    if(!strcmp(argv[2],name) ) {
                        job.format = xlPaperFormats[i].xl_nr;
                        break;
                    }
                }
                if(!name) {
                    XY_Puts(2,"\nPaperformats: \n");
                    for(i=0;(name=xlPaperFormats[i].name);i++) {
                        if(xlPaperFormats[i].width >0) {
                            XY_Puts(2,name);
                            XY_Puts(2," ");
                        }
                    }
                    XY_Puts(2,"\n");
                    exit(8);
                }
                argc-=2;argv+=2;
            } else if(!strcmp(argv[1],"-o")&&(argc>2)) {
                out_fd = open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0666);
                if(out_fd<0) {
                    perror("can't open outputfile");
                    exit(1);
                }
                argc-=2;argv+=2;
            } else if(!strcmp(argv[1],"-duplex")&&(argc>2)) {
                if(!strncmp(argv[2],"vertical",1)) {
                    job.duplex=eDuplexVerticalBinding;
                } else if (!strncmp(argv[2],"horizontal",1)) {
                    job.duplex=eDuplexHorizontalBinding;
                } else {
                    usage(pname);
                    exit(67);
                }
                argc-=2;argv+=2;
            } else if(!strcmp(argv[1],"-colorok")) {
				job.colorok=1;
                argc-=1;argv+=1;
            } else if(!strcmp(argv[1],"-center")) {
                job.center=1;
                argc-=1;argv+=1;
            } else {
                usage(pname);
                exit(7);
            }
        } else   {
            break;
        }
        
    }
    if(job.dpi==0)
        job.dpi=300;

    while(argc > 1) {
        InputSource *source;
        FILE * ifP;

        ifP = pm_openr(argv[1]);

        source=(InputSource*)malloc(sizeof(InputSource));
        source->next=NULL;
        source->in_file=NULL;   /* not open */
        source->name=argv[1];
        if(!job.source) {
            job.source=source;  
            last=source;
        } else {
            last->next=source;
            last=source;
        }
        pm_close(ifP);
        argv++;argc--;
    } 
    if(!job.source) {
        job.source=(InputSource*)malloc(sizeof(InputSource));
        job.source->in_file=stdin;
    }
    job_head(out_fd);
    start_session(out_fd,&job);
    print_pages(out_fd,&job);
    end_session(out_fd);
    job_end(out_fd);
    exit(0);
}
