+/*
+ efaxlib.c - utility routines for efax
+ Copyright 1995 Ed Casas
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "efaxmsg.h"
+#include "efaxlib.h"
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+#define DEFXRES 204.145 /* fax x and y resolution in dpi */
+#define DEFYRES 195.58
+
+#define DEFWIDTH 1728 /* 215x297 mm image at fax resolution */
+#define DEFHEIGHT 2287
+
+extern t4tab wtab [ ( 64 + 27 + 13 ) + 1 ] ; /* T.4 coding tables */
+extern t4tab btab [ ( 64 + 27 + 13 ) + 1 ] ;
+
+short short256 = 256 ; /* for endian-ness detection */
+
+/* Make sure printf strings have only %d escapes and n or fewer
+ of them. Returns 0 if OK, 1 on error. */
+
+int ckfmt ( char *p, int n )
+{
+ for ( ; *p ; p++ ) {
+ if ( p[0] == '%' ) {
+ if ( p[1] == 'd' ) n-- ;
+ else if ( p[1] == '%' ) p++ ;
+ else n=-1 ;
+ }
+ }
+
+ return n < 0 ;
+}
+
+
+/* Initialize state of variable-length code word encoder. */
+
+void newENCODER ( ENCODER *e )
+{
+ e->x = 0 ;
+ e->shift = -8 ;
+}
+
+
+/* Store a code word `code' of length `bits' (<=24) in buffer pointed to by
+ `buf'. Bits that don't fit in complete bytes are saved between calls.
+ To flush the remaining bits call the function with code=0 and bits=0.
+ Returns pointer to next free element in output buffer. Calling function
+ must ensure at least bits/8 bytes are available in buffer. */
+
+uchar *putcode ( ENCODER *e, short code, short bits, uchar *buf )
+{
+ e->x = ( e->x << bits ) | code ;
+ e->shift += bits ? bits : -e->shift ;
+
+ while ( e->shift >= 0 ) {
+ *buf++ = e->x >> e->shift ;
+ e->shift -= 8 ;
+ }
+
+ return buf ;
+}
+
+
+/* Convert run lengths to 1-D T.4-codes. First run is white. Silently
+ truncates run lengths that are too long. After using this function EOLs
+ may need to be added and/or the putcode() buffer flushed. Returns
+ pointer to next free element in output buffer. */
+
+uchar *runtocode ( ENCODER *e, short *runs, int nr, uchar *codes )
+{
+ uchar col = 0, *maxcodes = codes + MAXCODES ;
+ t4tab *ctab = wtab, *p ;
+ short rlen ;
+ long x ;
+ short shift ;
+
+#define PUTCODE(p) { x = ( x << p->bits ) | p->code ; shift += p->bits ; \
+ while ( shift >= 0 ) { *codes++ = x >> shift ; shift -= 8 ; } }
+
+ x = e->x ; shift = e->shift ;
+
+ while ( nr-- > 0 ) {
+ rlen = *runs++ ;
+ if ( rlen > 63 ) { /* make-up code */
+ if ( rlen > MAXRUNLEN ) rlen = MAXRUNLEN ;
+ p = ctab + 63 + ( rlen >> 6 ) ;
+ if ( codes < maxcodes ) PUTCODE(p) ;
+ }
+ p = ctab + ( rlen & 0x3f ) ; /* terminating code */
+ if ( codes < maxcodes ) PUTCODE(p) ;
+ ctab = ( col ^= 1 ) ? btab : wtab ;
+ }
+
+ e->x = x ; e->shift = shift ;
+
+ return codes ;
+}
+
+
+/* Pad/truncate run-length coded scan line 'runs' of 'nr' runs by 'pad'
+ pixels (truncate if negative). Returns the new number of runs. */
+
+int xpad ( short *runs, int nr, int pad )
+{
+ if ( pad < 0 ) { /* truncate */
+ while ( pad < 0 ) pad += ( nr <= 0 ? -pad : runs [ --nr ] ) ;
+ runs [ nr++ ] = pad ;
+ } else { /* pad with white */
+ if ( nr & 1 ) runs [ nr - 1 ] += pad ;
+ else runs [ nr++ ] = pad ;
+ }
+ return nr ;
+}
+
+
+/* Shift a run-length coded scan line right by s pixels (left if negative).
+ If necessary, zero-length runs are created to avoid copying. Returns
+ the pixel width change (+/-). */
+
+int xshift ( short *runs, int nr, int s )
+{
+ int i=0, n=0 ;
+ if ( s < 0 ) {
+ for ( i = 0 ; s < 0 && i < nr ; i++ ) {
+ s += runs [ i ] ;
+ n -= runs [ i ] ;
+ runs [ i ] = 0 ;
+ }
+ i-- ;
+ }
+ if ( i < nr ) {
+ runs [ i ] += s ;
+ n += s ;
+ }
+ return n ;
+}
+
+
+/* Scale nr run lengths in buffer pointed to by p to scale image
+ horizontally. The scaling factor is xs/256. Returns new line width in
+ pixels. */
+
+int xscale ( short *p, int nr, int xs )
+{
+ int inlen=0, outlen=0 ;
+ for ( ; nr-- > 0 ; p++ ) {
+ inlen += *p ;
+ *p = ( ( inlen * xs + 128 ) >> 8 ) - outlen ;
+ outlen += *p ;
+ }
+ return outlen ;
+}
+
+
+/* Zero-terminated lists of run lengths for each byte. */
+
+uchar byteruns [ 1408 + 1 ] =
+ "8071061106205120511105210530413041210411110411204220421104310440"
+ "3140313103121103122031112031111103112103113032303221032111032120"
+ "3320331103410350215021410213110213202121202121110212210212302111"
+ "3021112102111111021111202112202112110211310211402240223102221102"
+ "2220221120221111022121022130233023210231110231202420241102510260"
+ "1160115101141101142011312011311101132101133011213011212101121111"
+ "0112112011222011221101123101124011114011113101111211011112201111"
+ "1120111111110111112101111130111230111221011121110111212011132011"
+ "1311011141011150125012410123110123201221201221110122210122301211"
+ "3012112101211111012111201212201212110121310121401340133101321101"
+ "3220131120131111013121013130143014210141110141201520151101610170"
+ "1701610151101520141201411101421014301313013121013111101311201322"
+ "0132110133101340121401213101212110121220121112012111110121121012"
+ "1130122301222101221110122120123201231101241012501115011141011131"
+ "1011132011121201112111011122101112301111130111112101111111101111"
+ "1120111122011112110111131011114011240112310112211011222011211201"
+ "1211110112121011213011330113210113111011312011420114110115101160"
+ "2602510241102420231202311102321023302213022121022111102211202222"
+ "0222110223102240211402113102112110211220211112021111110211121021"
+ "1130212302122102121110212120213202131102141021503503410331103320"
+ "3212032111032210323031130311210311111031112031220312110313103140"
+ "4404310421104220411204111104121041305305210511105120620611071080" ;
+
+/* Convert byte-aligned bit-mapped n-byte scan line into array of run
+ lengths. Run length array must have *more* than 8*n elements. First
+ run is white. Returns number of runs coded. */
+
+int bittorun ( uchar *bits, int n, short *runs )
+{
+ static uchar init=0, *rltab [ 256 ] ;
+ register uchar *p, c, lastc = 0x00 ;
+ short *runs0 = runs ;
+
+ if ( ! init ) { /* initialize pointer and run tables */
+ int i = 0 ;
+ for ( rltab[ 0 ] = p = byteruns ; *p ; p++ )
+ if ( ! ( *p -= '0' ) && i < 255 )
+ rltab [ ++i ] = p+1 ;
+ init = 1 ;
+ }
+
+ *runs = 0 ;
+ for ( ; n > 0 ; n-- ) {
+ p = rltab [ c = *bits++ ] ;
+ if ( ( lastc & 0x01 ) ? ! ( c & 0x80 ) : ( c & 0x80 ) )
+ *(++runs) = *p++ ; /* new run */
+ else
+ *runs += *p++ ; /* continue run */
+ while ( *p )
+ *(++runs) = *p++ ;
+ lastc = c ;
+ }
+
+ return runs - runs0 + 1 ;
+}
+
+
+/* Bitwise-OR two run-length coded scan lines. The run length
+ vectors a and b are OR-ed into c. If c is null, the result is
+ placed in a. The new image width is stored in pels if it is
+ not null. Returns the number of runs in the result. */
+
+int runor ( short *a, int na, short *b, int nb, short *c, int *pels )
+{
+ register short la, lb ;
+ int ia, ib, ic, np=0 ;
+ short tmp [ MAXRUNS ] ;
+
+ if ( ! c ) c = tmp ;
+
+ la = a [ ia = 0 ] ;
+ lb = b [ ib = 0 ] ;
+ c [ ic = 0 ] = 0 ;
+
+ while ( 1 ) {
+ if ( la <= lb ) { /* select shorter sub-run */
+ if ( ( ( ia | ib ) ^ ic ) & 1 ) /* OR of subruns same colour as c? */
+ c [ ++ic ] = la ; /* no, new output run */
+ else
+ c [ ic ] += la ; /* yes, add it */
+ lb -= la ; /* align subruns */
+ if ( ++ia >= na ) break ; /* done */
+ la = a [ ia ] ; /* get new subrun */
+ } else { /* same for line b ... */
+ if ( ( ( ia | ib ) ^ ic ) & 1 )
+ c [ ++ic ] = lb ;
+ else
+ c [ ic ] += lb ;
+ la -= lb ;
+ if ( ++ib >= nb ) break ;
+ lb = b [ ib ] ;
+ }
+ }
+
+ if ( ia < na )
+ while ( 1 ) {
+ if ( ( ia ^ ic ) & 1 )
+ c [ ++ic ] = la ;
+ else
+ c [ ic ] += la ;
+ if ( ++ia >= na ) break ;
+ la = a [ ia ] ;
+ }
+ else
+ while ( 1 ) {
+ if ( ( ib ^ ic ) & 1 )
+ c [ ++ic ] = lb ;
+ else
+ c [ ic ] += lb ;
+ if ( ++ib >= nb ) break ;
+ lb = b [ ib ] ;
+ }
+
+ if ( c == tmp ) for ( ia=0 ; ia <= ic ; ia++ ) np += a[ia] = c[ia] ;
+
+ if ( pels ) *pels = np ;
+
+ return ic + 1 ;
+}
+
+
+/* Get a number from a PBM file header while skipping whitespace
+ and comments. Returns the number or 0 on EOF. Reads one more
+ byte than used by the number. */
+
+int pbmdim ( IFILE *f )
+{
+ int c, n=0 ;
+
+ /* scan for first digit and skip comments */
+ while ( ! isdigit ( c = fgetc ( f->f ) ) && c >= 0 )
+ if ( c == '#' )
+ while ( ( c = fgetc ( f->f ) ) != '\n' && c >= 0 ) ;
+
+ /* get the number */
+ if ( c >= 0 && isdigit( c ) ) {
+ n = c - '0' ;
+ while ( isdigit ( c = fgetc ( f->f ) ) && c >= 0 )
+ n = n * 10 + c - '0' ;
+ }
+
+ return n ;
+}
+
+
+/* Append nb bits from in[from] to bit-mapped scan line buffer
+ where `from' is a bit (not byte) index. Bits in bytes are
+ ordered from MS to LS bit. Initialize before each scan line by
+ calling with nb=0 and in pointing to output buffer. Flush
+ after each scan line by calling with nb=0 and in=NULL. */
+
+#define putbits( c, b ) { x = ( x << (b) ) | (c) ; shift += (b) ; \
+ if ( shift >= 0 ) { *out++ = x >> shift ; shift -= 8 ; } }
+
+void copybits ( uchar *in, int from, short nb )
+{
+ uchar *f ;
+ short bits ;
+ static uchar *out ;
+ static short x, shift ;
+ static unsigned char right [ 9 ] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff } ;
+
+ if ( ! nb ) { /* reset for new scan line */
+ if ( in ) out = in ;
+ else putbits ( 0, -shift ) ; /* or flush bit buffer */
+ x = 0 ;
+ shift = -8 ;
+ } else {
+ f = in + ( from >> 3 ) ;
+ bits = 8 - ( from & 7 ) ;
+
+ if ( nb >= bits ) {
+ putbits ( *f++ & right [ bits ], bits ) ;
+ nb -= bits ;
+ } else {
+ putbits ( ( *f >> ( bits - nb ) ) & right [ bits ], nb ) ;
+ nb = 0 ;
+ }
+
+ while ( nb >= 8 ) { putbits ( *f++, 8 ) ; nb -= 8 ; }
+
+ if ( nb > 0 ) putbits ( *f >> ( 8 - nb ), nb );
+ }
+}
+
+
+/* Generate scan line 'line' of string 'txt' using font `font'
+ and store the runs in 'runs'. The font is scaled so it
+ appears to have cells of width w and height h. lmargin pixels
+ of white space are added at the left margin. Sets 'pels' to
+ line width if not null. Returns number of runs coded. */
+
+int texttorun ( uchar *txt, faxfont *font, short line,
+ int w, int h, int lmargin,
+ short *runs, int *ppels )
+{
+ uchar *in, out [ MAXLINELEN * MAXFONTW / 8 + 1 ] ;
+ int i, nc = 0, cw, nr, pels ;
+
+ line = ( line * font->h + h/2 ) / h ;
+
+ cw = font->w ;
+ if ( line >= font->h ) line = font->h - 1 ;
+ in = font->buf + 256/8 * cw * line ;
+
+ copybits ( out, 0, 0 ) ;
+ for ( i=0 ; txt[i] && i < MAXLINELEN ; i++ ) {
+ copybits ( in, font->offset [ txt[i] ], cw ) ;
+ nc++ ;
+ while ( ( txt[i] == HT ) && ( nc & 7 ) ) { /* tab */
+ copybits ( in, font->offset [ ' ' ], cw ) ;
+ nc++ ;
+ }
+ }
+ copybits ( 0, 0, 0 ) ;
+
+ nr = bittorun ( out, ( nc*cw + 7 )/8, runs ) ;
+
+ if ( font->w == w )
+ pels = nc*cw ;
+ else
+ pels = xscale ( runs, nr, ( w * 256 ) / font->w ) ;
+
+ pels += xshift ( runs, nr, lmargin ) ;
+
+ if ( ppels ) *ppels = pels ;
+
+ return nr ;
+}
+
+ /* Image File Input Functions */
+
+
+/* Names of file formats */
+
+char *iformatname [ NIFORMATS ] = IFORMATS ;
+char *oformatname [ NOFORMATS ] = OFORMATS ;
+char *pformatname [ NPFORMATS ] = PFORMATS ;
+
+/* Log the names of files still to be sent using the "msg()"
+ format string s. */
+
+void logifnames ( IFILE *f, char *s )
+{
+ PAGE *p ;
+ char *fn ;
+
+ if ( f && f->page )
+ for ( p = f->page ; p <= f->lastpage ; p++ ) {
+ fn = p->fname ;
+ if ( fn ) msg ( s, fn ) ;
+ }
+}
+
+
+/* Read run lengths for one scan line from T.4-coded IFILE f into buffer
+ runs. If pointer pels is not null it is used to save pixel count.
+ Returns number of runs stored, EOF on RTC, or -2 on EOF or other
+ error. */
+
+int readruns ( IFILE *f, short *runs, int *pels )
+{
+ int err=0, c=EOF, n ;
+ register int x ;
+ dtab *tab, *t ;
+ short shift ;
+ short *p, *maxp, *q, len=0, npad=0 ;
+ DECODER *d ;
+ uchar reverse=f->page->revbits ;
+
+ maxp = ( p = runs ) + MAXRUNS ;
+ d = &f->d ;
+
+ x = d->x ; shift = d->shift ; tab = d->tab ; /* restore decoder state */
+
+ do {
+ do {
+ while ( shift < 0 ) {
+ if ( ( c = fgetc ( f->f ) ) == EOF ) {
+ x = ( x << 15 ) | 1 ; shift += 15 ; /* EOL pad at EOF */
+ npad++ ;
+ } else {
+ if ( reverse ) c = normalbits [ c & 0xff ] ;
+ x = ( x << 8 ) | c ; shift += 8 ;
+ }
+ }
+ t = tab + ( ( x >> shift ) & 0x1ff ) ;
+ tab = t->next ;
+ shift -= t->bits ;
+ } while ( ! t->code ) ;
+ if ( p < maxp ) *p++ = t->code ;
+ } while ( t->code != -1 ) ;
+
+ d->x = x ; d->shift = shift ; d->tab = tab ; /* save state */
+
+ if ( npad > 1 ) msg ("W EOF before RTC" ) ;
+
+ if ( p >= maxp ) msg ( "W run length buffer overflow" ) ;
+
+ /* combine make-up and terminating codes and remove +1 offset
+ in run lengths */
+
+ n = p - runs - 1 ;
+ for ( p = q = runs ; n-- > 0 ; )
+ if ( *p > 64 && n-- > 0 ) {
+ len += *q++ = p[0] + p[1] - 2 ;
+ p+=2 ;
+ } else {
+ len += *q++ = *p++ - 1 ;
+ }
+ n = q - runs ;
+
+ /* check for RTC and errors */
+
+ if ( len )
+ d->eolcnt = 0 ;
+ else
+ if ( ++(d->eolcnt) >= RTCEOL ) err = EOF ;
+
+ if ( c == EOF ) {
+ if ( ferror ( f->f ) ) {
+ err = -msg ("ES2error reading fax file:") ;
+ } else {
+ err = -2 ;
+ }
+ }
+
+ if ( pels ) *pels = len ;
+
+ return err ? err : n ;
+}
+
+
+/* Read a PCX compressed bit-map */
+
+int readpcx ( char *p, int len, IFILE *f )
+{
+ int err=0, n, c ;
+
+ while ( !err && len > 0 ) {
+ if ( ( c = fgetc ( f->f ) ) < 0 ) {
+ err = msg ( "ES2 PCX read failed" ) ;
+ } else {
+ if ( ( c & 0xc0 ) == 0xc0 ) { /* a run count */
+ n = c & 0x3f ;
+ c = fgetc ( f->f ) ;
+ if ( ( c = fgetc ( f->f ) ) < 0 ) {
+ err = msg ( "ES2 PCX read failed" ) ;
+ } else {
+ memset ( p, c, n <= len ? n : len ) ;
+ p += n ;
+ len -= n ;
+ }
+ } else { /* bits 0 to 7 are image data */
+ *p++ = c ;
+ len-- ;
+ }
+ }
+ }
+
+ if ( len != 0 )
+ msg ( "W PCX run-length coding error" ) ;
+
+ return err ;
+}
+
+/* Read a scan line from the current page of IFILE f. Stores
+ number of runs in runs and line width in pels if not null.
+ Pages ends at EOF. Text pages also end if a complete text line
+ would not fit or if the line contains a formfeed character.
+ PBM pages also end when all lines in the bitmap have been
+ read. Fax pages also end at RTC. Returns number of runs stored
+ or EOF at end of page. */
+
+int readline ( IFILE *f, short *runs, int *pels )
+{
+ int nr = 0, nb ;
+ uchar bits [ MAXBITS ] ;
+
+ if ( f->lines != 0 ) { /* -1 allowed as well */
+ switch ( f->page->format ) {
+
+ case P_TEXT :
+ if ( f->txtlines <= 0 ) { /* need another text line */
+ if ( fgets ( f->text, MAXLINELEN, f->f ) ) {
+ f->txtlines = f->charh ;
+ if ( strchr ( f->text, FF ) ) {
+ f->lines = 0 ; /* no more lines in this page */
+ nr = EOF ; /* don't return any */
+ }
+ } else {
+ nr = EOF ;
+ }
+ }
+ if ( nr != EOF ) {
+ nr = texttorun ( (uchar*) f->text, f->font, f->charh - f->txtlines,
+ f->charw, f->charh, f->lmargin,
+ runs, pels ) ;
+ f->txtlines-- ;
+ }
+ break ;
+
+ case P_RAW:
+ case P_PBM:
+ if ( fread ( bits, 1, f->page->w/8, f->f ) != f->page->w/8 ) {
+ nr = EOF ;
+ } else {
+ nr = bittorun ( bits, f->page->w/8, runs ) ;
+ if ( pels ) *pels = f->page->w ;
+ }
+ break ;
+
+ case P_FAX:
+ nr = readruns ( f, runs, pels ) ;
+ break ;
+
+ case P_PCX:
+ nb = ( ( f->page->w + 15 ) / 16 ) * 2 ; /* round up */
+ if ( readpcx ( bits, nb, f ) != 0 ) {
+ nr = EOF ;
+ } else {
+ nr = bittorun ( bits, nb, runs ) ;
+ if ( pels ) *pels = f->page->w ;
+ }
+ break ;
+
+ }
+ } else {
+ nr = EOF ;
+ }
+
+ if ( nr >= 0 && f->lines > 0 ) f->lines-- ;
+
+ return nr ;
+}
+
+
+/* Deduce the file type by scanning buffer p of n bytes. */
+
+int getformat ( uchar *p, int n )
+{
+ int format = 0 ;
+
+ /* figure out file type if not already set */
+
+ if ( ! format && n < 2 ) {
+ format = I_TEXT ;
+ msg ( "W only read %d byte(s) from input file, assuming text", n ) ;
+ }
+
+ if ( ! format && ! p[0] && ! ( p[1] & 0xe0 ) ) {
+ format = I_FAX ;
+ }
+
+ if ( ! format && ! strncmp ( p, "P4", 2 ) ) {
+ format = I_PBM ;
+ }
+
+ if ( ! format && n >= 128 && p[0] == 0x0a &&
+ strchr ("\02\03\05", p[1] ) && p[2] <= 1 ) {
+ if ( p[65] != 1 ) {
+ msg ( "E can't read colour PCX" ) ;
+ } else {
+ format = p[2] ? I_PCX : I_PCX ;
+ }
+ }
+
+ if ( ! format && ! strncmp ( p, "%!", 2 ) ) {
+ msg ( "W Postscript input file will be treated as text" ) ;
+ }
+
+ if ( ! format && ( p[0] == 'M' || p[1] == 'I' ) && ( p[1] == p[0] ) ) {
+ format = I_TIFF ;
+ }
+
+ if ( ! format &&
+ ( p[0] == 0x3a && p[1] == 0xde &&
+ p[2] == 0x68 && p[3] == 0xb1) ) {
+ format = I_DCX ;
+ }
+
+ if ( ! format && n ) { /* "90% printable" heuristic */
+ int i, nprint = 0 ;
+ for ( i=0 ; i<n ; i++ ) {
+ if ( isspace ( p[i] ) || isprint ( p[i] ) ) {
+ nprint++ ;
+ }
+ }
+ if ( ( nprint / (float) n ) > 0.90 ) {
+ format = I_TEXT ;
+ }
+ }
+
+ return format ;
+}
+
+
+/* initialize page descriptor */
+
+void page_init ( PAGE *p, char *fn )
+{
+ p->fname = fn ;
+ p->offset = 0 ;
+ p->w = DEFWIDTH ;
+ p->h = DEFHEIGHT ;
+ p->xres = DEFXRES ;
+ p->yres = DEFYRES ;
+ p->format = P_FAX ;
+ p->revbits = 0 ;
+}
+
+
+void page_report ( PAGE *p, int fmt, int n )
+{
+ msg ( "F page %d : %s + %ld : %dx%d @ %.fx%.f dpi %s/%s",
+ n,
+ p->fname, p->offset, p->w, p->h,
+ p->xres, p->yres,
+ iformatname [fmt], pformatname [p->format] ) ;
+}
+
+/* File handling for undefined file types */
+
+#define auto_first 0
+#define auto_next 0
+
+/* File handling for TIFF files */
+
+#define tiff_reset 0
+
+/* Name of TIFF tag 'tag'. */
+
+char *tagname ( int tag )
+{
+ static struct tagnamestruct { int code ; char *name ; }
+ tagnames [] = {
+ { 256, "width" },
+ { 257, "length" },
+ { 258, "bits/sample" },
+ { 259, "compresssion(g3=3)" },
+ { 262, "photometric(0-min=white)" },
+ { 266, "fill order(msb2lsb=1)" },
+ { 273, "strip offsets" },
+ { 274, "orientation(1=normal)" },
+ { 277, "samples/pixel" },
+ { 278, "rows/strip" },
+ { 279, "strip byte counts" },
+ { 282, "xresolution" },
+ { 283, "yresolution" },
+ { 284, "storage(1=single plane)" },
+ { 292, "g3options" },
+ { 296, "resolution units(2=in,3=cm)" },
+ { 297, "page number" },
+ { 327, "clean fax(0=clean/1=regen/2=errors)" },
+ {0,0} },
+ *p ;
+
+ for ( p=tagnames ; p->code ; p++ )
+ if ( tag == p->code ) break ;
+ return p->code ? p->name : "unknown tag" ;
+}
+
+/* Read 2- or 4-byte integers from a file and correct for file
+ endianness. Returns 0 if OK, 1 on error. */
+
+int fread2 ( unsigned short *p, IFILE *f )
+{
+ int err=0 ;
+ uchar c[2] ;
+
+ err = fread ( c, 1, 2, f->f ) == 2 ? 0 : 1 ;
+
+ *p = f->bigend ?
+ c[0] << 8 | c[1] << 0 :
+ c[1] << 8 | c[0] << 0 ;
+
+ return err ;
+}
+
+int fread4 ( unsigned long *p, IFILE *f )
+{
+ int err=0 ;
+ uchar c[4] ;
+
+ err = fread ( c, 1, 4, f->f ) == 4 ? 0 : 1 ;
+
+ *p = f->bigend ?
+ c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3] << 0 :
+ c[3] << 24 | c[2] << 16 | c[1] << 8 | c[0] << 0 ;
+
+ return err ;
+}
+
+
+/* Read a TIFF directory at current file offset, save image
+ format information and seek to next directory if any. Returns
+ 0 if OK, 2 on errors. */
+
+int tiff_next ( IFILE *f )
+{
+ int err=0 ;
+ unsigned short ntag, tag, type ;
+ unsigned long count, tv ;
+ double ftv ;
+
+ msg ( "F+ TIFF directory at %ld", ftell ( f->f ) ) ;
+
+ if ( fread2 ( &ntag, f ) ) {
+ err = msg ( "E2can't read TIFF tag count" ) ;
+ } else {
+ msg ( "F+ with %d tags", (int) ntag ) ;
+ }
+
+ for ( ; ! err && ntag > 0 ; ntag-- ) {
+
+ err = err || fread2 ( &tag, f ) ;
+ err = err || fread2 ( &type, f ) ;
+ err = err || fread4 ( &count, f ) ;
+
+ if ( type == 3 ) { /* left-aligned short */
+ unsigned short a, b ;
+ err = err || fread2 ( &a, f ) ;
+ err = err || fread2 ( &b, f ) ;
+ tv = a ;
+ } else { /* long or offset to data */
+ err = err || fread4 ( &tv, f ) ;
+ }
+
+ if ( type == 5 ) { /* float as ratio in directory data */
+ long a, b, where=0 ;
+ err = err || ( ( where = ftell ( f->f ) ) < 0 ) ;
+ err = err || fseek ( f->f, tv, SEEK_SET ) ;
+ err = err || fread4 ( &a, f ) ;
+ err = err || fread4 ( &b, f ) ;
+ err = err || fseek ( f->f, where, SEEK_SET ) ;
+ ftv = (float) a / ( b ? b : 1 ) ;
+ } else {
+ ftv = 0.0 ;
+ }
+
+ if ( err ) {
+ err = msg ( "ES2can't read TIFF tag" ) ;
+ continue ;
+ }
+
+#ifdef TIFF_DEBUG
+ {
+ char *tagtype[] = { "none", "byte", "ascii", "short", "long", "ratio" } ;
+ msg ( "F %3d %-5s tag %s %5ld (%3d:%s)",
+ count,
+ type <= 5 ? tagtype[type] : "other",
+ count > 1 ? "@" : "=",
+ type == 5 ? (long) ftv : tv, tag, tagname(tag) ) ;
+ }
+#endif
+
+ switch ( tag ) {
+ case 256 : /* width */
+ f->page->w = tv ;
+ break ;
+ case 257 : /* height */
+ f->page->h = tv ;
+ break ;
+ case 259 : /* compression: 1=none, 3=G3 */
+ if ( tv == 1 ) {
+ f->page->format = P_RAW ;
+ } else if ( tv == 3 ) {
+ f->page->format = P_FAX ;
+ } else {
+ err = msg ( "E2can only read TIFF/G3 or TIFF/uncompressed" ) ;
+ }
+ break ;
+ case 266 : /* fill order */
+ f->page->revbits = ( tv == 2 ? 1 : 0 ) ;
+ break ;
+ case 273 : /* data offset */
+ if ( count != 1 )
+ err = msg ( "E2can't read multi-strip TIFF files" ) ;
+ else
+ f->page->offset = tv ;
+ break ;
+ case 282 : /* x resolution */
+ f->page->xres = ftv ;
+ break ;
+ case 283 : /* y resolution */
+ f->page->yres = ftv ;
+ break ;
+ case 292 : /* T4 options: 1=2D, 2=uncompressed */
+ if ( tv & 0x1 )
+ err = msg ( "E2can't read 2D compressed TIFF-F file" ) ;
+ if ( tv & 0x2 )
+ err = msg ( "E2can't read uncompressed TIFF-F file" ) ;
+ break ;
+ case 296 : /* units: 2=in, 3=cm */
+ if ( tv == 3 ) {
+ f->page->xres *= 2.54 ;
+ f->page->yres *= 2.54 ;
+ }
+ break ;
+ }
+
+ } /* end of tag reading loop */
+
+
+ if ( f->page->format == I_AUTO ) {
+ msg ( "W missing TIFF compression format, set to raw" ) ;
+ f->page->format = P_RAW ;
+ }
+
+ if ( ! err ) {
+
+ if ( fread4 ( &(f->next), f ) ) {
+ err = msg ( "E2can't read offset to next TIFF directory" ) ;
+ } else {
+ if ( f->next ) {
+ msg ( "F , next directory at %ld.", f->next ) ;
+ if ( fseek ( f->f, f->next, SEEK_SET ) )
+ err = msg ( "ES2 seek to next TIFF directory failed" ) ;
+ } else {
+ msg ( "F , last image." ) ;
+ }
+ }
+
+ }
+
+ if ( ! f->page->offset )
+ err = msg ( "E2 missing offset to TIFF data" ) ;
+
+ return err ;
+}
+
+
+int tiff_first ( IFILE *f )
+{
+ short magic, version ;
+
+ fread ( (uchar*) &magic, 1, 2, f->f ) ;
+ f->bigend = ( *(uchar*) &magic == 'M' ) ? 1 : 0 ;
+ fread2 ( &version, f ) ;
+ fread4 ( &(f->next), f ) ;
+
+ msg ( "F TIFF version %d.%d file (%s-endian)",
+ version/10, version%10, f->bigend ? "big" : "little" ) ;
+
+ fseek ( f->f, f->next, SEEK_SET ) ;
+
+ return tiff_next ( f ) ;
+}
+
+
+/* File handling for text files. */
+
+int text_next ( IFILE *f )
+{
+ int err = 0, i, nc ;
+ char buf [ MAXLINELEN ] ;
+
+ f->page->offset = ftell ( f->f ) ;
+ f->page->format = P_TEXT ;
+
+ nc = ( f->page->w - f->lmargin ) / f->charw ;
+
+ if ( nc > MAXLINELEN )
+ nc = MAXLINELEN ;
+
+ for ( i = 0 ; i < f->pglines && fgets ( buf, nc, f->f ) ; i++ ) ;
+
+ f->next = ! feof(f->f) ? ftell ( f->f ) : 0 ;
+
+ err = ferror(f->f) ? 2 : 0 ;
+
+ return err ;
+}
+
+
+int text_first ( IFILE *f )
+{
+ static faxfont defaultfont ;
+
+ if ( ! f->font ) {
+ /* use default font scaled 2X, 1 inch margin, 66 lines/page */
+ f->font = &defaultfont ;
+ readfont ( 0, f->font ) ;
+ if ( ! f->charw ) f->charw = 2 * f->font->w ;
+ if ( ! f->charh ) f->charh = 2 * f->font->h ;
+ if ( ! f->lmargin ) f->lmargin = 204 ;
+ if ( ! f->pglines ) f->pglines = DEFPGLINES ;
+ }
+ /* if not set, take default values from font */
+ if ( ! f->charw ) f->charw = f->font->w ;
+ if ( ! f->charh ) f->charh = f->font->h ;
+ if ( ! f->lmargin ) f->lmargin = 0 ;
+ if ( ! f->pglines ) f->pglines = f->page->h / f->charh - 6 ;
+
+ return text_next ( f ) ;
+}
+
+
+/* File handling for PBM files */
+
+int pbm_first ( IFILE *f )
+{
+ int err=0 ;
+
+ fseek ( f->f, 2, SEEK_SET ) ;
+
+ if ( ! ( f->page->w = pbmdim ( f ) ) || ! ( f->page->h = pbmdim ( f ) ) ) {
+ err = msg ( "E2 EOF or 0 dimension in PBM header" ) ;
+ } else if ( f->page->w % 8 ) {
+ err = msg ( "E2 PBM width must be multiple of 8" ) ;
+ } else {
+ msg ( "F read %dx%d PBM header", f->page->w, f->page->h ) ;
+ }
+
+ f->page->offset = ftell ( f->f ) ;
+ f->page->format = P_PBM ;
+ f->next = 0 ;
+
+ return err ;
+}
+
+#define pbm_next 0
+
+
+/* File handling for FAX files */
+
+#define fax_first 0
+
+#define fax_next 0
+
+/* File handling for PCX files */
+
+/* get a 16-bit word in Intel byte order. */
+
+int fgeti ( IFILE *f )
+{
+ return fgetc ( f->f ) + fgetc ( f->f ) * 256 ;
+}
+
+int pcx_first ( IFILE *f )
+{
+ int err=0, xmin, xmax, ymin, ymax, nc, nb ;
+ long start ;
+
+ start = ftell ( f->f ) ;
+
+ fseek ( f->f, start+4, SEEK_SET ) ;
+ xmin = fgeti ( f ) ;
+ ymin = fgeti ( f ) ;
+ xmax = fgeti ( f ) ;
+ ymax = fgeti ( f ) ;
+
+ f->page->w = xmax - xmin + 1 ;
+ f->page->h = ymax - ymin + 1 ;
+
+ f->page->xres = fgeti ( f ) ;
+ f->page->yres = fgeti ( f ) ;
+
+ fseek ( f->f, start+0x41, SEEK_SET ) ;
+ nc = fgetc ( f->f ) ;
+ nb = fgeti ( f ) ;
+
+ if ( nc != 1 )
+ msg ( "W mono PCX file has %d colour planes", nc ) ;
+
+ if ( nb != ( f->page->w + 15 ) / 16 * 2 )
+ msg ( "W PCX file has %d bytes per scan line for %d pels",
+ nb, f->page->w ) ;
+
+ f->page->offset = start + 128 ;
+ f->page->format = P_PCX ;
+
+ return err ;
+}
+
+#define pcx_next 0
+
+/* File handling for DCX files */
+
+int dcx_next ( IFILE *f )
+{
+ int err=0 ;
+ long thisp, nextp ;
+
+ /* get this and next pages' offsets */
+
+ fseek ( f->f, f->next, SEEK_SET ) ;
+ fread4 ( &thisp, f ) ;
+ fread4 ( &nextp, f ) ;
+
+ /* save address of next directory entry, if any */
+
+ f->next = nextp ? f->next + 4 : 0 ;
+
+ if ( ! thisp )
+ err = msg ( "E2 can't happen (dcx_next)" ) ;
+ else
+ fseek ( f->f, thisp, SEEK_SET ) ;
+
+ return err ? err : pcx_first ( f ) ;
+}
+
+int dcx_first ( IFILE *f )
+{
+ f->bigend = 0 ;
+ f->next = 4 ;
+ return dcx_next ( f ) ;
+}
+
+
+#define raw_reset 0
+#define raw_first 0
+#define raw_next 0
+
+
+/* input file state reset for different compression methods */
+
+#define pcx_reset 0
+#define pbm_reset 0
+
+int text_reset ( IFILE *f )
+{
+ int err=0 ;
+
+ f->lines = ( ( f->pglines * f->charh ) / f->charh ) * f->charh ;
+ f->txtlines = 0 ;
+ f->page->yres = f->lines > 1078 ? 196 : 98 ; /* BOGUS */
+
+ return err ;
+}
+
+int fax_reset ( IFILE *f )
+{
+ int pels ;
+ short runs [ MAXRUNS ] ;
+
+ newDECODER ( &f->d ) ;
+ if ( readruns ( f, runs, &pels ) < 0 || pels ) /* skip first EOL */
+ msg ( "W first line has %d pixels: probably not fax data", pels ) ;
+ f->lines = -1 ;
+
+ return 0 ;
+}
+
+
+/* Skip to start of same (dp=0) or next (dp=1) page image.
+ Returns 0 if OK, 1 if no more pages, 2 on errors. */
+
+int nextipage ( IFILE *f, int dp )
+{
+ int err=0 ;
+
+ int ( *reset [NPFORMATS] ) ( IFILE * ) = {
+ raw_reset, fax_reset, pbm_reset, text_reset, pcx_reset
+ }, (*pf)(IFILE*) ;
+
+ /* close current file if any and set to NULL */
+
+ if ( f->f ) {
+ fclose ( f->f ) ;
+ f->f = 0 ;
+ }
+
+ /* if requested, point to next page and check if done */
+
+ if ( dp ) {
+ f->page++ ;
+ }
+
+ if ( f->page > f->lastpage ) {
+ err = 1 ;
+ }
+
+ /* open the file and seek to start of image data */
+
+ if ( ! err ) {
+ f->f = fopen ( f->page->fname, (f->page->format == P_TEXT) ? "r" : "rb" ) ;
+ if ( ! f->f )
+ err = msg ( "ES2can't open %s:", f->page->fname ) ;
+ }
+
+ if ( ! err && fseek ( f->f, f->page->offset, SEEK_SET ) )
+ err = msg ( "ES2 seek failed" ) ;
+
+ /* default initializations */
+
+ f->lines = f->page->h ;
+
+ /* coding-specific initializations for this page */
+
+ if ( ! err ) {
+ pf = reset[f->page->format] ;
+ if ( pf )
+ err = (*pf)(f) ;
+ }
+
+ return err ;
+}
+
+
+/* Returns true if on last file. */
+
+int lastpage ( IFILE *f )
+{
+ return f->page >= f->lastpage ;
+}
+
+#define dfax_first 0
+#define dfax_next 0
+
+/* Initialize an input (IFILE) structure. This structure
+ collects the data about images to be processed to allow a
+ simple interface for functions that need to read image files.
+
+ The IFILE is initialized by building an array of information
+ for each page (image) in all of the files.
+
+ The page pointer index is initialized so that the first call
+ to nextipage with dp=1 actually opens the first file.
+
+*/
+
+
+int newIFILE ( IFILE *f, char **fnames )
+{
+ int err=0, i, n, fformat=0 ;
+ char **p ;
+ uchar buf[128] ;
+ int ( *fun ) ( IFILE * ) ;
+
+ int ( *first [NIFORMATS] ) ( IFILE * ) = {
+ auto_first, pbm_first, fax_first, text_first, tiff_first,
+ dfax_first, pcx_first, raw_first, dcx_first
+ } ;
+
+ int ( *next [NIFORMATS] ) ( IFILE * ) = {
+ auto_next, pbm_next, fax_next, text_next, tiff_next,
+ dfax_next, pcx_next, raw_next, dcx_next
+ } ;
+
+ f->page = f->pages ;
+
+ /* get info for all pages in all files */
+
+ for ( p=fnames ; ! err && *p ; p++ ) {
+
+ if ( ! ( f->f = fopen ( *p, "rb" ) ) )
+ err = msg ( "ES2 can't open %s:", *p ) ;
+
+ if ( ! err ) {
+ n = fread ( buf, 1, 128, f->f ) ;
+ if ( ferror ( f->f ) )
+ err = msg ( "ES2 can't open %s:", *p ) ;
+ }
+
+ if ( ! err ) {
+ fformat = getformat ( buf, n ) ;
+ if ( ! fformat )
+ err = msg ( "E2 can't get format of %s", *p ) ;
+ }
+
+ if ( ! err && fseek ( f->f, 0, SEEK_SET ) )
+ err = msg ( "ES2 can't rewind %s:", *p ) ;
+
+ /* get format information for all pages in this file */
+
+ for ( i=0 ; ! err ; i++ ) {
+
+ page_init ( f->page, *p ) ;
+
+ if ( ( fun = i ? next[fformat] : first[fformat] ) )
+ err = (*fun)(f) ;
+
+ if ( ! err ) {
+
+ page_report ( f->page, fformat, f->page - f->pages + 1 ) ;
+
+ f->page++ ;
+
+ if ( f->page >= f->pages + MAXPAGE )
+ err = msg ( "E2 too many pages (max is %d)", MAXPAGE ) ;
+ }
+
+ if ( ! f->next ) break ;
+ }
+
+ if ( f->f ) {
+ fclose ( f->f ) ;
+ f->f = 0 ;
+ }
+
+ }
+
+ f->lastpage = f->page - 1 ;
+
+ f->page = f->pages ;
+
+ if ( ! normalbits[1] ) initbittab() ; /* bit-reverse table initialization */
+
+ return err ;
+}
+
+ /* Image File Output Functions */
+
+/* Strings and function to write a bit map in HP-PCL format. The only
+ compression is removal of trailing zeroes. Margins and resolution are
+ set before first write. */
+
+char *PCLBEGIN =
+ "\033E" /* Printer reset. */
+ "\033&l0E" /* top margin = 0 */
+ "\033&a0L" /* left margin = 0 */
+ "\033*t%dR" /* Set raster graphics resolution */
+ "\033*r1A" ; /* Start raster graphics, rel. adressing */
+
+char *PCLEND =
+ "\033*rB" /* end raster graphics */
+ "\014" /* form feed */
+ "\033E" ; /* Printer reset. */
+
+void pclwrite ( OFILE *f, unsigned char *buf, int n )
+{
+ while ( n > 0 && buf [ n-1 ] == 0 ) n-- ;
+ fprintf( f->f, "\033*b%dW", n ) ;
+ fwrite ( buf, n, 1, f->f ) ;
+}
+
+
+/* Write a bit map as (raw) Portable Gray Map (PGM) format after
+ decimating by a factor of 4. Sums bits in each 4x4-pel square
+ to compute sample value. This function reduces each dimension
+ of a bit map by 4 (it writes n*8/4 pixels per scan line and
+ one scan line for every 4 in). The 17 possible sample values
+ are spread linearly over the range 0-255. */
+
+void pgmwrite ( OFILE *f, uchar *buf, int n )
+{
+ static uchar gval [ MAXBITS * 8 / 4 ] ;
+ static int init=0, lines=0 ;
+ static uchar hbits [ 256 ], lbits [ 256 ] ;
+ static int nybblecnt [ 16 ] = { 0,1,1,2, 1,2,2,3, 1,2,2,3, 2,3,3,4 } ;
+ static uchar corr [ 17 ] = { 255, 239, 223, 207, 191, 175, 159, 143, 127,
+ 111, 95, 79, 63, 47, 31, 15, 0 } ;
+ int m ;
+ uchar *p, *q ;
+
+ if ( ! init ) { /* build table of bit counts in each nybble */
+ short i ;
+ for ( i=0 ; i<256 ; i++ ) {
+ hbits [ i ] = nybblecnt [ i >> 4 & 0x0f ] ;
+ lbits [ i ] = nybblecnt [ i & 0x0f ] ;
+ }
+ init = 1 ;
+ }
+
+ for ( m=n, p=gval, q=buf ; m-- > 0 ; q++ ) {
+ *p++ += hbits [ *q ] ;
+ *p++ += lbits [ *q ] ;
+ }
+
+ if ( ( lines++ & 0x03 ) == 0x03 ) {
+ for ( p=gval, m=2*n ; m-- > 0 ; p++ ) *p = corr [ *p ] ;
+ fwrite ( gval, 1, 2*n, f->f ) ;
+ memset ( gval, 0, 2*n ) ;
+ }
+}
+
+
+/* Postscript image data is differentially coded vertically and
+ run-length coded horizontally. A leading byte (n) defines the type
+ of coding for subsequent data:
+
+ 0 repeat previous line
+ 1-127 n data bytes follow
+ 128-254 copy n-127 bytes from previous line
+ 255 n repeat the next character 'n' times
+
+ The overhead for coding a copy is 2 bytes (copy count, data count),
+ so copies > 2 bytes should be so coded. The overhead for coding a
+ run is 4 bytes (255, count, byte, data count), so runs > 4 bytes
+ should be so coded. Copies decode/execute faster and code more
+ compactly so are preferred over runs.
+
+*/
+
+const char PSBEGIN [] = /* start of file */
+ "%%!PS-Adobe-2.0 EPSF-2.0 \n"
+ "%%%%Creator: efax (Copyright 1995 Ed Casas) \n"
+ "%%%%Title: efix output\n"
+ "%%%%Pages: (atend) \n"
+ "%%%%BoundingBox: 0 0 %d %d \n"
+ "%%%%BeginComments \n"
+ "%%%%EndComments \n"
+ "/val 1 string def \n"
+ "/buf %d string def \n"
+ "/getval { \n"
+ " currentfile val readhexstring pop 0 get \n"
+ "} bind def \n"
+ "/readbuf { \n"
+ " 0 %% => index \n"
+ " { \n"
+ " dup buf length ge { exit } if \n"
+ " getval %% => index run_length \n"
+ " dup 127 le { \n"
+ " dup 0 eq { \n"
+ " pop buf length \n"
+ " } { \n"
+ " currentfile buf 3 index 3 index getinterval readhexstring pop pop\n"
+ " } ifelse \n"
+ " } { \n"
+ " dup 255 eq { \n"
+ " pop getval getval %% => index run_length value \n"
+ " 2 index 1 3 index 2 index add 1 sub %% => ... start 1 end \n"
+ " { buf exch 2 index put } for \n"
+ " pop \n"
+ " } { \n"
+ " 127 sub \n"
+ " } ifelse \n"
+ " } ifelse \n"
+ " add %% => index \n"
+ " } loop \n"
+ " pop \n"
+ " buf \n"
+ "} bind def \n"
+ "%%%%EndProlog \n" ;
+
+const char PSPAGE [] = /* start of page */
+ "%%%%Page: %d %d \n"
+ "gsave \n"
+ "%f %f translate \n"
+ "%f %f scale \n"
+ "%d %d %d [ %d %d %d %d %d %d ] { readbuf } image \n" ;
+
+const char PSPAGEEND [] = /* end of page */
+ "\n"
+ "grestore \n"
+ "showpage \n" ;
+
+const char PSEND [] = /* end of file */
+ "%%Trailer \n"
+ "%%%%Pages: %d \n" ;
+
+
+void psinit ( OFILE *f, int newfile, int page, int w, int h, int n )
+{
+ float ptw, pth ;
+
+ if ( ! f ) {
+ msg ( "E2 can't happen (psinit)" ) ;
+ return ;
+ }
+
+ ptw = w/f->xres * 72.0 ; /* convert to points */
+ pth = h/f->yres * 72.0 ;
+
+ if ( newfile )
+ fprintf ( f->f, PSBEGIN,
+ (int) ptw, (int) pth, /* Bounding Box */
+ n ) ; /* buffer string length */
+
+ fprintf ( f->f, PSPAGE,
+ page, page, /* page number */
+ 0.0, 0.0, /* shift */
+ ptw, pth, /* scaling */
+ w, h, 1, /* image size */
+ w, 0, 0, -h, 0, h ) ; /* CTM */
+ f->pslines = 0 ;
+ f->lastpageno = page ;
+}
+
+
+char nhexout = 0, hexchars [ 16 ] = "0123456789abcdef" ;
+
+#define hexputc( f, c ) ( \
+ putc ( hexchars [ (c) >> 4 ], f ), \
+ putc ( hexchars [ (c) & 0x0f ], f ), \
+ ( ( ( nhexout++ & 31 ) == 31 ) ? putc ( '\n', f ) : 0 ) )
+
+void hexputs ( FILE *f, uchar *p, int n )
+{
+ uchar c ;
+ if ( n > 0 ) {
+ hexputc ( f, n ) ;
+ while ( n-- ) { c = *p++ ^ 0xff ; hexputc ( f, c ) ; }
+ }
+}
+
+/* Encode into postscript. If not a repeated line, test (using
+ index j) from current position (i) for possible encodings as:
+ copy of > 2 bytes, runs of > 4 or data >=127. Otherwise the
+ byte is skipped. Uncoded bytes are output from the last
+ uncoded byte (l) before output of runs/copies. */
+
+void pswrite ( OFILE *f, unsigned char *buf, int n )
+{
+ int i, j, l ;
+ static unsigned char last [ MAXBITS ] ;
+
+ l=i=0 ;
+
+ if ( ! f || ! buf || n<0 ) {
+ msg ( "E2 can't happen (pswrite)" ) ;
+ return ;
+ }
+
+ for ( j=0 ; j<n && buf[j]==last[j] && f->pslines ; j++ ) ;
+ if ( j == n ) { /* repeat line */
+ hexputc ( f->f, 0 ) ;
+ l=i=n ;
+ }
+
+ while ( i<n ) {
+
+ for ( j=i ; j<n && buf[j]==last[j] && j-i<127 && f->pslines ; j++ ) ;
+ if ( j-i > 2 ) { /* skip */
+ hexputs ( f->f, buf+l, i-l ) ;
+ hexputc ( f->f, j-i + 127 ) ;
+ l=i=j ;
+ } else {
+ for ( j=i ; j<n && buf[j]==buf[i] && j-i<255 ; j++ ) ;
+ if ( j-i > 4 ) { /* run */
+ hexputs ( f->f, buf+l, i-l ) ;
+ hexputc ( f->f, 255 ) ;
+ hexputc ( f->f, j-i ) ;
+ hexputc ( f->f, buf[i] ^ 0xff ) ;
+ l=i=j ;
+ } else {
+ if ( i-l >= 127 ) { /* maximum data length */
+ hexputs ( f->f, buf+l, i-l ) ;
+ l=i ;
+ } else { /* data */
+ i++ ;
+ }
+ }
+ }
+
+ }
+ hexputs ( f->f, buf+l, i-l ) ;
+
+ if ( n >= 0 )
+ memcpy ( last, buf, n ) ;
+
+ f->pslines++ ;
+}
+
+
+/* Write 2- and 4-byte integers to an image output file. Return
+ as for fwrite. */
+
+int fwrite2 ( short s, OFILE *f )
+{
+ uchar *p = (void*) &s ;
+ return fwrite ( bigendian ? p + sizeof(short) - 2 : p, 2, 1, f->f ) ;
+}
+
+int fwrite4 ( long l, OFILE *f )
+{
+ uchar *p = (void*) &l ;
+ return fwrite ( bigendian ? p + sizeof(long ) - 4 : p, 4, 1, f->f ) ;
+}
+
+
+/* Write a TIFF directory tag. Returns 0 if OK, 1 on errors. */
+
+int wtag ( OFILE *f, int lng, short tag, short type, long count, long offset )
+{
+ int err=0 ;
+
+ err = err || ! fwrite2 ( tag, f ) ;
+ err = err || ! fwrite2 ( type, f ) ;
+ err = err || ! fwrite4 ( count, f ) ;
+ if ( lng ) {
+ err = err || ! fwrite4 ( offset, f ) ;
+ } else {
+ err = err || ! fwrite2 ( offset, f ) ;
+ err = err || ! fwrite2 ( 0, f ) ;
+ }
+
+ if ( err ) msg ( "ES2 can't write TIFF tag" ) ;
+ return err ;
+}
+
+
+/* Write TIFF header and directory. File format based on Sam
+ Leffler's tiff.h. Can only be used for single-image TIFFs
+ because always seeks to start of file to re-write the
+ header. */
+
+#define NTAGS 17 /* number of tags in directory */
+#define NRATIO 2 /* number of floats (as ratios) */
+
+int tiffinit ( OFILE *f )
+{
+ int err=0, compr=1 ;
+ long tdoff, doff ;
+
+ fseek ( f->f, 0, SEEK_SET ) ;
+
+ /* 0 ==> (start of TIFF file) */
+
+ /* write magic, TIFF version and offset to directory */
+
+ fwrite2 ( bigendian ? 0x4d4d : 0x4949, f ) ;
+ fwrite2 ( 42, f ) ;
+ fwrite4 ( 8, f ) ;
+
+ /* 8 ==> directory */
+
+ fwrite2 ( NTAGS, f ) ;
+
+ /* figure out offsets within file and compression code */
+
+ tdoff = 8 + 2 + NTAGS*12 + 4 ; /* offset to directory data */
+ doff = tdoff + NRATIO*8 ; /* offset to image data */
+
+ switch ( f->format ) {
+ case O_TIFF_RAW: compr = 1 ; break ;
+ case O_TIFF_FAX: compr = 3 ; break ;
+ default: err = msg ( "E2can't happen(tiffinit)" ) ; break ;
+ }
+
+ /* write directory tags, 12 bytes each */
+
+ wtag( f, 1, 256, 4, 1, f->w ) ; /* width long */
+ wtag( f, 1, 257, 4, 1, f->h ) ; /* length long */
+ wtag( f, 0, 258, 3, 1, 1 ) ; /* bits/sample short */
+
+ wtag( f, 0, 259, 3, 1, compr ) ; /* compresssion(g3=3) short */
+ wtag( f, 0, 262, 3, 1, 0 ) ; /* photometric(0-min=white) short */
+ wtag( f, 0, 266, 3, 1, 1 ) ; /* fill order(msb2lsb=1) short */
+ wtag( f, 1, 273, 4, 1, doff ) ; /* strip offsets long */
+
+ wtag( f, 0, 274, 3, 1, 1 ) ; /* orientation(1=normal) short */
+ wtag( f, 0, 277, 3, 1, 1 ) ; /* samples/pixel short */
+ wtag( f, 1, 278, 4, 1, f->h ) ; /* rows/strip long */
+ wtag( f, 1, 279, 4, 1, f->bytes ) ; /* strip byte counts long */
+
+ wtag( f, 1, 282, 5, 1, tdoff+0 ) ; /* xresolution ratio */
+ wtag( f, 1, 283, 5, 1, tdoff+8 ) ; /* yresolution ratio */
+ wtag( f, 0, 284, 3, 1, 1 ) ; /* storage(1=single plane) short */
+ wtag( f, 1, 292, 4, 1, 0 ) ; /* g3options long */
+
+ wtag( f, 0, 296, 3, 1, 2 ) ; /* resolution units(2=in,3=cm) short */
+ wtag( f, 0, 327, 3, 1, 0 ) ; /* clean fax(0=clean) short */
+
+ fwrite4 ( 0, f ) ; /* offset to next dir (no more) */
+
+ /* ==> tdoff (tag data offset), write ratios for floats here */
+
+ fwrite4 ( f->xres+0.5, f ) ;
+ fwrite4 ( 1, f ) ;
+ fwrite4 ( f->yres+0.5, f ) ;
+ fwrite4 ( 1, f ) ;
+
+ /* ==> doff (strip data offset), image data goes here */
+
+ return err ;
+}
+
+
+/* Convert array 'runs' of 'nr' run lengths into a bit map 'buf'. Returns
+ the number of bytes filled. */
+
+int runtobit ( short *runs, int nr, uchar *buf )
+{
+ static uchar zerofill [ 9 ] = {
+ 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00 } ;
+ static uchar onefill [ 9 ] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff } ;
+
+ uchar col=0, *buf0 = buf ;
+ register short len, b=8, bytes ;
+
+ while ( nr-- > 0 ) {
+ len = *runs++ ;
+ if ( col ) *buf |= onefill [ b ] ; /* right bits of cur. byte */
+ else *buf &= zerofill [ b ] ;
+ if ( b > len ) { /* done fill */
+ b -= len ;
+ } else { /* continue to next byte */
+ len -= b ;
+ buf++ ;
+ b = 8 ;
+ if ( ( bytes = len>>3 ) > 0 ) { /* fill >1 byte */
+ memset ( buf, col, bytes ) ;
+ len -= bytes*8;
+ buf += bytes ;
+ }
+ *buf = col ; /* flood the rest */
+ b -= len ;
+ }
+ col ^= 0xff ;
+ }
+
+ return buf - buf0 + ( b < 8 ) ;
+}
+
+
+/* Write a PCX file header. */
+
+int fputi ( int i, OFILE *f )
+{
+ putc ( i & 0xff, f->f ) ;
+ putc ( ( i >> 8 ) & 0xff, f->f ) ;
+ return 0 ;
+}
+
+void pcxinit ( OFILE *f )
+{
+ uchar buf [ 60 ] = { 0x0a, 3, 1, 1 } ; /* magic, version, compr, BPP */
+
+ fwrite ( buf, 1, 4, f->f ) ; /* 4 */
+ fputi ( 0, f ) ; /* 8 xmin, ymin, xmax, ymax */
+ fputi ( 0, f ) ;
+ fputi ( f->w-1, f ) ;
+ fputi ( f->h-1, f ) ;
+ fputi ( f->xres, f ) ; /* 4 x and y dpi */
+ fputi ( f->yres, f ) ;
+ memset ( buf, 0, 48 ) ; /* 48 palette */
+ fwrite ( buf, 1, 48, f->f ) ;
+ putc ( 0, f->f ) ; /* 1 reserved */
+ putc ( 1, f->f ) ; /* 1 planes per pixel */
+ fputi ( (f->w+15)/16*2, f ) ; /* 2 bytes per line */
+ memset ( buf, 0, 60 ) ; /* 60 zero */
+ fwrite ( buf, 1, 60, f->f ) ;
+}
+
+/* Write a PCX-compressed scan line. */
+
+void pcxwrite ( OFILE *of, uchar *p, int nb )
+{
+ int c, n, runc ;
+ FILE *f = of->f ;
+
+ runc = *p++ ;
+ n = 1 ;
+
+ for ( nb-- ; nb > 0 ; nb-- ) {
+ c = *p++ ;
+ if ( c == runc && n < 63 ) { /* continue run */
+ n++ ;
+ } else { /* terminate run */
+ if ( n > 1 || ( ( runc & 0xc0 ) == 0xc0 ) ) /* output as run */
+ putc ( n | 0xc0, f ) ;
+ putc ( runc, f ) ;
+ runc = c ; /* start new run */
+ n = 1 ;
+ }
+ }
+
+ /* last run */
+
+ if ( n > 1 || ( ( runc & 0xc0 ) == 0xc0 ) ) /* output as run */
+ putc ( n | 0xc0, f ) ;
+ putc ( runc, f ) ;
+
+}
+
+
+/* Begin/end output pages. If not starting first page (0), terminate
+ previous page. If output filename pattern is defined, [re-]opens that
+ file. If not terminating last page (page==EOF), writes file header.
+ Returns 0 or 2 on errors. */
+
+int nextopage ( OFILE *f, int page )
+{
+ int err = 0 ;
+ int i, nb=0 ;
+ uchar *p, codes [ ( RTCEOL * EOLBITS ) / 8 + 3 ] ;
+
+ if ( f->f ) { /* terminate previous page */
+
+ switch ( f->format ) {
+ case O_PBM:
+ break ;
+ case O_PGM:
+ break ;
+ case O_FAX:
+ case O_TIFF_FAX:
+ for ( p = codes, i=0 ; i<RTCEOL ; i++ )
+ p = putcode ( &f->e, EOLCODE, EOLBITS, p ) ;
+ nb = putcode ( &f->e, 0, 0, p ) - codes ;
+ fwrite ( codes, 1, nb, f->f ) ;
+ f->bytes += nb ;
+ if ( f->format == O_TIFF_FAX ) tiffinit ( f ) ;
+ break ;
+ case O_TIFF_RAW:
+ tiffinit(f) ; /* rewind & update TIFF header */
+ break ;
+ case O_PCL:
+ fprintf ( f->f, PCLEND ) ;
+ break ;
+ case O_PS:
+ fprintf ( f->f, PSPAGEEND ) ;
+ if ( f->fname || page<0 ) fprintf ( f->f, PSEND, f->lastpageno ) ;
+ break ;
+ case O_PCX:
+ case O_PCX_RAW:
+ fseek ( f->f, 0, SEEK_SET ) ;
+ pcxinit ( f ) ;
+ break ;
+ }
+
+ if ( ferror ( f->f ) ) {
+ err = msg ("ES2output error:" ) ;
+ } else {
+ msg ( "F+ wrote %s as %dx%d pixel %.fx%.f dpi %s page",
+ f->cfname, f->w, f->h, f->xres, f->yres,
+ oformatname [f->format] ) ;
+
+ switch ( f->format ) {
+ case O_PS:
+ msg ( "F (%d lines)", f->pslines ) ;
+ break ;
+ case O_TIFF_RAW:
+ case O_TIFF_FAX:
+ msg ( "F (%d bytes)", f->bytes ) ;
+ break ;
+ default:
+ msg ( "F " ) ;
+ break ;
+ }
+
+ }
+
+ }
+
+ if ( ! err && page >= 0 ) { /* open new file */
+ if ( f->fname ) {
+ sprintf ( f->cfname, f->fname, page+1, page+1, page+1 ) ;
+
+ if ( ! f->f )
+ f->f = fopen ( f->cfname, ( f->format == O_PS ) ? "w" : "wb+" ) ;
+ else
+ f->f = freopen ( f->cfname, ( f->format == O_PS ) ? "w" : "wb+", f->f ) ;
+
+ if ( ! f->f ) {
+ err = msg ("ES2can't open output file %s:", f->cfname ) ;
+ }
+ } else {
+ f->f = stdout ;
+ strcpy ( f->cfname, "standard output" ) ;
+ }
+ }
+
+ /* start new page */
+
+ if ( ! err && page >= 0 ) {
+ switch ( f->format ) {
+ case O_PBM:
+ fprintf ( f->f, "P4 %d %d\n", f->w, f->h ) ;
+ break ;
+ case O_PGM:
+ fprintf ( f->f, "P5 %d %d %d\n", f->w/4, f->h/4, 255 ) ;
+ break ;
+ case O_FAX:
+ case O_TIFF_FAX:
+ if ( f->format == O_TIFF_FAX ) tiffinit ( f ) ;
+ p = putcode ( &f->e, EOLCODE, EOLBITS, codes ) ;
+ nb = p - codes ;
+ fwrite ( codes, 1, nb, f->f ) ;
+ break ;
+ case O_TIFF_RAW:
+ tiffinit ( f ) ;
+ break ;
+ case O_PCL:
+ fprintf ( f->f, PCLBEGIN, (int) f->xres ) ;
+ break ;
+ case O_PS:
+ psinit ( f, ( f->fname || page==0 ), page+1, f->w, f->h, f->w/8 ) ;
+ break ;
+ case O_PCX:
+ case O_PCX_RAW:
+ fseek ( f->f, 0, SEEK_SET ) ;
+ pcxinit ( f ) ;
+ break ;
+ }
+
+ if ( ferror ( f->f ) ) err = msg ("ES2output error:" ) ;
+ }
+
+ /* only count lines/bytes for those formats that don't have
+ headers or where we will update the headers on closing */
+
+ switch ( f->format ) {
+ case O_FAX:
+ case O_TIFF_FAX:
+ case O_PCX:
+ case O_PCX_RAW:
+ f->h = 0 ;
+ f->bytes = nb ;
+ break ;
+ }
+
+ return err ;
+}
+
+
+/* Output scan line of nr runs no times to output file f. */
+
+void writeline ( OFILE *f, short *runs, int nr, int no )
+{
+ int nb = 0 ;
+ uchar *p, buf [ MAXCODES ] ;
+
+ /* if line to be output, convert to right format */
+
+ if ( no > 0 )
+ switch ( f->format ) {
+ case O_PBM:
+ case O_PGM:
+ case O_PCL:
+ case O_PS:
+ case O_TIFF_RAW:
+ case O_PCX:
+ case O_PCX_RAW:
+ nb = runtobit ( runs, nr, buf ) ;
+ break ;
+ case O_FAX:
+ case O_TIFF_FAX:
+ break ;
+ }
+
+ /* output `no' times. */
+
+ while ( no-- > 0 ) {
+ switch ( f->format ) {
+ case O_PCX_RAW:
+ case O_TIFF_RAW:
+ case O_PBM:
+ fwrite ( buf, 1, nb, f->f ) ;
+ break ;
+ case O_PGM:
+ pgmwrite ( f, buf, nb ) ;
+ break ;
+ case O_TIFF_FAX:
+ case O_FAX:
+ p = runtocode ( &f->e, runs, nr, buf ) ;
+ p = putcode ( &f->e, EOLCODE, EOLBITS, p ) ;
+ nb = p - buf ;
+ fwrite ( buf, 1, nb, f->f ) ;
+ break ;
+ case O_PCL:
+ pclwrite ( f, buf, nb ) ;
+ break ;
+ case O_PS:
+ pswrite ( f, buf, nb ) ;
+ break ;
+ case O_PCX:
+ pcxwrite ( f, buf, nb ) ;
+ break ;
+ }
+
+ /* only count lines/bytes for those formats that don't have
+ headers or where we will update the headers on closing */
+
+ switch ( f->format ) {
+ case O_FAX:
+ case O_TIFF_FAX:
+ case O_TIFF_RAW:
+ case O_PCX:
+ case O_PCX_RAW:
+ f->h++ ;
+ f->bytes += nb ;
+ break ;
+ }
+
+ }
+}
+
+
+/* Initialize new output file. If fname is NULL, stdout will be used for
+ all images. */
+
+void newOFILE ( OFILE *f, int format, char *fname,
+ float xres, float yres, int w, int h )
+{
+ f->f = 0 ;
+ f->format = format ;
+ f->fname = fname ;
+ f->xres = xres ;
+ f->yres = yres ;
+ f->w = w ;
+ f->h = h ;
+ f->bytes = 0 ;
+ newENCODER ( &f->e ) ;
+}
+
+/* Read a bitmap to use as a font and fill in the font data. If
+ the file name is null, empty, or there are errors, the font is
+ initialized to the built-in font. Returns 0 if OK, 2 on
+ errors. */
+
+int readfont ( char *fname, faxfont *font )
+{
+ int err=0, i, j, n=0, nr, nb, fontok=0, pels ;
+ char *fnames [2] = { 0, 0 } ;
+ short runs [ MAXRUNS ] ;
+ IFILE f;
+
+ if ( fname && *fname ) {
+
+ fnames[0] = fname ;
+
+ newIFILE ( &f, fnames ) ;
+
+ if ( nextipage ( &f, 0 ) ) {
+ err = msg ( "E2 can't open font file %s", fnames[0] ) ;
+ }
+
+ nb = 0 ;
+ while ( ! err && ( nr = readline ( &f, runs, &pels ) ) >= 0 ) {
+ if ( nb+pels/8 < MAXFONTBUF ) {
+ nb += runtobit ( runs, nr, font->buf+nb ) ;
+ } else {
+ err = msg ("E2font file %s too large (max %d bytes)",
+ fnames[0], MAXFONTBUF ) ;
+ }
+ }
+
+ if ( ! err && nb != f.page->w * f.page->h / 8 )
+ err = msg ( "E2 read %d bytes of font data for %dx%d bitmap",
+ nb, f.page->w, f.page->h ) ;
+
+ if ( ! err && ( f.page->w / 256 > MAXFONTW || f.page->h > MAXFONTH ) ) {
+ err = msg ( "E2font size (%dx%d) too large", f.page->w, f.page->h ) ;
+ }
+
+ if ( err ) {
+ font->w = font->h = 0 ;
+ } else {
+ font->w = f.page->w / 256 ;
+ font->h = f.page->h ;
+ for ( i=0 ; i<256 ; i++ ) font->offset[i] = i*font->w ;
+ msg ("Iread %dx%d font %s (%d bytes)", font->w, font->h, fname, nb ) ;
+ fontok = 1 ;
+ }
+
+ if ( f.f ) {
+ fclose ( f.f ) ;
+ f.f = 0 ;
+ }
+ }
+
+ if ( ! fontok ) { /* use built-in font */
+
+ font->w = STDFONTW ;
+ font->h = STDFONTH ;
+
+ for ( i=j=0 ; j<STDFONTBUF ; i++ ) /* expand bit map */
+ if ( stdfont [ i ] == 0 )
+ for ( n = stdfont [ ++i ] ; n > 0 ; n-- )
+ font->buf [ j++ ] = 0 ;
+ else
+ font->buf [ j++ ] = stdfont [ i ] ;
+
+ if ( i != 1980 ) err = msg ( "E2can't happen(readfont)" ) ;
+
+ for ( i=0 ; i<256 ; i++ ) font->offset[i] = i*font->w ;
+ }
+
+ return err ;
+}
+
+
+/* Initialize bit reversal lookup tables (note that the
+ `normalbits' array is the one actually used for the bit
+ reversal. */
+
+void initbittab ( void )
+{
+ int i ;
+ for ( i=0 ; i<256 ; i++ )
+ normalbits [ reversebits [ i ] = i ] =
+ ( i& 1 ? 128:0 ) | ( i& 2 ? 64:0 ) | ( i& 4 ? 32:0 ) | ( i& 8 ? 16:0 ) |
+ ( i&16 ? 8:0 ) | ( i&32 ? 4:0 ) | ( i&64 ? 2:0 ) | ( i&128 ? 1:0 ) ;
+}
+
+
+ /* T.4 Encoding/Decoding */
+
+/* Table-lookup decoder for variable-bit-length codewords. The table index
+ is the N most recently undecoded bits with the first (oldest) undecoded
+ bit as the MS bit. If the N bits uniquely identify a codeword then the
+ indexed 'code' member identifies the code, otherwise it is zero. The
+ 'bits' member gives the number of bits to be considered decoded (to be
+ removed from the bit stream) and the 'next' element is a pointer to the
+ table to use for decoding the next part of the bit sequence.
+
+ For T.4 decoding the longest T.4 codeword is 13 bits. The implementation
+ below uses two tables of 512 elements (N=9 bits) for each colour.
+ Codewords longer than 9 bits require a second lookup. Since all
+ codewords longer than than 9 bits have a 4-bit zero prefix it is
+ possible to use only one secondary 9-bit lookup table by dropping only
+ the first 4 bits after the first lookup. The code indentifier is the run
+ length + 1. A separate table is used for decoding the variable-length
+ FILL patterns.
+
+ For undefined codewords, one bit is skipped and decoding continues at
+ the white code table. */
+
+/* the lookup tables for each colour and the fill lookup table */
+
+dtab tw1 [ 512 ], tw2 [ 512 ], tb1 [ 512 ], tb2 [ 512 ], fill [ 512 ] ;
+char tabinit=0 ;
+
+/* Add code cword shifted left by shift to decoding table tab. */
+
+void addcode ( dtab *tab, int cword, int shift,
+ short code, short bits, dtab *next )
+{
+ int i, n = 1 << shift ;
+
+ for ( i = cword << shift ; n-- > 0 ; i++ ) {
+ tab[i].code = code ;
+ tab[i].bits = bits ;
+ tab[i].next = next ;
+ }
+}
+
+/* Initialize the decoding table for one colour using the codes in the T.4
+ table p0. t1 and t2 are the two decoding tables and ot is the first
+ table of the other colour. */
+
+void init1dtab ( t4tab *p0, dtab *t1, dtab *t2, dtab *ot )
+{
+ t4tab *p ;
+ for ( p = p0 ; p->code ; p++ )
+ if ( p->bits <= 9 ) {
+ addcode ( t1, p->code, 9 - p->bits, p->rlen + 1, p->bits,
+ ( p - p0 ) > 63 ? t1 : ot ) ;
+ } else {
+ addcode ( t1, p->code >> ( p->bits - 9 ), 0, 0, 4, t2 ) ;
+ addcode ( t2, p->code, 13 - p->bits, p->rlen + 1, p->bits - 4,
+ ( p - p0 ) > 63 ? t1 : ot ) ;
+ }
+}
+
+
+/* Initialize a T.4 decoder. */
+
+void newDECODER ( DECODER *d )
+{
+ int i ;
+
+ if ( ! tabinit ) {
+
+ /* undefined codes */
+
+ addcode ( tw1, 0, 9, 0, 1, tw1 ) ;
+ addcode ( tw2, 0, 9, 0, 1, tw1 ) ;
+ addcode ( tb1, 0, 9, 0, 1, tw1 ) ;
+ addcode ( tb2, 0, 9, 0, 1, tw1 ) ;
+ addcode ( fill, 0, 9, 0, 1, tw1 ) ;
+
+ /* fill and EOL */
+
+ addcode ( tw1, 0, 0, 0, 4, tw2 ) ;
+ addcode ( tw2, 0, 2, 0, 7, fill ) ;
+ addcode ( tb1, 0, 0, 0, 4, tb2 ) ;
+ addcode ( tb2, 0, 2, 0, 7, fill ) ;
+
+ addcode ( fill, 0, 0, 0, 9, fill ) ;
+ for ( i=0 ; i<=8 ; i++ )
+ addcode ( fill, 1, i, -1, 9-i, tw1 ) ;
+
+ /* white and black runs */
+
+ init1dtab ( wtab, tw1, tw2, tb1 ) ;
+ init1dtab ( btab, tb1, tb2, tw1 ) ;
+
+ tabinit=1 ;
+ }
+
+ /* initialize decoder to starting state */
+
+ d->x = 0 ;
+ d->shift = -9 ;
+ d->tab = tw1 ;
+ d->eolcnt = 0 ;
+}
+
+ /* T.4 coding table and default font for efax/efix */
+
+/* T.4 1-D run-length coding tables. codes must be in run length
+ order for runtocode(). */
+
+t4tab wtab [ ( 64 + 27 + 13 ) + 1 ] = { /* runs of white */
+
+/* Terminating White Codes */
+
+{53,8,0}, {7,6,1}, {7,4,2}, {8,4,3}, {11,4,4}, {12,4,5},
+{14,4,6}, {15,4,7}, {19,5,8}, {20,5,9}, {7,5,10}, {8,5,11},
+{8,6,12}, {3,6,13}, {52,6,14}, {53,6,15}, {42,6,16}, {43,6,17},
+{39,7,18}, {12,7,19}, {8,7,20}, {23,7,21}, {3,7,22}, {4,7,23},
+{40,7,24}, {43,7,25}, {19,7,26}, {36,7,27}, {24,7,28}, {2,8,29},
+{3,8,30}, {26,8,31}, {27,8,32}, {18,8,33}, {19,8,34}, {20,8,35},
+{21,8,36}, {22,8,37}, {23,8,38}, {40,8,39}, {41,8,40}, {42,8,41},
+{43,8,42}, {44,8,43}, {45,8,44}, {4,8,45}, {5,8,46}, {10,8,47},
+{11,8,48}, {82,8,49}, {83,8,50}, {84,8,51}, {85,8,52}, {36,8,53},
+{37,8,54}, {88,8,55}, {89,8,56}, {90,8,57}, {91,8,58}, {74,8,59},
+{75,8,60}, {50,8,61}, {51,8,62}, {52,8,63},
+
+/* Make Up White Codes */
+
+{27,5,64}, {18,5,128}, {23,6,192}, {55,7,256}, {54,8,320}, {55,8,384},
+{100,8,448}, {101,8,512}, {104,8,576}, {103,8,640}, {204,9,704}, {205,9,768},
+{210,9,832}, {211,9,896}, {212,9,960}, {213,9,1024},{214,9,1088},{215,9,1152},
+{216,9,1216},{217,9,1280},{218,9,1344},{219,9,1408},{152,9,1472},{153,9,1536},
+{154,9,1600},{24,6,1664}, {155,9,1728},
+
+/* Extended Make Up Codes (Black and White) */
+
+{8,11,1792}, {12,11,1856},{13,11,1920},{18,12,1984},{19,12,2048},{20,12,2112},
+{21,12,2176},{22,12,2240},{23,12,2304},{28,12,2368},{29,12,2432},{30,12,2496},
+{31,12,2560},
+
+{0,0,0} } ;
+
+t4tab btab [ ( 64 + 27 + 13 ) + 1 ] = { /* runs of black */
+
+/* Terminating Black Codes */
+
+{55,10,0}, {2,3,1}, {3,2,2}, {2,2,3}, {3,3,4}, {3,4,5},
+{2,4,6}, {3,5,7}, {5,6,8}, {4,6,9}, {4,7,10}, {5,7,11},
+{7,7,12}, {4,8,13}, {7,8,14}, {24,9,15}, {23,10,16}, {24,10,17},
+{8,10,18}, {103,11,19}, {104,11,20}, {108,11,21}, {55,11,22}, {40,11,23},
+{23,11,24}, {24,11,25}, {202,12,26}, {203,12,27}, {204,12,28}, {205,12,29},
+{104,12,30}, {105,12,31}, {106,12,32}, {107,12,33}, {210,12,34}, {211,12,35},
+{212,12,36}, {213,12,37}, {214,12,38}, {215,12,39}, {108,12,40}, {109,12,41},
+{218,12,42}, {219,12,43}, {84,12,44}, {85,12,45}, {86,12,46}, {87,12,47},
+{100,12,48}, {101,12,49}, {82,12,50}, {83,12,51}, {36,12,52}, {55,12,53},
+{56,12,54}, {39,12,55}, {40,12,56}, {88,12,57}, {89,12,58}, {43,12,59},
+{44,12,60}, {90,12,61}, {102,12,62}, {103,12,63},
+
+/* Make Up Black Codes */
+
+{15,10,64}, {200,12,128},{201,12,192},{91,12,256}, {51,12,320}, {52,12,384},
+{53,12,448}, {108,13,512},{109,13,576},{74,13,640}, {75,13,704}, {76,13,768},
+{77,13,832}, {114,13,896},{115,13,960},{116,13,1024},{117,13,1088},
+{118,13,1152},
+{119,13,1216},{82,13,1280},{83,13,1344},{84,13,1408},{85,13,1472},{90,13,1536},
+{91,13,1600},{100,13,1664},{101,13,1728},
+
+/* Extended Make Up Codes (Black and White) */
+
+{8,11,1792}, {12,11,1856},{13,11,1920},{18,12,1984},{19,12,2048},{20,12,2112},
+{21,12,2176},{22,12,2240},{23,12,2304},{28,12,2368},{29,12,2432},{30,12,2496},
+{31,12,2560},
+
+{0,0,0} } ;
+
+
+/* The built-in 8x16 font. Runs of zeroes are coded as 0
+ followed by the repetition count. */
+
+uchar stdfont [ 1980 ] = {
+0,255,0,255,0,194,8,4,12,10,18,0,3,16,4,8,20,8,4,8,20,0,1,10,8,4,
+4,10,18,0,2,16,4,8,20,4,0,68,20,0,1,8,0,2,12,6,48,0,5,2,0,43,14,32,
+56,0,2,12,0,1,32,0,1,2,0,1,14,0,1,32,8,4,32,56,0,14,6,8,48,0,40,8,
+0,1,18,0,6,30,0,4,4,0,11,4,8,18,20,18,12,0,2,8,8,20,20,4,8,20,20,
+0,1,20,4,8,10,20,18,0,2,8,8,20,20,8,0,1,24,8,4,8,10,20,12,0,2,8,4,
+8,20,16,8,8,20,54,10,8,4,8,10,20,0,2,16,4,8,20,4,0,1,20,0,33,12,20,
+18,28,48,12,12,8,8,8,0,4,2,28,8,28,28,4,62,28,62,28,28,0,5,60,28,
+12,60,14,56,62,30,14,34,62,62,33,16,33,34,12,60,12,60,30,127,34,33,
+65,34,34,62,8,32,8,8,0,1,24,0,1,32,0,1,2,0,1,16,0,1,32,8,4,32,8,0,
+7,16,0,6,8,8,8,0,36,4,14,0,1,34,8,12,18,28,24,0,3,28,0,1,24,0,1,28,
+28,8,0,1,30,0,2,8,28,0,1,100,100,98,0,6,18,31,14,0,8,56,0,7,13,0,
+5,32,36,4,8,20,20,20,18,0,2,4,8,20,20,8,16,20,20,8,20,4,8,20,20,20,
+0,2,8,8,20,20,8,32,20,0,33,12,20,18,42,73,18,24,8,8,42,8,0,3,4,34,
+24,34,34,12,32,34,2,34,34,0,2,2,0,1,16,2,34,12,34,18,36,32,16,18,
+34,8,8,34,16,51,50,18,34,18,34,32,8,34,33,73,34,34,2,8,16,8,8,0,1,
+24,0,1,32,0,1,2,0,1,16,0,1,32,0,2,32,8,0,7,16,0,6,8,8,8,0,36,15,16,
+65,34,8,18,0,1,34,4,0,3,34,0,1,36,8,2,2,0,2,58,0,2,56,34,0,1,36,36,
+18,0,1,12,12,12,12,12,12,24,18,62,62,62,62,62,62,62,62,36,34,12,12,
+12,12,12,0,1,18,34,34,34,34,34,32,36,0,5,12,0,10,52,0,6,8,0,6,32,
+0,34,12,0,1,63,40,74,18,0,1,16,4,20,8,0,3,4,34,40,2,2,20,32,32,2,
+34,34,24,24,4,0,1,8,2,78,18,34,32,34,32,16,32,34,8,8,36,16,51,50,
+33,34,33,34,32,8,34,33,73,20,34,4,8,16,8,20,0,2,28,44,14,30,28,62,
+30,44,56,60,34,8,82,44,28,44,30,22,30,62,34,34,65,34,34,62,8,8,8,
+0,35,12,20,16,62,34,8,16,0,1,77,4,0,3,93,0,1,24,8,2,12,0,1,34,58,
+0,2,8,34,0,1,40,40,100,4,12,12,12,12,12,12,40,32,32,32,32,32,8,8,
+8,8,34,50,18,18,18,18,18,34,35,34,34,34,34,34,60,40,28,28,28,28,28,
+28,54,14,28,28,28,28,56,56,56,56,2,44,28,28,28,28,28,8,29,34,34,34,
+34,34,44,34,0,33,12,0,1,18,24,52,12,0,1,16,4,42,8,0,3,8,34,8,2,2,
+36,60,32,4,34,34,24,24,8,127,4,2,82,18,34,32,34,32,16,32,34,8,8,40,
+16,45,42,33,34,33,34,48,8,34,33,73,20,20,4,8,8,8,20,0,2,34,50,16,
+34,34,16,34,50,8,4,36,8,109,50,34,50,34,24,32,16,34,34,73,34,34,2,
+4,8,16,57,0,34,12,36,16,34,20,0,1,40,0,1,81,28,18,127,0,1,89,0,2,
+127,12,2,0,1,34,58,28,0,1,8,34,36,40,40,24,4,18,18,18,18,18,18,40,
+32,32,32,32,32,8,8,8,8,34,50,33,33,33,33,33,20,37,34,34,34,34,20,
+34,40,34,34,34,34,34,34,9,16,34,34,34,34,8,8,8,8,30,50,34,34,34,34,
+34,0,1,34,34,34,34,34,34,50,34,0,33,12,0,1,18,12,8,25,0,1,16,4,8,
+127,0,1,127,0,1,8,34,8,4,12,68,2,60,8,28,30,0,2,16,0,1,2,28,82,18,
+60,32,34,60,30,32,62,8,8,56,16,45,42,33,34,33,60,28,8,34,18,85,8,
+20,8,8,8,8,34,0,2,2,34,32,34,34,16,34,34,8,4,40,8,73,34,34,34,34,
+16,32,16,34,34,73,20,34,4,24,8,12,78,0,35,36,60,34,62,0,1,36,0,1,
+81,36,36,1,28,85,0,2,8,16,2,0,1,34,26,28,0,1,8,34,18,18,22,106,0,
+1,18,18,18,18,18,18,47,32,60,60,60,60,8,8,8,8,122,42,33,33,33,33,
+33,8,45,34,34,34,34,20,34,36,2,2,2,2,2,2,9,32,34,34,34,34,8,8,8,8,
+34,34,34,34,34,34,34,127,38,34,34,34,34,34,34,34,0,33,8,0,1,63,10,
+22,37,0,1,16,4,0,1,8,0,3,8,34,8,8,2,126,2,34,8,34,2,0,2,8,127,4,16,
+86,63,34,32,34,32,16,34,34,8,8,36,16,45,38,33,60,33,36,6,8,34,18,
+54,20,8,16,8,8,8,34,0,2,30,34,32,34,62,16,34,34,8,4,56,8,73,34,34,
+34,34,16,28,16,34,20,85,8,20,8,4,8,16,0,35,8,36,16,34,8,8,18,0,1,
+81,26,72,1,0,1,34,0,2,8,30,28,0,1,34,10,28,0,1,8,28,9,22,17,22,4,
+63,63,63,63,63,63,120,32,32,32,32,32,8,8,8,8,34,42,33,33,33,33,33,
+20,41,34,34,34,34,8,34,34,30,30,30,30,30,30,63,32,62,62,62,62,8,8,
+8,8,34,34,34,34,34,34,34,0,1,42,34,34,34,34,20,34,20,0,35,18,10,41,
+34,0,1,16,4,0,1,8,0,3,16,34,8,16,2,4,2,34,16,34,2,0,2,4,0,1,8,0,1,
+73,33,34,32,34,32,16,34,34,8,8,34,16,33,38,33,32,33,34,2,8,34,18,
+34,20,8,16,8,4,8,0,3,34,34,32,34,32,16,38,34,8,4,36,8,73,34,34,34,
+34,16,2,16,34,20,34,20,20,16,8,8,8,0,35,12,20,16,62,62,8,10,0,1,77,
+0,1,36,1,0,1,28,0,6,34,10,0,4,18,42,34,42,28,33,33,33,33,33,33,72,
+32,32,32,32,32,8,8,8,8,34,38,33,33,33,33,33,34,49,34,34,34,34,8,60,
+34,34,34,34,34,34,34,72,32,32,32,32,32,8,8,8,8,34,34,34,34,34,34,
+34,8,50,34,34,34,34,20,34,20,0,33,12,0,1,18,42,73,34,0,1,8,8,0,1,
+8,12,0,1,24,16,34,8,32,34,4,34,34,16,34,34,24,24,2,0,1,16,16,32,33,
+34,16,36,32,16,18,34,8,8,33,16,33,34,18,32,18,34,2,8,34,12,34,34,
+8,32,8,4,8,0,3,34,34,16,38,34,16,26,34,8,4,34,8,73,34,34,34,38,16,
+2,16,38,8,34,34,8,32,8,8,8,0,35,12,15,16,65,8,8,4,0,1,34,0,1,18,0,
+5,127,0,3,54,10,0,4,36,79,68,79,32,33,33,33,33,33,33,72,16,32,32,
+32,32,8,8,8,8,36,38,18,18,18,18,18,0,1,18,34,34,34,34,8,32,34,34,
+34,34,34,34,34,72,16,34,34,34,34,8,8,8,8,34,34,34,34,34,34,34,8,34,
+38,38,38,38,8,34,8,0,33,12,0,1,18,28,6,29,0,1,8,8,0,2,12,0,1,24,32,
+28,8,62,28,4,28,28,16,28,28,24,24,0,3,16,28,33,60,14,56,62,16,14,
+34,62,112,33,30,33,34,12,32,12,34,60,8,28,12,34,34,8,62,8,2,8,0,3,
+29,60,14,26,28,16,2,34,8,4,33,8,73,34,28,60,26,16,60,14,26,8,34,34,
+8,62,8,8,8,0,35,12,4,62,0,1,8,8,36,0,1,28,0,11,42,10,0,5,66,71,66,
+32,33,33,33,33,33,33,79,14,62,62,62,62,62,62,62,62,56,34,12,12,12,
+12,12,0,1,44,28,28,28,28,8,32,36,29,29,29,29,29,29,55,14,28,28,28,
+28,8,8,8,8,28,34,28,28,28,28,28,0,1,92,26,26,26,26,8,60,8,0,36,8,
+0,3,6,48,0,2,24,0,2,32,0,11,48,0,21,6,0,9,14,2,56,0,1,127,0,7,2,0,
+2,4,0,5,32,2,0,7,16,0,1,6,8,48,0,35,12,0,4,8,24,0,13,32,10,0,1,4,
+0,6,32,0,7,4,0,31,4,0,21,16,32,16,0,81,3,0,21,28,0,2,56,0,5,32,2,
+0,7,48,0,39,12,0,19,32,0,2,24,0,6,30,0,7,24,0,31,24,0,21,48,32,48,
+0,255,0,1
+} ;