Sophie

Sophie

distrib > Mandriva > 10.0 > i586 > media > updates > by-pkgid > 9f76d24be37ca2d6b601b0e9b5bc1cc7 > files > 245

printer-filters-1.0-138.2.100mdk.i586.rpm

X-UIDL: 8ebb0e68de80cba84ab6fd8d7d563809
X-Mozilla-Status: 0003
X-Mozilla-Status2: 00000000
X-Apparently-To: hintak_leung@yahoo.co.uk via 66.218.78.109; 02 Jan 2003 14:25:32 -0800 (PST)
Return-Path: <r.ragusa@libero.it>
Received: from 193.70.192.51  (EHLO smtp1.libero.it) (193.70.192.51)
  by mta421.mail.yahoo.com with SMTP; 02 Jan 2003 14:25:31 -0800 (PST)
Received: from linux86.end (151.28.159.88) by smtp1.libero.it (6.7.015)
        id 3E0C7171001ECD47; Thu, 2 Jan 2003 23:25:25 +0100
Received: from linux86.end (linux86.end [10.0.0.86])
	by linux86.end (Postfix) with SMTP
	id 989F82ACB4; Thu,  2 Jan 2003 23:12:43 +0100 (CET)
Date: Thu, 2 Jan 2003 23:12:43 +0100
From: Roberto Ragusa <r.ragusa@libero.it>
To: Robert L Krawitz <rlk@alum.mit.edu>
Cc: Hin-Tak Leung <hintak_leung@yahoo.co.uk>,
	Kyle Gordon <kyle@lodge.glasgownet.com>
Subject: epl5x00.c: here it is
Message-Id: <20030102231243.656b8f66.r.ragusa@libero.it>
X-Mailer: Sylpheed version 0.8.5 (GTK+ 1.2.10; i386-redhat-linux)
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit

Hi,

as I promised, here is the code I used to make a first print
on my EPL-5900L.
There are many macros, but I think I'll convert them to (inlined)
functions as Robert Krawitz suggested me.
This code should be fragmented in two or three files, separating the
stripe compression, the header/footer stuff and an interface
part (file I/O, options). This way the interface part can be
adapted to bpm/gs/ijs/gimp-print without touching the rest.

I also like the fact that the protocol description is just
embedded into the code as comments.

Feel free to play with this code and tell me how we should go on
(maybe using sourceforge).

If you want to change something, please let me know. I'd prefer
not to have several versions of the code around, but integrate
all the ideas.

BTW, we now know for sure that EPL-5700L and EPL-5900L have
different data formats, even if the stripe structure is identical.
EPL-5800L owner wanted.

Have fun.

----------cut here----------
/*
 * Copyright 2003 Roberto Ragusa <r.ragusa@libero.it>
 *
 * This program converts a 600dpi bitmap representation of an A4 sized
 * page (4768x6796 pbm file with *exactly* 13 bytes of header) into a
 * binary stream of data.
 * If this data is sent to a Epson EPL-5700L, EPL-5800L or EPL-5900L
 * laser printer the original image will be printed.
 *
 * This code is currently highly experimental and tested only on an
 * EPL-5900L.
 * All the parameters (dpi, paper size are hard coded into the
 * binary header).
 * 
 * The purpose of this program is to build the fundamentals of an
 * Epson EPL5x00L printer driver.
 *
 * History:
 *
 *    2003-01-01 Roberto Ragusa <r.ragusa@libero.it>
 *
 *             * First version, the code is not commented and the
 *             protocol is not described at all.
 *             Preliminar tests successful!
 *             Running speed and compression ratio are very good.
 *
 *    2003-01-02 Roberto Ragusa <r.ragusa@libero.it>
 *
 *             * Added copy-2 and copy-3 commands.
 *             * Little fixes and polishing.
 *             * Added huge comment text describing protocol.
 *             The program is now understandable by humans.
 *
 *
 * ToDo:
 *             * Dimensions of bitmap are not perfect. 6796 isn't
 *             even divisible by 64, we're faking a few lines
 *	       on the bottom of the page.
 *             * We don't even check if file I/O is failing. Wow.
 *
 */

/*
 * --- Explanation of how it works (the though part) ---
 * 
 * The Epson EPL-5700L, EPL-5800L and EPL-5900L printers are a low cost
 * version of the equivalent "not-L" printers. The difference is that L
 * versions have less memory and CPU power on-board and some of the
 * firmware functionalities are moved to the host sending the file to
 * print. There is no Postscript support and no support for widely known
 * bitmap printing protocols such as PCL or ESC/P2.
 * The printers want the bitmap in a specific binary format and not in
 * a simple raw format. The format is designed to give a good
 * compression of typical images so to have a smaller data file going
 * from host to printer.
 * 
 * No documentation on the format is publicly available, so a lot of
 * guess work had to be done to understand the meaning of spool files
 * generated by official drivers (that is, closed-source drivers).
 * 
 * There are some job-header, page-header and page-footer byte sequences
 * which will not be discussed here. They contain information about
 * papersize, resolution and other options. Some documentation about
 * the headers can be found rather easily elsewhere.
 * The bitmap is divided in horizontal stripes, each composed by 64
 * rows.
 * Every stripe is encoded independently from the others.
 * The stripe description starts with a stripe header with information
 * about the length of the following binary data.
 * 
 * Now, the actual thing: how the stripe data is encoded.
 * The binary data is a set of 16bit words. The bit stream starts with
 * the less significant bit and after the most significant bit we pass
 * to the next 16bit word, again LSB first.
 * Example:
 * a binary data like this (letters are just marks to help you tracking
 * the bits)
 *  01110001 10101111  00001000 01001110 ...
 *  a      b c     de  f      g h      i
 * 
 * has 2 16 bit words before the dots.
 * The first word is to be considered first. But not starting from the
 * MSB, instead from LSB.
 * So the real bit stream should be interpreted as
 *  11110101100011100111001000010000...
 *  ed     cb      ai      hg      f
 * 
 * Looking at things this way is not very comfortable because pixel
 * patterns and binary counters encoding needs to be bit-reversed (that
 * is, a 8bit value of 128 is not 1000000 but 00000001).
 * 
 * So, we face the situation differently. We imagine the bit stream grows
 * in the left direction (not right). So the data is for us
 *   ... 00001000 01001110 01110001 10101111
 *       f      g h      i a      b c     de
 * Note that the dots are now on the right and if we read from right
 * to left we have a correct bit order (we encounter marks "edcbaihgf").
 * 
 * We start with an empty 32bit temp variable and populate the LSB bit
 * first and than the others. When we have more than 16 bits ready
 * we copy bit 15-8 and 7-0 (LSB is bit 0) in the first two bytes of the
 * output buffer and shift the temp word 16 bit right, ready to populate
 * other bits.
 * 
 * There is a last thing to be careful about. Bits are not inserted
 * into the temp word one by one, but on groups of variable length.
 * These groups are represented by numeric constants just ready for
 * insertion and so are to be interpreted from right to left.
 * 
 * The bit stream above could have been generated by an insertion
 * of 101111 and then 0110 and then 011100 and then 10 and then 0011 and
 * so on.
 * So if I say we emit a 001 code followed by a 101 code I'm
 * speaking of this:
 *    ...101001
 * and if I say we emit a 111 code followed by the 8 bit representation
 * of the number 64 I'm speaking of this
 *    ...01000000111
 * and if I say we emit a 110 code followed by a bitmap bbwwbbbb
 * (b=black bit=1, w=white bit=0) I'm speaking of this
 *    ...00110000110
 * Note that we can simply paste the 64 and the bitmap in the stream
 * this way.
 * 
 * The above description is fundamental to quickly understand the
 * following.
 *  
 * We are going to encode the stripe.
 * we assume we have the bitmap data in the simple bit by bit
 * left-to-right order, so 3 white pixels followed by 6 black, 2 white
 * and 5 black is
 *   00011111 10011111 (0x1f,0x9f)
 * Note that I delimited byte boundaries.
 * This is the format commonly used in PBM format files.
 * 
 * The stripe is formed by 64 rows.
 * The description of one row can use data present at the row just above
 * the current one. If we are in the first row (row 0) we *can* use
 * an hypothetical row -1 which is assumed completely white.
 * 
 * Now the description of a row. It is all byte based.
 * There are two types of possible commands: literal and copy.
 * A literal command is followed by the bitmap data, while a
 * copy command tells that we can copy one of the bytes already sent
 * (the specific copy command tells where it is, as there are
 * more than one copy command).
 * After a copy command there is *always* a repetition command
 * which indicates how many times the copy has to be done.
 * Literal commands are *not* repeatable.
 * 
 * Here is the full table:
 * 
 *   LITERAL
 * 
 *     10 xxxxxxxx     The bitmap data is the byte xxxxxxxx
 *     00 xxxx         (this is not completely understood and is not used)
 * 
 *   COPY
 * 
 *     01              Copy the byte from the row above
 *     011             Copy the byte from our left (last byte sent)
 *     0111            Copy the second byte from our left (current-2)
 *     1111            Copy the third byte from our left (current-3)
 * 
 *     REPETITIONS (only after copy)
 *       0             Copy only one time (no repetition)
 *       01            Copy 2 times
 *       0011          Copy 3 times
 *       1011          Copy 4 times
 *       01111         Copy 5 times
 *       011111        Copy 6 times
 *       111111        Copy 7 times
 *       0111 yyyyyyy  Copy more than 7 times. yyyyyyy is a 7 bit
 *                       value containing the number of repetitions.
 * 		         This number can be:
 * 		         - 0       special case: copy until end of row
 * 		         - 1..7    probably accepted, but why not use
 * 		                     the short form?
 * 		         - 8..126  repeat said times
 * 		         - 127     repeat 127 times, and immediately
 * 		                     read another 7 bit yyyyyyy value
 * 				     again. This way long repetitions
 * 				     can be specified 127 by 127 and
 * 				     finally the rest.
 * 
 * That's all, folks.
 * 
 * Note that 0111 yyyyyyy means emit 0111 and then yyyyyyy so the
 * bit stream is
 *   ...yyyyyyy0111
 * as explained above.
 * 
 * When a full stripe has been encoded a certain number of 0 bits
 * is emitted to complete the last 16bit word.
 * 
 * EXAMPLES:
 * 
 * Suppose we want to encode a white row and assume the row is
 * 260 byte long (260*8 pixels).
 * 
 * We can do:
 *   10 00000000 10 00000000 10 00000000 10 00000000 ... 260 times
 * that is
 *   literal 0x00 260 times (awful!)
 * 
 * Or we can do:
 *   10 00000000 011 0111 1111111 1111111 0000101
 * that is
 *   literal 0x00, copy last byte repeating copy 127+127+5 times
 *   
 * Or just:
 *   10 00000000 011 0111 0000000
 * that is
 *   literal 0x00, copy last byte until the end of the row
 *   
 * Or just (if this is the first row of the stripe or the row
 * above is completely white):
 *   01 0111 0000000
 * that is
 *   copy from above repeating until row end.
 * 
 * For a totally black row (without using the row above):
 *   10 11111111 011 0111 0000000
 * that is
 *   literal 0xff, copy last byte until row end
 * 
 * If we have a totally white stripe we can do:
 *   01 0111 0000000 ...        64 times
 * that is
 *   64 commands "copy from above until row end"
 * 
 * A more complex example.
 * We want to code a white stripe with a black box inside;
 * 2 white rows above the box and 10 below the box (which
 * is 52 rows tall); 15 white pixels on the left of the
 * box (which is 6 pixels wide) and white on the right
 * of it.
 * The bitmap in hexadecimal is:
 *   0x00 0x00 0x00 0x00 0x00 0x00 0x00 ...
 *   0x00 0x00 0x00 0x00 0x00 0x00 0x00 ...
 *   0x00 0x01 0xf8 0x00 0x00 0x00 0x00 ... 52 times
 *   0x00 0x00 0x00 0x00 0x00 0x00 0x00 ... 10 times
 * A good coding is:
 *   01 0111 0000000
 *   01 0111 0000000
 *   01 0 10 00000001 10 11111000 1111 0 01 0111 0000000
 *   01 0111 0000000         51 times
 *   01 0 011 0111 0000000
 *   01 0111 0000000         9 times
 * that is
 *   copy above (white) row
 *   copy above row
 *   copy 1 byte from above (don't repeat), literal 0x1,
 *      literal 0xf8, copy from current-3 (don't repeat),
 *      copy from above until row end
 *   copy above row      (51 times)
 *   copy 1 byte from above (don't repeat), copy last
 *      until row end
 *   copy above row      (9 times)
 * Note that the current-3 copy is a waste because without
 * it we should have copied from above the white byte
 * we needed.
 * A final detail.
 * When a copy is repeating both the source and destination
 * pointers move forward. So a
 *   01 0111 0000000
 * at the beginning of a row copies all the row above,
 * it doesn't fill the row with a repetition of the first
 * byte of the row above.
 * If we had used "copy current-3 repeating until end"
 * in the last example we wouldn't have had a white
 * sequence but a repeated pattern of 0x00 0x01 0xf8.
 * 
 *   Roberto Ragusa
 * 
 */
#include <stdio.h>

int main(void){
  int wpix,hpix;
  int wbytes,hstripes;

  wpix=4768;
  hpix=6796;

  wbytes=wpix/8;
  hstripes=(hpix+63)/64; /* +63 is a ugly hack, we don't know what we're printing */

  unsigned char inbuffer[wbytes*64];
  unsigned char whiterow[wbytes];
  unsigned char *thisrow, *rowabove;
  int i;
  int x,y,s;
  int slen;

/* STREAM HANDLING */

  unsigned char stream_outbuffer[wbytes*64+(wbytes*64/2)];/* allow 50% expansion for safety */
  unsigned char *stream_ptr;
  int stream_temp,stream_bits;
    
#define stream_begin() do{\
    stream_ptr=&stream_outbuffer[0];\
    stream_temp=0;\
    stream_bits=0;\
  } while(0)

#define stream_emit(d,l) do{\
    stream_emit_nocheck((d),(l));\
    stream_check();\
  } while(0)

#define stream_emit_nocheck(d,l) do{\
    stream_temp|=(d)<<stream_bits;\
    stream_bits+=(l);\
  } while(0)

#define stream_check() do{\
    if(stream_bits>=16){\
      *(stream_ptr++)=(stream_temp>>8)&0xff;\
      *(stream_ptr++)=stream_temp&0xff;\
      stream_temp>>=16;\
      stream_bits-=16;\
    }\
  } while(0)

#define stream_end() do{\
    stream_emit(0,(16-stream_bits)%16);\
  } while(0)


/* RUN LENGTH */

  int run_length;
  int run_length_table_d[]={/**/0,/*0*/0,/*01*/1,/*0011*/3,/*1011*/0xb,/*01111*/0xf,/*011111*/0x1f,/*111111*/0x3f};
  int run_length_table_l[]={    0,     1,      2,        4,          4,           5,             6,             6};
  int run_length_returned;

#define run_length_find(b1,b2,maxl) do{\
    for(run_length=1;run_length<(maxl)&&(b1)[run_length]==(b2)[run_length];run_length++);\
    run_length_returned=run_length;\
    if(run_length==(maxl)){\
      stream_emit(/*0000000 0111*/0x007,4+7);\
    }\
    else if(run_length>=8){\
      stream_emit(/*0111*/0x7,4);\
      while(run_length>=127){\
        stream_emit(127,7);\
        run_length-=127;\
      }\
      stream_emit(run_length,7);\
    }\
    else{\
      stream_emit(run_length_table_d[run_length],run_length_table_l[run_length]);\
    }\
  } while(0)


/* MISC DATA */

unsigned char header_data[]={
  0x1b,0x01,0x40,0x45,0x4a,0x4c,0x20,0x0a, 0x40,0x45,0x4a,0x4c,0x20,0x53,0x54,0x41,  /* |..@EJL .@EJL STA| */
  0x52,0x54,0x4a,0x4f,0x42,0x20,0x4d,0x41, 0x43,0x48,0x49,0x4e,0x45,0x3d,0x22,0x65,  /* |RTJOB MACHINE="e| */
  0x78,0x70,0x65,0x72,0x69,0x6d,0x65,0x6e, 0x74,0x22,0x20,0x55,0x53,0x45,0x52,0x3d,  /* |xperiment" USER=| */
  0x22,0x65,0x70,0x6c,0x35,0x78,0x30,0x30, 0x6c,0x5f,0x64,0x72,0x69,0x76,0x65,0x72,  /* |"epl5x00l_driver| */
  0x22,0x0a,0x40,0x45,0x4a,0x4c,0x20,0x45, 0x4e,0x20,0x4c,0x41,0x3d,0x45,0x53,0x43,  /* |".@EJL EN LA=ESC| */
  0x2f,0x50,0x41,0x47,0x45,0x0a,0x1d,0x34, 0x65,0x70,0x73,0x7b,0x49,0x00,0x00,0x00,  /* |/PAGE..4eps{I...| */
  0x00,0x1d,0x39,0x65,0x70,0x73,0x7b,0x49, 0x02,0x00,0x01,0x00,0x01,0x00,0x00,0x03,  /* |..9eps{I........| */
  0x00,0x1d,0x32,0x36,0x65,0x70,0x73,0x7b, 0x49,0x04,0x00,0x0e,0x40,0x02,0x54,0x00,  /* |..26eps{I...@.T.| */
  0x00,0x00,0x00,0x1a,0x8c,0x12,0x84,0x00, 0x6b,0x00,0x00,0x01,0xff,0xfe,0x00,0x00,  /* |........k.......| */
  0x00,0x00,0x01                                                                     /* |...|              */
};
unsigned char footer_data[]={
  0x1d,0x32,0x65,0x70,0x73,0x7b,0x49,0x05, 0x00,0x1d,0x32,0x65,0x70,0x73,0x7b,0x49,  /* |.2eps{I...2eps{I| */
  0x03,0x00,0x1d,0x32,0x65,0x70,0x73,0x7b, 0x49,0x01,0x00,0x1b,0x01,0x40,0x45,0x4a,  /* |...2eps{I....@EJ| */
  0x4c,0x20,0x0a,0x40,0x45,0x4a,0x4c,0x20, 0x45,0x4a,0x20,0x0a,0x1b,0x01,0x40,0x45,  /* |L .@EJL EJ ...@E| */
  0x4a,0x4c,0x20,0x0a                                                                /* |JL .|             */
};

  for(i=0;i<wbytes;i++) whiterow[i]=0;

  fread(inbuffer,1,0xd,stdin);			/* skip 13 bytes (horror!)*/
  fwrite(header_data,1,sizeof(header_data),stdout);

  for(s=0;s<hstripes;s++){
    fread(inbuffer,1,wbytes*64,stdin);
    rowabove=&whiterow[0];			/* special case */
    thisrow=&inbuffer[0];
    stream_begin();
    for(y=0;y<64;y++){
      for(x=0;x<wbytes;){
        if(thisrow[x]==rowabove[x]){
          stream_emit(/*01*/0x1,2);
          run_length_find(&thisrow[x],&rowabove[x],wbytes-x);
          x+=run_length_returned;
        }
        else if(x-1>=0&&thisrow[x]==thisrow[x-1]){
          stream_emit(/*011*/0x3,3);
          run_length_find(&thisrow[x],&thisrow[x-1],wbytes-x);
          x+=run_length_returned;
  	}
        else if(x-2>=0&&thisrow[x]==thisrow[x-2]){
          stream_emit(/*0111*/0x7,4);
          run_length_find(&thisrow[x],&thisrow[x-2],wbytes-x);
          x+=run_length_returned;
  	}
        else if(x-3>=0&&thisrow[x]==thisrow[x-3]){
          stream_emit(/*1111*/0xf,4);
          run_length_find(&thisrow[x],&thisrow[x-3],wbytes-x);
          x+=run_length_returned;
  	}
        else if(0/*because_it_is_not_reliable*/){					/* currently not enabled */
          stream_emit(/*00*/0x0,2);
  	  //stream_emit(i_dont_know_exactly_what,4);
          x++;
  	}
        else{
          stream_emit(/*10*/0x2,2);
  	  stream_emit(thisrow[x],8);
          x++;
  	}
      }
      rowabove=thisrow;
      thisrow+=wbytes;
    }
    stream_end();
    slen=stream_ptr-stream_outbuffer;
    fprintf(stdout,"\x1d%deps{I%c%c%c%c%c%c%c",slen+7,6,0,1,(slen>>24)&0xff,(slen>>16)&0xff,(slen>>8)&0xff,slen&0xff);
    fwrite(stream_outbuffer,1,slen,stdout);
  }

  fwrite(footer_data,1,sizeof(footer_data),stdout);
  return 0;
}
----------cut here----------

-- 
   Roberto Ragusa    r.ragusa at libero.it