/*
  pbmtopk, adapted from "pxtopk.c by tomas rokicki" by AJCD 1/8/90
  
  compile with: cc -o pbmtopk pbmtopk.c -lm -lpbm
  */

#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include "pbm.h"

#define MAXPKCHAR 256
#define MAXOPTLINE 200
#define MAXWIDTHTAB 256
#define MAXHEIGHTTAB 16
#define MAXDEPTHTAB 16
#define MAXITALICTAB 64
#define MAXPARAMS 30
#define NAMELENGTH 80

#define fixword(d) ((int)((double)(d)*1048576))
#define unfixword(f) ((double)(f) / 1048576)
#define fixrange(f) ((f) < 16777216 && (f) > -16777216)
#define designunits(p) ((p)*72.27/(double)resolution/unfixword(designsize))

/* character flags: in order of appearance in option files. */
#define XOFFSET     1
#define YOFFSET     2
#define HORZESC     4
#define VERTESC     8
#define TFMWIDTH   16
#define TFMHEIGHT  32
#define TFMDEPTH   64
#define TFMITALIC 128

typedef int integer ;
typedef char quarterword ;
typedef char boolean ;
typedef quarterword ASCIIcode ;
typedef quarterword eightbits ;
typedef unsigned char byte ;

integer resolution, designsize ;
char *filename[MAXPKCHAR] ;

integer xoffset[MAXPKCHAR] ;
integer yoffset[MAXPKCHAR] ;
integer horzesc[MAXPKCHAR] ;
integer vertesc[MAXPKCHAR] ;

byte tfmindex[MAXPKCHAR] ;
byte hgtindex[MAXPKCHAR] ;
byte depindex[MAXPKCHAR] ;
byte italindex[MAXPKCHAR] ;
byte charflags[MAXPKCHAR] ;

bit **bitmap ;
integer smallestch = MAXPKCHAR ;
integer largestch = -1;
integer emwidth  = 0;
integer checksum ;
char *codingscheme = "GRAPHIC" ;
char *familyname = "PBM" ;

integer widthtab[MAXWIDTHTAB] = {0}; /* TFM widths */
integer numwidth = 1;      /* number of entries in width table */
integer heighttab[MAXHEIGHTTAB]  = {0};
integer numheight = 1;
integer depthtab[MAXDEPTHTAB] = {0};
integer numdepth = 1;
integer italictab[MAXITALICTAB] = {0};
integer numitalic = 1;
integer parameters[MAXPARAMS] = {0};
integer numparam = 0;

static ASCIIcode xord[128] ;
static char xchr[256] = {
   '?', '?', '?', '?', '?', '?', '?', '?',
   '?', '?', '?', '?', '?', '?', '?', '?',
   '?', '?', '?', '?', '?', '?', '?', '?',
   '?', '?', '?', '?', '?', '?', '?', '?',
   ' ', '!', '"', '#', '$', '%', '&', '\'',
   '(', ')', '*', '+', ',', '-', '.', '/',
   '0', '1', '2', '3', '4', '5', '6', '7',
   '8', '9', ':', ';', '<', '=', '>', '?',
   '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
   'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
   'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
   'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
   '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
   'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
   'x', 'y', 'z', '{', '|', '}', '~', '?' };

static FILE *tfmfile, *pkfile ;
static char tfmname[NAMELENGTH+1], pkname[NAMELENGTH+1] ;
static integer pbmtopk_pkloc = 0 ;
static integer bitweight ;
static integer outputbyte ;
static integer car ;
static integer hppp ;
static integer width ;
static integer height ;

/* checksum not computed at the moment
static integer compute_checksum()
{
     begin
     c0:=bc; c1:=ec; c2:=bc; c3:=ec;
     for c:=bc to ec do if char_wd[c]>0 then
     begin
     temp_width:=memory[char_wd[c]];
     if design_units<>unity then
     temp_width:=round((temp_width/design_units)*1048576.0);
     temp_width:=temp_width + (c+4)*@'20000000; {this should be positive}
     c0:=(c0+c0+temp_width) mod 255;
     c1:=(c1+c1+temp_width) mod 253;
     c2:=(c2+c2+temp_width) mod 251;
     c3:=(c3+c3+temp_width) mod 247;
     end;
     header_bytes[check_sum_loc]:=c0;
     header_bytes[check_sum_loc+1]:=c1;
     header_bytes[check_sum_loc+2]:=c2;
     header_bytes[check_sum_loc+3]:=c3;
     end
}
*/

#define add_tfmwidth(v) (add_tfmtable(widthtab, &numwidth, v, MAXWIDTHTAB,\
				      "TFM width"))
#define add_tfmheight(v) (add_tfmtable(heighttab, &numheight, v, MAXHEIGHTTAB,\
				       "TFM height"))
#define add_tfmdepth(v) (add_tfmtable(depthtab, &numdepth, v, MAXDEPTHTAB,\
				      "TFM depth"))
#define add_tfmitalic(v) (add_tfmtable(italictab, &numitalic, v, MAXITALICTAB,\
				       "Italic correction"))
static byte
add_tfmtable(table, count, value, max_count, name)
     integer *table, *count, value, max_count;
     char *name;
{
   integer i;
   for (i = 0; i < *count; i++) /* search for value in tfm table */
      if (table[i] == value) return (byte)i;
   if (*count >= max_count)
      pm_error("too many values in %s table", name) ;
   if (!fixrange(value))
      pm_error("%s %f for char %d out of range",
	       name, unfixword(value), car);
   table[*count] = value ;
   return (*count)++ ;
}

/* add a suffix to a filename in an allocated space */
static void pbmtopk_add_suffix(name, suffix)
     char *name, *suffix ;
{
   char *slash = strrchr(name, '/');
   char *dot = strrchr(name, '.');

   if ((dot && slash ? dot < slash : !dot) && strcmp(name, "-"))
      strcat(name, suffix);
}

/* initialize the PK parameters */
static void initialize_pk()
{
   integer i ;
   pm_message("This is PBMtoPK, version 2.4") ;
   for (i = 127 ; i <= 255 ; i ++) xchr[i] = '?' ;
   for (i = 0 ; i <= 127 ; i ++) xord[i] = 32 ;
   for (i = 32 ; i < 127 ; i ++) xord[(int)xchr[i]] = i ;
   for (i = 0; i < MAXPKCHAR; i++) {
      filename[i] = NULL;
      charflags[i] = 0;
   }
   designsize = fixword(1.0) ;
}

/* write a single byte to the PK file */
static void pbmtopk_pkbyte(b)
     integer b ;
{
   if (b < 0) b += 256 ;
   putc(b, pkfile) ;
   pbmtopk_pkloc++ ;
}

/* write two bytes to the PK file */
static void pkhalfword(a)
     integer a ;
{
   if (a < 0) a += 65536 ;
   pbmtopk_pkbyte(a >> 8) ;
   pbmtopk_pkbyte(a & 255) ;
}

/* write three bytes to the PK file */
static void pkthreebytes(a)
     integer a ;
{
   pbmtopk_pkbyte((a>>16) & 255) ;
   pbmtopk_pkbyte((a>>8) & 255) ;
   pbmtopk_pkbyte(a & 255) ;
}

/* write four bytes to the PK file */
static void pkword(a)
     integer a ;
{
   pbmtopk_pkbyte((a>>24) & 255) ;
   pbmtopk_pkbyte((a>>16) & 255) ;
   pbmtopk_pkbyte((a>>8) & 255) ;
   pbmtopk_pkbyte(a & 255) ;
}

/* write a nibble to the PK file */
static void pknyb(a)
     integer a ;
{
   if (bitweight == 16) {
      outputbyte = (a<<4) ;
      bitweight = 1 ;
   } else {
      pbmtopk_pkbyte(outputbyte + a) ;
      bitweight = 16 ;
   }
}

/* write preamble to PK file */
static void writepreamble()
{
   integer i ;
   char *comment = "PBMtoPK 2.4 output" ;
   
   pbmtopk_pkbyte(247) ;				/* PRE command */
   pbmtopk_pkbyte(89) ;				/* PK file type */
   pbmtopk_pkbyte(strlen(comment)) ;			/* output comment */
   for (i = 0 ; i < strlen(comment); i++) pbmtopk_pkbyte(xord[(int)comment[i]]) ;
   pkword(designsize) ;				/* write designsize */
   pkword(checksum) ;		/* write checksum; calculate if possible */
   pkword(hppp) ;				/* write H pixels per point */
   pkword(hppp) ;				/* write V pixels per point */
}

/* write postamble to PK file, padded to word length */
static void writepostamble()
{
   pbmtopk_pkbyte(245) ;				/* POST command */
   while (pbmtopk_pkloc % 4)
      pbmtopk_pkbyte(246) ;				/* pad with no-ops */
   pm_message("%d bytes written to packed file.", pbmtopk_pkloc) ;
}

/* write a byte to the TFM file */
static void tfmbyte(b)
     integer b ;
{
   if (b < 0) b += 256 ;
   putc(b, tfmfile) ;
}

/* write a half word to the TFM file */
static void tfmhalfword(a)
     integer a ;
{
   if (a < 0) a += 65536 ;
   tfmbyte(a >> 8) ;
   tfmbyte(a & 255) ;
}

/* write a word to the TFM file */
static void tfmword(a)
     integer a ;
{
   tfmbyte((a>>24) & 255) ;
   tfmbyte((a>>16) & 255) ;
   tfmbyte((a>>8) & 255) ;
   tfmbyte(a & 255) ;
}

/* write the whole TFM file for the font */
static void writetfmfile()
{
   integer totallength ;
   integer headersize = 17;
   integer i ;
   
   if (largestch - smallestch < 0) {
      largestch = 0;
      smallestch = 1;
   }
   if (numparam < 7) /* set default parameters */
      switch (numparam) {
      case 0: /* slant */
	 parameters[numparam++] = 0 ;
      case 1: /* space */
	 parameters[numparam++] = fixword(designunits(emwidth/3.0));
      case 2: /* space_stretch */
	 parameters[numparam++] = fixword(unfixword(parameters[1])/2.0) ;
      case 3: /* space_shrink */
	 parameters[numparam++] = fixword(unfixword(parameters[1])/3.0) ;
      case 4: /* x_height */
	 parameters[numparam++] = fixword(0.45);
      case 5: /* quad */
	 parameters[numparam++] = fixword(designunits(emwidth)) ;
      case 6: /* extra_space */
	 parameters[numparam++] = fixword(unfixword(parameters[1])/3.0) ;
      }
   totallength = 6 + headersize + (largestch+1-smallestch) +
      numwidth + numheight + numdepth + numitalic + numparam ;
   /* lengths */
   tfmhalfword(totallength) ;			/* write file TFM length */
   tfmhalfword(headersize) ;			/* write TFM header length */
   tfmhalfword(smallestch) ;			/* write lowest char index */
   tfmhalfword(largestch) ;			/* write highest char index */
   tfmhalfword(numwidth) ;			/* write number of widths */
   tfmhalfword(numheight) ;			/* write number of heights */
   tfmhalfword(numdepth) ;			/* write number of depths */
   tfmhalfword(numitalic) ;			/* write number of italcorrs */
   tfmhalfword(0) ;				/* lig/kern table */
   tfmhalfword(0) ;				/* kern table */
   tfmhalfword(0) ;				/* extensible char table */
   tfmhalfword(numparam) ;			/* number of fontdimens */
   /* header */
   tfmword(checksum) ;				/* write checksum */
   tfmword(designsize) ;			/* write designsize */
   if (strlen(codingscheme) > 39) tfmbyte(39) ;	/* write coding scheme len */
   else tfmbyte(strlen(codingscheme)) ;
   for (i = 0; i < 39; i++)			/* write coding scheme */
      if (*codingscheme) tfmbyte(xord[(int)(*codingscheme++)]) ;
      else tfmbyte(0) ;
   if (strlen(familyname) > 19) tfmbyte(19) ;	/* write family length */
   else tfmbyte(strlen(familyname)) ;
   for (i = 0; i < 19; i++)			/* write family */
      if (*familyname) tfmbyte(xord[(int)(*familyname++)]) ;
      else tfmbyte(0) ;
   /* char_info */
   for (car = smallestch; car <= largestch; car++)
      if (filename[car]) {			/* write character info */
	 tfmbyte(tfmindex[car]) ;
	 tfmbyte((hgtindex[car]<<4) + depindex[car]) ;
	 tfmbyte(italindex[car]<<2) ;
	 tfmbyte(0) ;
      } else tfmword(0) ;
   /* width table */
   for (i = 0; i < numwidth; i++) tfmword(widthtab[i]) ;
   /* height table */
   for (i = 0; i < numheight; i++) tfmword(heighttab[i]) ;
   /* depth table */
   for (i = 0; i < numdepth; i++) tfmword(depthtab[i]) ;
   /* italic correction table */
   for (i = 0; i < numitalic; i++) tfmword(italictab[i]) ;
   /* no lig_kern, kern, or exten tables */
   /* fontdimen table */
   for (i = 0; i < numparam; i++)
      if (i && !fixrange(parameters[i]))
	 pm_error("parameter %d out of range (-p)", i);
      else
	 tfmword(parameters[i]) ;
   pm_message("%d bytes written to tfm file.", totallength*4) ;
}

/* read a character from a PBM file */
static void readcharacter()
{
   FILE *fp;
   
   fp = pm_openr(filename[car]);
   bitmap = pbm_readpbm(fp, &width, &height) ;
   pm_close(fp) ;
   
   if ((charflags[car] & HORZESC) == 0) horzesc[car] = width ;
   if ((charflags[car] & VERTESC) == 0) vertesc[car] = 0;
   if ((charflags[car] & XOFFSET) == 0) xoffset[car] = 0;
   if ((charflags[car] & YOFFSET) == 0) yoffset[car] = height-1;
   if ((charflags[car] & TFMWIDTH) == 0)
      tfmindex[car] = add_tfmwidth(fixword(designunits(width)));
   if ((charflags[car] & TFMHEIGHT) == 0)
      hgtindex[car] = add_tfmheight(fixword(designunits(yoffset[car]+1)));
   if ((charflags[car] & TFMDEPTH) == 0)
      depindex[car] = add_tfmdepth(fixword(designunits(height-1-yoffset[car])));
   if ((charflags[car] & TFMITALIC) == 0) italindex[car] = 0;
   
   if (car < smallestch) smallestch = car;
   if (car > largestch) largestch = car;
   if (width > emwidth) emwidth = width ;
}

/* test if two rows of the PBM are the same */
static int equal(row1, row2)
     bit *row1, *row2 ;
{
   integer i ;
   
   for (i = 0; i < width; i++)
      if (row1[i] != row2[i]) return (0) ;
   return(1) ;
}

static void shipcharacter()
{
   integer compsize ;
   integer i, j, k ;
   bit *zerorow, *onesrow ;
   integer *repeatptr, *bitcounts ;
   integer count ;
   integer test ;
   integer curptr, rowptr ;
   integer bitval ;
   integer repeatflag ;
   integer colptr ;
   integer currepeat ;
   integer dynf ;
   integer deriv[14] ;
   integer bcompsize ;
   boolean firston ;
   integer flagbyte ;
   boolean state ;
   boolean on ;
   integer hbit ;
   integer pbit ;
   boolean ron, son ;
   integer rcount, scount ;
   integer ri, si ;
   integer max2 ;
   integer predpkloc ;
   integer buff ;
   
   integer tfwid = widthtab[tfmindex[car]] ;
   integer hesc = horzesc[car] ;
   integer vesc = vertesc[car] ;
   integer xoff = xoffset[car] ;
   integer yoff = yoffset[car] ;
   
   repeatptr =
      (integer *)malloc((unsigned int)((height+1)*sizeof(integer))) ;
   bitcounts =
      (integer *)malloc((unsigned int)((height*width)*sizeof(integer))) ;
   if (repeatptr == NULL || bitcounts == NULL)
      pm_error("out of memory while allocating bit counts");
   zerorow = pbm_allocrow(width) ;		/* initialise plain rows */
   onesrow = pbm_allocrow(width) ;
   for (i = 0 ; i < width ; i++) {
      zerorow[i] = PBM_WHITE ;
      onesrow[i] = PBM_BLACK ;
   }
   for (i=0; i < height; i = k) {		/* set repeat pointers */
      k = i + 1;
      if (!equal(bitmap[i], zerorow) && !equal(bitmap[i], onesrow)) {
	 while (k < height && equal(bitmap[i], bitmap[k]))
	    k++;
	 repeatptr[i] = k - i - 1;
      } else {
	 repeatptr[i] = 0;
      }
   }
   repeatptr[height] = 0 ;
   colptr = width - 1 ;
   repeatflag = currepeat = curptr = count = rowptr = 0 ;
   test = PBM_WHITE ;
   do {
      colptr++ ;
      if (colptr == width) {			/* end of row, get next row */
	 colptr = 0 ;
	 rowptr = currepeat ;
	 if (repeatptr[currepeat] > 0) {
	    repeatflag = repeatptr[currepeat] ;
	    currepeat += repeatflag ;
	    rowptr += repeatflag ;
	 }
	 currepeat++ ;
      }
      if (rowptr >= height) bitval = -1 ;
      else bitval = bitmap[rowptr][colptr] ;
      if (bitval == test) count++ ;		/* count repeated pixels */
      else {					/* end of pixel run */
	 bitcounts[curptr++] = count ;
	 if (curptr+3 >= height*width)
	    pm_error("out of memory while saving character counts");
	 count = 1 ;
	 test = bitval ;
	 if (repeatflag > 0) {
	    bitcounts[curptr++] = -repeatflag ;
	    repeatflag = 0 ;
	 }
      }
   } while (test != -1) ;
   bitcounts[curptr] = 0 ;
   bitcounts[curptr + 1] = 0 ;
   for (i = 1 ; i <= 13 ; i ++) deriv[i] = 0 ;
   i = firston = (bitcounts[0] == 0) ;
   compsize = 0 ;
   while (bitcounts[i] != 0) {			/* calculate dyn_f */
      j = bitcounts[i] ;
      if (j == -1) compsize++ ;
      else {
	 if (j < 0) {
	    compsize++ ;
	    j = -j ;
	 }
	 if (j < 209) compsize += 2 ;
	 else {
	    k = j - 193 ;
	    while (k >= 16) {
	       k >>= 4 ;
	       compsize += 2 ;
	    }
	    compsize++ ;
	 }
	 if (j < 14) (deriv[j])-- ;
	 else if (j < 209) (deriv[(223 - j) / 15])++ ;
	 else {
	    k = 16 ;
	    while (((k<<4) < j + 3)) k <<= 4 ;
	    if (j - k <= 192)
	       deriv[(207 - j + k) / 15] += 2 ;
	 }
      }
      i++ ;
   }
   bcompsize = compsize ;
   dynf = 0 ;
   for (i = 1 ; i <= 13 ; i ++) {
      compsize += deriv[i] ;
      if (compsize <= bcompsize) {
	 bcompsize = compsize ;
	 dynf = i ;
      }
   }
   compsize = ((bcompsize + 1)>>1) ;
   if ((compsize > ((height*width+7)>>3)) || (height*width == 0)) {
      compsize = ((height*width+7)>>3) ;
      dynf = 14 ;
   }
   flagbyte = (dynf<<4) ;
   if (firston) flagbyte |= 8 ;
   if (tfwid > 16777215 || tfwid < 0 || hesc < 0 || vesc != 0 ||
       compsize > 196579 || width > 65535 || height > 65535 ||
       xoff > 32767 || yoff > 32767 || xoff < -32768 || yoff < -32768) {
      flagbyte |= 7 ;				/* long form preamble */
      pbmtopk_pkbyte(flagbyte) ;
      compsize += 28 ;
      pkword(compsize) ;			/* char packet size */
      pkword(car) ;				/* character number */
      predpkloc = pbmtopk_pkloc + compsize ;
      pkword(tfwid) ;				/* TFM width */
      pkword(hesc<<16) ;			/* horiz escapement */
      pkword(vesc<<16) ;			/* vert escapement */
      pkword(width) ;				/* bounding box width */
      pkword(height) ;				/* bounding box height */
      pkword(xoff) ;				/* horiz offset */
      pkword(yoff) ;				/* vert offset */
   } else if (hesc > 255 || width > 255 || height > 255 ||
	      xoff > 127 || yoff > 127 || xoff < -128 ||
	      yoff < -128 || compsize > 1016) {
      compsize += 13 ;				/* extended short preamble */
      flagbyte += (compsize>>16) + 4 ;
      pbmtopk_pkbyte(flagbyte) ;
      pkhalfword(compsize & 65535) ;		/* char packet size */
      pbmtopk_pkbyte(car) ;				/* character number */
      predpkloc = pbmtopk_pkloc + compsize ;
      pkthreebytes(tfwid) ;			/* TFM width */
      pkhalfword(hesc) ;			/* horiz escapement */
      pkhalfword(width) ;			/* bounding box width */
      pkhalfword(height) ;			/* bounding box height */
      pkhalfword(xoff) ;			/* horiz offset */
      pkhalfword(yoff) ;			/* vert offset */
   } else {
      compsize += 8 ;				/* short form preamble */
      flagbyte = flagbyte + (compsize>>8) ;
      pbmtopk_pkbyte(flagbyte) ;
      pbmtopk_pkbyte(compsize & 255) ;			/* char packet size */
      pbmtopk_pkbyte(car) ;				/* character number */
      predpkloc = pbmtopk_pkloc + compsize ;
      pkthreebytes(tfwid) ;			/* TFM width */
      pbmtopk_pkbyte(hesc) ;				/* horiz escapement */
      pbmtopk_pkbyte(width) ;				/* bounding box width */
      pbmtopk_pkbyte(height) ;				/* bounding box height */
      pbmtopk_pkbyte(xoff) ;				/* horiz offset */
      pbmtopk_pkbyte(yoff) ;				/* vert offset */
   }
   if (dynf != 14) {				/* write packed character */
      bitweight = 16 ;
      max2 = 208 - 15 * dynf ;
      i = firston ;
      while (bitcounts[i] != 0) {
	 j = bitcounts[i] ;
	 if (j == - 1) pknyb(15) ;
	 else {
	    if (j < 0) {
	       pknyb(14) ;
	       j = -j ;
	    }
	    if (j <= dynf) pknyb(j) ;
	    else if (j <= max2) {
	       j -= dynf + 1 ;
	       pknyb((j >> 4) + dynf + 1) ;
	       pknyb((j & 15)) ;
	    } else {
	       j -= max2 - 15 ;
	       k = 16 ;
	       while (k <= j) {
		  k <<= 4 ;
		  pknyb(0) ;
	       }
	       while (k > 1) {
		  k >>= 4 ;
		  pknyb(j / k) ;
		  j = j % k ;
	       }
	    }
	 }
	 i++ ;
      }
      if (bitweight != 16) pbmtopk_pkbyte(outputbyte) ;
   } else {					/* write bitmap character */
      buff = 0 ;
      pbit = 8 ;
      i = firston ;
      hbit = width ;
      on = ! firston ;
      state = 0 ;
      count = repeatflag = 0 ;
      while ((bitcounts[i] != 0) || state || (count > 0)) {
	 if (state) {
	    count = rcount ;
	    i = ri ;
	    on = ron ;
	    repeatflag-- ;
	 } else {
	    rcount = count ;
	    ri = i ;
	    ron = on ;
	 }
	 do {
	    if (count == 0) {
	       if (bitcounts[i] < 0) {
		  if (! state) repeatflag = -bitcounts[i] ;
		  i++ ;
	       }
	       count = bitcounts[i] ;
	       i++ ;
	       on = !on ;
	    }
	    if ((count >= pbit) && (pbit < hbit)) {
	       if (on) buff += (1 << pbit) - 1 ;
	       pbmtopk_pkbyte(buff) ;
	       buff = 0 ;
	       hbit -= pbit ;
	       count -= pbit ;
	       pbit = 8 ;
	    } else if ((count < pbit) && (count < hbit)) {
	       if (on) buff += (1 << pbit) - (1 << (pbit - count)) ;
	       pbit -=  count ;
	       hbit -= count ;
	       count = 0 ;
	    } else {
	       if (on) buff += (1 << pbit) - (1 << (pbit - hbit)) ;
	       count -= hbit ;
	       pbit -= hbit ;
	       hbit = width ;
	       if (pbit == 0) {
		  pbmtopk_pkbyte(buff) ;
		  buff = 0 ;
		  pbit = 8 ;
	       }
	    }
	 } while (hbit != width) ;
	 if (state && (repeatflag == 0)) {
	    count = scount ;
	    i = si ;
	    on = son ;
	    state = 0 ;
	 } else if (! state && (repeatflag > 0)) {
	    scount = count ;
	    si = i ;
	    son = on ;
	    state = 1 ;
	 }
      }
      if (pbit != 8) pbmtopk_pkbyte(buff) ;
   }
   if (predpkloc != pbmtopk_pkloc)
      pm_error("bad predicted character length: character %d", car);
   pbm_freerow(zerorow); 
   pbm_freerow(onesrow); 
   free((char *)repeatptr);
   free((char *)bitcounts);
}

/* check that character is in valid range */
static void checkchar()
{
   if (car < 0 || car >= MAXPKCHAR)
      pm_error("character must be in range 0 to %d", MAXPKCHAR-1) ;
}

/* read character information from an option file */
static void optionfile(name)
     char *name ;
{
   FILE *fp ;
   char buffer[MAXOPTLINE] ;
   
   fp = pm_openr(name);
   while (!feof(fp)) {
      char *here = buffer;
      
      if (fgets(buffer, MAXOPTLINE, fp) == NULL) break ;
      while (isspace(*here)) here++ ;
      if (*here && *here == '=') {
	 if (sscanf(here+1, "%d", &car) != 1)
	    pm_error("bad option file line %s", buffer) ;
      } else if (*here && *here != '%' && *here != '#') {
	 char str[NAMELENGTH] ;
	 integer i, n;
	 
	 checkchar() ;
	 if (sscanf(here, "%s%n", str, &n) != 1)
	    pm_error("bad option file line %s", buffer) ;
	 filename[car] =
	    (char *)malloc((unsigned int)(sizeof(char)*(strlen(str)+1))) ;
	 if (filename[car] == NULL)
	    pm_error("out of memory allocating filename %s", str);
	 strcpy(filename[car], str) ;
	 for (i = 1; i < 256; i<<=1) {
	    here += n;
	    if (sscanf(here, "%s%n", str, &n) != 1) break ;
	    if (strcmp(str, "*")) {
	       charflags[car] |= i ;
	       switch (i) {
	       case XOFFSET:
		  xoffset[car] = atoi(str) ;
		  break ;
	       case YOFFSET:
		  yoffset[car] = atoi(str) ;
		  break ;
	       case HORZESC:
		  horzesc[car] = atoi(str) ;
		  break ;
	       case VERTESC:
		  vertesc[car] = atoi(str) ;
		  break ;
	       case TFMWIDTH:
		  tfmindex[car] = add_tfmwidth(fixword(atof(str))) ;
		  break ;
	       case TFMHEIGHT:
		  hgtindex[car] = add_tfmheight(fixword(atof(str))) ;
		  break ;
	       case TFMDEPTH:
		  depindex[car] = add_tfmdepth(fixword(atof(str))) ;
		  break ;
	       case TFMITALIC:
		  italindex[car] = add_tfmitalic(fixword(atof(str))) ;
		  break ;
	       }
	    }
	 }
	 car++ ;
      }
   }
   pm_close(fp) ;
}

int
main(argc, argv)
     int argc ;
     char *argv[] ;
{
   integer i, hesc, vesc, xoff, yoff, tfwid, tfdep, tfhgt, tfital ;
   byte flags ;
   char *usage = "pkfile[.pk] tfmfile[.tfm] dpi [-s designsize] [-p num param...]\n\
        [-C codingscheme ] [-F family] [-c num | <char>]...\n\
   <char> is:\n\
        [-W tfmwidth] [-H tfmheight] [-D tfmdepth] [-I ital_corr] [-h horiz]\n\
        [-v vert] [-x xoffset] [-y yoffset] file\n\
    or:\n\
        -f optfile\n" ;

   pbm_init(&argc, argv);
   initialize_pk() ;
   
   if (--argc < 1) pm_usage(usage) ;
   strcpy(pkname, *++argv) ;
   pbmtopk_add_suffix(pkname, ".pk") ;
   
   if (--argc < 1) pm_usage(usage) ;
   strcpy(tfmname, *++argv) ;
   pbmtopk_add_suffix(tfmname, ".tfm") ;
   
   if (--argc < 1) pm_usage(usage) ;
   resolution = atoi(*++argv) ;
   if (resolution < 1 || resolution > 32767)
      pm_error("unlikely resolution %d dpi", resolution);
   
   car = flags = hesc = vesc = xoff = yoff = tfwid = 0;
   while (++argv, --argc) {
      if (argv[0][0] == '-' && argv[0][1]) {
	 char c, *p;
	 c = argv[0][1] ;
	 if (argv[0][2]) p = *argv + 2 ;	/* set argument pointer */
	 else if (++argv, --argc) p = *argv ;
	 else pm_usage(usage) ;
	 switch (c) {
	 case 'C':
	    codingscheme = p;
	    break ;
	 case 'F':
	    familyname = p;
	    break ;
	 case 'c':
	    car = atoi(p) ;
	    break ;
	 case 's':
	    designsize = fixword(atof(p));
	    if (designsize < 1048576)
	       pm_error("design size %f out of range", unfixword(designsize));
	 case 'h':
	    hesc = atoi(p) ;
	    flags |= HORZESC ;
	    break ;
	 case 'v':
	    vesc = atoi(p) ;
	    flags |= VERTESC ;
	    break ;
	 case 'x':
	    xoff = atoi(p) ;
	    flags |= XOFFSET ;
	    break ;
	 case 'y':
	    yoff = atoi(p) ;
	    flags |= YOFFSET ;
	    break ;
	 case 'W':
	    tfwid = fixword(atof(p)) ;
	    flags |= TFMWIDTH ;
	    break ;
	 case 'H':
	    tfhgt = fixword(atof(p)) ;
	    flags |= TFMHEIGHT ;
	    break ;
	 case 'D':
	    tfdep = fixword(atof(p)) ;
	    flags |= TFMDEPTH ;
	    break ;
	 case 'I':
	    tfital = fixword(atof(p)) ;
	    flags |= TFMITALIC ;
	    break ;
	 case 'f':
	    optionfile(p) ;
	    break ;
	 case 'p':
	    numparam = atoi(p);
	    if (numparam < 1 || numparam > MAXPARAMS)
	       pm_error("parameter count %d out of range", numparam);
	    for (i=0; i<numparam; i++)
	       if (++argv,--argc)
		  parameters[i] = fixword(atof(*argv)) ;
	       else
		  pm_error("not enough parameters (-p)");
	    break ;
	 default:
	    pm_usage(usage) ;
	 }
      } else  {
	 checkchar() ;
	 if (flags & TFMWIDTH)
	    tfmindex[car] = add_tfmwidth(tfwid);
	 if (flags & TFMDEPTH)
	    depindex[car] = add_tfmdepth(tfdep);
	 if (flags & TFMHEIGHT)
	    hgtindex[car] = add_tfmheight(tfhgt);
	 if (flags & TFMITALIC)
	    italindex[car] = add_tfmitalic(tfital);
	 horzesc[car] = hesc ;
	 vertesc[car] = vesc ;
	 xoffset[car] = xoff ;
	 yoffset[car] = yoff ;
	 filename[car] = *argv ;
	 charflags[car] = flags ;
	 car++ ;
	 flags = 0;
      }
   }
   hppp = ROUND((resolution<<16) / 72.27) ;
   pkfile = pm_openw(pkname);
   tfmfile = pm_openw(tfmname);
   writepreamble() ;
   for (car = 0 ; car < MAXPKCHAR ; car++)
      if (filename[car]) {
	 readcharacter() ;
	 shipcharacter() ;
      }
   writepostamble() ;
   writetfmfile() ;
   pm_close(pkfile) ;
   pm_close(tfmfile) ;
   exit(0);
}

