#include "gdevprn.h"
This file is part of GNU Ghostscript.
GNU Ghostscript is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
anyone for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing. Refer to
the GNU General Public License for full details.
Everyone is granted permission to copy, modify and redistribute GNU
Ghostscript, but only under the conditions described in the GNU General
Public License. A copy of this license is supposed to have been given to
you along with GNU Ghostscript so you can know your rights and
responsibilities. It should be in a file named COPYING. Among other
things, the copyright notice and this notice must be preserved on all
copies.
Aladdin Enterprises is not affiliated with the Free Software Foundation or
the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
does not depend on any other GNU software.
*/
* $Id: gdevlx7.c,v 1.5 2002/08/03 03:08:26 tillkamppeter Exp $
* Lexmark 7000 ink-jet "GDI" printer driver
* * just started...
*
* Henryk Paluch paluch@bimbo.fjfi.cvut.cz
* http://bimbo.fjfi.cvut.cz/~paluch/l7kdriver/
*
* BIG thanks to Peter B. West that discovered and described many
* "funny" things of the Lexmark 5xxx protocol.
*
* THIS GHOSTSCRIPT DRIVER USES MANY THINGS FROM
* STEPHEN'S LEXMARK 5700 GHOSCRIPT DRIVER:
*
* Stephen Taylor setaylor@ma.ultranet.com staylor@cs.wpi.edu
* http://www.ultranet.com/~setaylor/papers.htm
*
* ALL INTRODUCED BUGS ARE PROPERTY OF HENRYK :-)
*/
#define LX7_BSW_H 208
#define LX7_CSW_H 192
* 13 - compression bits, 2 = data words
*/
#define LX7_MAX_SWBYTES 26
#define LX7_MAX_PACKET 28
#define LX7_MAX_SWWORDS 13
#define LXH_SKIP1 99
#define LXH_SKIP2 109
#define LXH_DSKIP1 (LXH_SKIP1*2-1)
#define LXH_DSKIP2 (LXH_SKIP2*2+1)
#define LX7_INIT1 0x1b,0x2a,0x6d,0x00,0x40,0x10,0x03,0x10,0x11
#define LX7_INIT2 0xa5,0x00,0x06,0x50,0x03,0x03,0xc0,0x0f,0x0f, \
0xa5,0x00,0x03,0x50,0x04,0x05, \
0xa5,0x00,0x03,0x50,0x04,0x06, \
0xa5,0x00,0x03,0x50,0x04,0x07, \
0xa5,0x00,0x03,0x50,0x04,0x08, \
0xa5,0x00,0x04,0x50,0x04,0x0c,0x00, \
0xa5,0x00,0x04,0x50,0xe0,0x0b,0x03
* in other modes
*/
#define LX7_INIT3 0xa5,0x00,0x0b,0x50,0xe0,0x41,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x02,\
0xa5,0x00,0x06,0x50,0x05,0x00,0x00,0x80,0x00, \
0xa5,0x00,0x04,0x50,0x04,0x0c,0x00
#define LX7_INIT4 0x1b,0x2a,0x07,0x73,0x30
#define LX7_INIT5 0x1b,0x2a,0x07,0x63
#define LX7_INIT6 0x1b,0x2a,0x6d,0x00,0x42,0x00,0x00
#define LX7_INIT7 0xa5,0x00,0x05,0x40,0xe0,0x80,0x07,0x08
static byte lx7_fullinit[]={LX7_INIT1, LX7_INIT4,
LX7_INIT5, LX7_INIT6};
static byte lx7_pageinit[]={LX7_INIT4, LX7_INIT4, LX7_INIT5, LX7_INIT6};
#ifndef MIN
#define MIN(x,y) ( (x) < (y) ? (x) : (y))
#endif
#ifndef MAX
#define MAX(x,y) ( (x) > (y) ? (x) : (y))
#endif
static dev_proc_print_page(lxmgen_print_page);
static dev_proc_get_params(lxm_get_params);
static dev_proc_put_params(lxm_put_params);
static void
lxm7000m_initialize_device_procs(gx_device *dev)
{
gdev_prn_initialize_device_procs_mono(dev);
set_dev_proc(dev, get_params, lxm_get_params);
set_dev_proc(dev, put_params, lxm_put_params);
}
typedef struct lxm_device_s {
gx_device_common;
gx_prn_device_common;
int headSeparation;
byte *fullInit;
int nfullInit;
byte *pageInit;
int npageInit;
int printertype;
} lxm_device;
* stored in 'printertype' variable
*/
#define LXT_7000 0
#define LXT_5700 1
#define LXT_3200 2
#define LXT_2050 3
#define LXR_300 0
#define LXR_600 1
#define LXR_1200 2
lxm_device far_data gs_lex7000_device = {
prn_device_std_body(lxm_device, lxm7000m_initialize_device_procs, "lex7000",
DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
* may be overriden by -sPAPERSIZE=a4 of -sPAPERSIZE=letter
*/
600, 600,
0.0, 0.1, 0.3, 0.1,
* whole top and bottom of paper :-)
*/
1, lxmgen_print_page),
16,
lx7_fullinit,
sizeof(lx7_fullinit),
lx7_pageinit,
sizeof(lx7_pageinit),
LXT_7000
};
lxm_device far_data gs_lex5700_device = {
prn_device_std_body(lxm_device, lxm7000m_initialize_device_procs, "lex5700",
DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
* may be overriden by -sPAPERSIZE=a4 of -sPAPERSIZE=letter
*/
600, 600,
0.1, 0.1, 0.1, 0.0,
* whole top and bottom of paper :-)
*/
1, lxmgen_print_page),
16,
lx7_fullinit,
sizeof(lx7_fullinit),
lx7_pageinit,
sizeof(lx7_pageinit),
LXT_5700
};
lxm_device far_data gs_lex3200_device = {
prn_device_std_body(lxm_device, lxm7000m_initialize_device_procs, "lex3200",
DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
* may be overriden by -sPAPERSIZE=a4 of -sPAPERSIZE=letter
*/
600, 600,
0.1, 0.1, 0.1, 0.0,
* whole top and bottom of paper :-)
*/
1, lxmgen_print_page),
16,
lx7_fullinit,
sizeof(lx7_fullinit),
lx7_pageinit,
sizeof(lx7_pageinit),
LXT_3200
};
lxm_device far_data gs_lex2050_device = {
prn_device_std_body(lxm_device, lxm7000m_initialize_device_procs, "lex2050",
DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
* may be overriden by -sPAPERSIZE=a4 of -sPAPERSIZE=letter
*/
600, 600,
0.1, 0.1, 0.1, 0.0,
* whole top and bottom of paper :-)
*/
1, lxmgen_print_page),
16,
lx7_fullinit,
sizeof(lx7_fullinit),
lx7_pageinit,
sizeof(lx7_pageinit),
LXT_2050
};
#define get_lx_type(pdev) \
(((lxm_device*)pdev)->printertype)
#define LX_LINE_EMPTY(buf,len) \
(buf[0]==0 && memcmp(buf,buf+1,len-1)==0)
static byte ofs8[8]={128,64,32,16,8,4,2,1};
static void lex_eject(gp_file *out)
{
byte buf[4]={0x1b,0x2a,0x7,0x65};
#ifdef DEBUG
dprintf("Sending page eject.\n");
#endif
gp_fwrite(buf,sizeof(buf),1,out);
}
* shift paper (skip empty lines)
* offset is in pixels in 1200 dpi resolution, i. e.,
* 384 for 192 pixels or
* 416 for 208 pixels etc...
*/
static void paper_shift(gp_file *out,int offset)
{
byte buf[5]={0x1b,0x2a,0x3,0x0,0x0};
buf[3]=(byte)(offset >> 8);
buf[4]=(byte)(offset & 0xFF);
#ifdef DEBUG
dprintf3("paper_shift() %d 1200dpi = %d 600dpi = %d 300dpi pixels\n",
offset,offset/2,offset/4);
#endif
gp_fwrite(buf,sizeof(buf),1,out);
}
static int leftmost_pixel(byte *buf, int bytelen)
{
int i;
byte *r=buf;
byte b;
if (LX_LINE_EMPTY(buf,bytelen))
return bytelen*8-1;
while( *r==0 && bytelen>0)
{
r++;
bytelen--;
}
b= *r;
for(i=0;i<8;i++)
if ( ( b & ofs8[i])!=0)
break;
return (r-buf)*8+i;
}
static int rightmost_pixel(byte *buf, int bytelen)
{
int i;
byte *r=buf+(bytelen-1);
byte b;
if (LX_LINE_EMPTY(buf,bytelen))
return 0;
while( *r==0 && bytelen>1)
{
r--;
bytelen--;
}
b= *r;
for(i=7;i>=0;i--)
if ( ( b & ofs8[i])!=0)
break;
return (r-buf)*8+i;
}
* buf - pixel buffer
* bytelen - width of line (in bytes)
* bufheight - buffer height (pixels)
* interlaced - if even lines have different horizontal offset
* than odd lines
* intershift - horizontal offset between even/odd lines
* leftmost,
* rightmost - output
*/
static void find_lr_pixels(byte *buf[],int bytelen,int bufheight,
int interlaced, int intershift,
int *leftmost, int *rightmost)
{
int maxright=bytelen*8-1;
int left=maxright;
int right=0;
int i;
for(i=0;i<bufheight;i++)
{
int ltmp,rtmp;
ltmp=leftmost_pixel(buf[i],bytelen);
rtmp=rightmost_pixel(buf[i],bytelen);
#if 0
dprintf3("buf[%d]: leftmost %d, rightmost %d\n",
i,ltmp,rtmp);
#endif
if (interlaced && (i & 1)==1)
{
ltmp=MAX((ltmp-intershift),0);
rtmp=MIN((rtmp+intershift),maxright);
if (ltmp==maxright)
ltmp--;
}
if (ltmp<left)
left=ltmp;
if (rtmp>right)
right=rtmp;
}
*leftmost = left;
*rightmost = right;
return;
}
enum {
LEFT_MARGIN=50,
VERTSIZE=LX7_BSW_H
};
*/
enum {
IDX_SEQLEN=5,
IDX_HORRES=8,
IDX_PACKETS=13,
IDX_5700DIF=12,
IDX_HSTART=15,
IDX_HEND=17,
IDX_DATA=26,
IDX_CARTRIDGE=10
};
#define DIV8(x) ( (x) >> 3 )
#define MOD8(x) ( (x) & 0x7 )
#define DIV16(x) ( (x) >> 4 )
#define MOD16(x) ( (x) & 0xf )
#define OUT_BUF_SIZE 256000
* see lexmarkprotocol.txt for more information */
* by lxmgen_print_page() to avoid large static array
* in gs binary
*/
static byte outb[]={0x1B,0x2A,0x04,0x00,0x00,0xFF,0xFF,
0x00,0x02,0x01,0x01,0x1A,0x11,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x22,0x33,0x44,0x55,0x01};
#define BITSTART12 4096
static int print_cols(gp_file *prn_stream,gx_device_printer *pdev,
byte *outbuf,
int left,int right,int vstart, int vend,byte *buf[],
int width,int LR_SHIFT)
{
unsigned char mask[8]={128,64,32,16,8,4,2,1};
unsigned int mask16[16]={0x8000,0x4000,0x2000,0x1000,
0x0800,0x0400,0x0200,0x0100,
0x0080,0x0040,0x0020,0x0010,
0x0008,0x0004,0x0002,0x0001};
int l8,r8,packets;
byte *p=outbuf+IDX_DATA;
int clen;
int i,k;
unsigned int vbuf[LX7_MAX_SWWORDS];
int vi;
unsigned char *vp,*vp2;
int smask,smask2;
l8=left+LEFT_MARGIN;
r8=right+LEFT_MARGIN;
packets=r8-l8+1;
outbuf[IDX_PACKETS] =(unsigned char)(packets >> 8);
outbuf[IDX_PACKETS+1]=(unsigned char)(packets & 0xFF);
outbuf[IDX_HSTART] =(unsigned char)(l8 >> 8);
outbuf[IDX_HSTART+1] =(unsigned char)(l8 & 0xFF);
outbuf[IDX_HEND] =(unsigned char)(r8 >> 8);
outbuf[IDX_HEND+1] =(unsigned char)(r8 & 0xFF);
outbuf[IDX_5700DIF]= ( get_lx_type(pdev)==LXT_7000 ? 0x11 : 0x1 );
for(i=left;i<=right;i++)
{
int rle1size,rle2size;
int bits=0;
byte *tbits;
if ( (p-outbuf)>=OUT_BUF_SIZE-28 )
{
return -1;
}
tbits=p;
p+=2;
memset(vbuf,0,sizeof(vbuf));
smask=mask[MOD8(i)];
smask2=mask[MOD8(i+LR_SHIFT)];
vi=vstart*2;
for(k=vstart;k<vend;k++)
{
vp=buf[k*2]+DIV8(i);
vp2=buf[k*2+1]+DIV8(i+LR_SHIFT);
if ( ( vp[0] & smask) != 0)
vbuf[DIV16(vi)] |= mask16[MOD16(vi)];
vi++;
if (i+LR_SHIFT<width*8)
{
if ( (vp2[0] & smask2) !=0 )
vbuf[DIV16(vi)] |=mask16[MOD16(vi)];
}
vi++;
}
* 1 = 8x left white, 8x right white
* 0 = 2 data bytes follow (8 bits left, 8 bits right)
*/
for(k=0;k<LX7_MAX_SWWORDS;k++)
{
unsigned int t=vbuf[k];
bits= (bits>>1);
if (t==0)
{
bits+=BITSTART12;
}
else
{
*(p++)=(t >> 8) & 0xFF;
*(p++)=(t & 0xFF);
}
}
tbits[1]=(unsigned char)( (bits) & 0xFF);
*tbits =(unsigned char)( ((bits>>8) & 0x1f) | 0x20 );
rle1size=(p-tbits);
#if 0
dprintf1("RLE1(%d), ",rle1size);
#endif
if (rle1size>6)
{
byte ob[LX7_MAX_PACKET];
unsigned int lastword=0x8FFF;
int byts=0;
byte *pp=ob+2;
memset(ob,0,LX7_MAX_PACKET);
for(k=0;k<LX7_MAX_SWWORDS;k++)
{
unsigned int t=vbuf[k];
byts= (byts>>1);
if (t==lastword)
{
byts+=BITSTART12;
}
else
{
*(pp++)=(t >> 8) & 0xFF;
*(pp++)=(t & 0xFF);
lastword=t;
}
}
ob[1]=(unsigned char)( (byts) & 0xFF);
ob[0]=(unsigned char)( (byts>>8) & 0x1f );
rle2size=(pp-ob);
#if 0
dprintf1("RLE2(%d), ",rle2size);
#endif
if (rle1size>rle2size)
{
#if 0
dprintf2("\n**RLE2 WIN** %d > %d \n",rle1size,rle2size);
dprintf("\nUsing RLE2 compression\n");
#endif
memcpy(tbits,ob,rle2size);
p=tbits+rle2size;
}
}
}
clen=p-outbuf;
outbuf[IDX_SEQLEN-1] =(unsigned char)(clen >> 16);
outbuf[IDX_SEQLEN] =(unsigned char)(clen >> 8);
outbuf[IDX_SEQLEN+1]=(unsigned char)(clen & 0xFF);
gp_fwrite(outbuf,1,clen,prn_stream);
#ifdef DEBUG
dprintf1("\nSent %d data bytes\n",clen);
#endif
return 0;
}
static int
lxmgen_print_page(gx_device_printer *pdev, gp_file *prn_stream)
{
int pheight=pdev->height;
#ifdef DEBUG
int pwidth=pdev->width;
#endif
int bwidth=gdev_mem_bytes_per_scan_line((gx_device*)pdev);
int prest=pheight;
int brest;
int skipline=208;
* about 208 pixels... */
byte *pbuf;
byte *ppbuf,*ppbuf2;
byte *outbuf;
* but allocated here, to avoid unnecessary
* de & allocation
*/
int pbufsize,rpbufsize;
int hres,vres;
int interlaced;
int lr_shift=((lxm_device*)pdev)->headSeparation;
byte *obp[LX7_BSW_H];
int bufHeight;
int code = 0;
vres=LXR_600;
if (pdev->y_pixels_per_inch<301.0)
vres=LXR_300;
if (pdev->y_pixels_per_inch>=601.0)
vres=LXR_1200;
bufHeight=LX7_BSW_H;
if (vres==LXR_300)
{
bufHeight /= 2;
skipline /= 2;
}
if (vres==LXR_1200)
{
bufHeight *= 2;
skipline *=2;
}
pbufsize = bwidth * bufHeight;
rpbufsize = bwidth * (bufHeight+1);
#ifdef DEBUG
dprintf1("[%s] print_page() start\n",pdev->dname);
dprintf1("Is first page? %d\n",gdev_prn_file_is_new(pdev));
dprintf1("Head Separation %d\n",((lxm_device*)pdev)->headSeparation);
dprintf2("Width %d pixels, %d bytes\n",pwidth,bwidth);
dprintf1("Height %d pixels\n",pheight);
dprintf1("One pass buffer size %d\n",pbufsize);
dprintf1("Output buffer size %d\n",OUT_BUF_SIZE);
dprintf2("Current resolution is %f width x %f height dpi\n",
pdev->x_pixels_per_inch, pdev->y_pixels_per_inch );
#endif
pbuf = (byte *)gs_malloc(pdev->memory->non_gc_memory, rpbufsize, 1, "lxmgen_print_page(pbuf)");
if (pbuf == NULL)
return_error(gs_error_VMerror);
outbuf = (byte *)gs_malloc(pdev->memory->non_gc_memory, OUT_BUF_SIZE, 1, "lxmgen_print_page(outbuf)");
if (outbuf == NULL)
{
gs_free(pdev->memory->non_gc_memory, (char*)pbuf,pbufsize, 1, "lxmgen_print_page(pbuf)");
return_error(gs_error_VMerror);
}
memcpy(outbuf,outb,sizeof(outb));
memset(pbuf+pbufsize,0,bwidth);
hres=LXR_600;
if (pdev->x_pixels_per_inch<301.0)
hres=LXR_300;
if (pdev->x_pixels_per_inch>=601.0)
hres=LXR_1200;
if (hres==LXR_300)
lr_shift >>=1;
if (hres==LXR_1200)
lr_shift <<=1;
interlaced=1;
if (get_lx_type(pdev)==LXT_7000 || get_lx_type(pdev)==LXT_5700)
{
byte mspeed[3]={1,2,5};
outbuf[IDX_HORRES]=mspeed[hres];
}
#ifdef DEBUG
dprintf1("Choosed motor speed %d\n",(int)outbuf[IDX_HORRES]);
#endif
if(vres==LXR_600)
{
int i;
for(i=0;i<LX7_BSW_H;i++)
obp[i]=pbuf+(i*bwidth);
}
if(vres==LXR_300)
{
int i;
for(i=0;i<LX7_BSW_H;i++)
if ( (i & 1)!=0)
obp[i]=pbuf+pbufsize;
else
obp[i]=pbuf+(i/2*bwidth);
}
if(gdev_prn_file_is_new(pdev))
gp_fwrite(((lxm_device*)pdev)->fullInit,
((lxm_device*)pdev)->nfullInit,
1,prn_stream);
else
gp_fwrite(((lxm_device*)pdev)->pageInit,
((lxm_device*)pdev)->npageInit,
1,prn_stream);
while(prest>0)
{
int i;
int leftmost;
int rightmost;
int c1200;
code = gdev_prn_get_bits(pdev, pheight-prest,
pbuf,
&ppbuf);
* either our buffer or
* gs internal data buffer
*/
if (code < 0)
goto error;
if (vres==LXR_1200 && (pheight-prest+LXH_DSKIP1<pheight))
{
code = gdev_prn_get_bits(pdev, pheight-prest+LXH_DSKIP1,
pbuf+bwidth,
&ppbuf2);
c1200=LX_LINE_EMPTY(ppbuf2,bwidth);
if (code < 0)
goto error;
}
else
c1200=1;
if (LX_LINE_EMPTY(ppbuf,bwidth) && c1200)
{
prest--;
skipline++;
continue;
}
for(i=0;i<(vres==LXR_1200 ? 2 : 1);i++)
{
if (skipline>0)
{
int mult[3]={4,2,1};
paper_shift(prn_stream,skipline*mult[vres]);
skipline=0;
}
if (vres==LXR_1200)
{
int j;
for(j=0;j<LX7_BSW_H;j++)
if ( (j & 1)!=i)
obp[j]=pbuf+pbufsize;
else
obp[j]=pbuf+(j*2*bwidth);
}
brest=MIN(bufHeight,prest);
#ifdef DEBUG
dprintf2("Copying %d lines to buffer, vertpos %d\n",brest,
pheight-prest);
#endif
gdev_prn_copy_scan_lines(pdev,pheight-prest,pbuf,pbufsize);
if (bufHeight-brest>0)
memset(pbuf+(brest*bwidth),0,(bufHeight-brest)*bwidth);
find_lr_pixels(obp,bwidth,LX7_BSW_H,interlaced,
((lxm_device*)pdev)->headSeparation,
&leftmost,&rightmost);
#ifdef DEBUG
dprintf2("Leftmost pixel %d, rightmost pixel %d\n",
leftmost,rightmost);
#endif
if (leftmost<rightmost)
if (print_cols(prn_stream,pdev,outbuf,leftmost,rightmost,
0,VERTSIZE/2,obp,bwidth,lr_shift)==-1)
{
print_cols(prn_stream,pdev,outbuf,leftmost,rightmost,0,
VERTSIZE/4,obp,bwidth,lr_shift);
print_cols(prn_stream,pdev,outbuf,leftmost,rightmost,
VERTSIZE/4,VERTSIZE/2,
obp,bwidth,lr_shift);
#ifdef DEBUG
dprintf("Overflow workaround used\n");
#endif
}
if (vres!=LXR_1200)
{
skipline=brest;
}
else
{
skipline+=( i==0 ? LXH_DSKIP1 : LXH_DSKIP2 );
}
prest-=skipline;
if (prest<=0)
break;
}
}
lex_eject(prn_stream);
error:
gs_free(pdev->memory->non_gc_memory, (char*)pbuf,rpbufsize, 1, "lxmgen_print_page(pbuf)");
gs_free(pdev->memory->non_gc_memory, (char*)outbuf,OUT_BUF_SIZE, 1, "lxmgen_print_page(outbuf)");
#ifdef DEBUG
dprintf1("[%s] print_page() end\n",pdev->dname);
#endif
return code;
}
static int
lxm_get_params(gx_device *pdev, gs_param_list *plist)
{
lxm_device* const ldev = (lxm_device*)pdev;
int code = gdev_prn_get_params(pdev, plist);
if ( code < 0 ) return code;
code = param_write_int(plist,
"HeadSeparation",
(int *)&(ldev->headSeparation));
return code;
}
static int
lxm_put_params(gx_device *pdev, gs_param_list *plist)
{
int ecode;
lxm_device* const ldev = (lxm_device*)pdev;
int trialHeadSeparation=ldev->headSeparation;
int code = param_read_int(plist, "HeadSeparation", &trialHeadSeparation);
if ( trialHeadSeparation < 1 || trialHeadSeparation > 32 )
param_signal_error(plist, "HeadSeparation", gs_error_rangecheck);
ecode = gdev_prn_put_params(pdev, plist);
if ( code < 0 ) return code;
if (ecode < 0) return ecode;
ldev->headSeparation = trialHeadSeparation;
if ( code == 1) return ecode;
return 0;
}