/*
  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 <pbm.h>
#include <ctype.h>

extern double atof();

#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 true (1)
#define false (0)
#define chr(a) a
  
#define round(a) ((int)(a+.5))
#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 FILE *bytefile ;
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, largestch ;
integer emwidth ;
integer checksum ;
char *codingscheme = "GRAPHIC" ;
char *familyname = "PBM" ;

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

ASCIIcode xord[128] ;
char xchr[256] ;
bytefile tfmfile, pkfile ;
char tfmname[NAMELENGTH+1], pkname[NAMELENGTH+1] ;
integer pkloc ;
integer bitweight ;
integer outputbyte ;
integer car ;
integer hppp ;
integer width ;
integer height ;
integer power[32] ;

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"))
byte
add_tfmtable(table, count, value, max, name)
     integer *table, *count, value, max;
     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) {
    fprintf(stderr, "Too many values in %s table!\n", name) ;
    jumpout();
  }
  if (!fixrange(value)) {
    fprintf(stderr, " %s %f for char %d out of range!\n",
	    name, unfixword(value), car);
    jumpout();
  }
  table[*count] = value ;
  return (*count)++ ;
}

add_suffix(name, suffix)
     char *name, *suffix ;
{
  int haveext = 0;
  if (name && strcmp(name, "-")) {
    while (*name) {
      if (*name == '/') haveext = 0 ;
      else if (*name == '.') haveext = 1 ;
      name++ ;
    }
    if (!haveext) {
      *name++ = '.';
      strcpy(name,suffix) ;
    }
  }
}

initialize()
{
  integer i ;
  fprintf(stderr, "This is PBMtoPK, C Version 2.3\n") ;
  for (i = 0 ; i <= 31 ; i ++) xchr[i] = '?' ;
  xchr[32] = ' ' ;
  xchr[33] = '!' ;
  xchr[34] = '"' ;
  xchr[35] = '#' ;
  xchr[36] = '$' ;
  xchr[37] = '%' ;
  xchr[38] = '&' ;
  xchr[39] = '\'' ;
  xchr[40] = '(' ;
  xchr[41] = ')' ;
  xchr[42] = '*' ;
  xchr[43] = '+' ;
  xchr[44] = ',' ;
  xchr[45] = '-' ;
  xchr[46] = '.' ;
  xchr[47] = '/' ;
  xchr[48] = '0' ;
  xchr[49] = '1' ;
  xchr[50] = '2' ;
  xchr[51] = '3' ;
  xchr[52] = '4' ;
  xchr[53] = '5' ;
  xchr[54] = '6' ;
  xchr[55] = '7' ;
  xchr[56] = '8' ;
  xchr[57] = '9' ;
  xchr[58] = ':' ;
  xchr[59] = ';' ;
  xchr[60] = '<' ;
  xchr[61] = '=' ;
  xchr[62] = '>' ;
  xchr[63] = '?' ;
  xchr[64] = '@' ;
  xchr[65] = 'A' ;
  xchr[66] = 'B' ;
  xchr[67] = 'C' ;
  xchr[68] = 'D' ;
  xchr[69] = 'E' ;
  xchr[70] = 'F' ;
  xchr[71] = 'G' ;
  xchr[72] = 'H' ;
  xchr[73] = 'I' ;
  xchr[74] = 'J' ;
  xchr[75] = 'K' ;
  xchr[76] = 'L' ;
  xchr[77] = 'M' ;
  xchr[78] = 'N' ;
  xchr[79] = 'O' ;
  xchr[80] = 'P' ;
  xchr[81] = 'Q' ;
  xchr[82] = 'R' ;
  xchr[83] = 'S' ;
  xchr[84] = 'T' ;
  xchr[85] = 'U' ;
  xchr[86] = 'V' ;
  xchr[87] = 'W' ;
  xchr[88] = 'X' ;
  xchr[89] = 'Y' ;
  xchr[90] = 'Z' ;
  xchr[91] = '[' ;
  xchr[92] = '\\' ;
  xchr[93] = ']' ;
  xchr[94] = '^' ;
  xchr[95] = '_' ;
  xchr[96] = '`' ;
  xchr[97] = 'a' ;
  xchr[98] = 'b' ;
  xchr[99] = 'c' ;
  xchr[100] = 'd' ;
  xchr[101] = 'e' ;
  xchr[102] = 'f' ;
  xchr[103] = 'g' ;
  xchr[104] = 'h' ;
  xchr[105] = 'i' ;
  xchr[106] = 'j' ;
  xchr[107] = 'k' ;
  xchr[108] = 'l' ;
  xchr[109] = 'm' ;
  xchr[110] = 'n' ;
  xchr[111] = 'o' ;
  xchr[112] = 'p' ;
  xchr[113] = 'q' ;
  xchr[114] = 'r' ;
  xchr[115] = 's' ;
  xchr[116] = 't' ;
  xchr[117] = 'u' ;
  xchr[118] = 'v' ;
  xchr[119] = 'w' ;
  xchr[120] = 'x' ;
  xchr[121] = 'y' ;
  xchr[122] = 'z' ;
  xchr[123] = '{' ;
  xchr[124] = '|' ;
  xchr[125] = '}' ;
  xchr[126] = '~' ;
  for (i = 127 ; i <= 255 ; i ++) xchr[i] = '?' ;
  for (i = 0 ; i <= 127 ; i ++) xord[chr(i)] = 32 ;
  for (i = 32 ; i <= 126 ; i ++) xord[xchr[i]] = i ;
  for (i = 0 ; i < 32 ; i++) power[i] = 1 << i ;
  for (i = 0; i < MAXPKCHAR; i++) {
    filename[i] = NULL;
    charflags[i] = 0;
  }
  pkloc = 0 ;
  designsize = fixword(1.0) ;
  numparam = 0;
  widthtab[0] = heighttab[0] = depthtab[0] = italictab[0] = 0 ;
  numwidth = numheight = numdepth = numitalic = 1;
  smallestch = MAXPKCHAR ;
  largestch = -1 ;
  emwidth = 0 ;
}

jumpout()
{
  exit(1) ;
}

pkbyte(b)
     integer b ;
{
  if (b < 0) b = b + 256 ;
  putc(b, pkfile) ;
  pkloc++ ;
}

pkhalfword(a)
     integer a ;
{
  if (a < 0) a = a + 65536 ;
  pkbyte(a >> 8) ;
  pkbyte(a & 255) ;
}

pkthreebytes(a)
     integer a ;
{
  pkbyte((a>>16) & 255) ;
  pkbyte((a>>8) & 255) ;
  pkbyte(a & 255) ;
}

pkword(a)
     integer a ;
{
  pkbyte((a>>24) & 255) ;
  pkbyte((a>>16) & 255) ;
  pkbyte((a>>8) & 255) ;
  pkbyte(a & 255) ;
}

pknyb(a)
     integer a ;
{
  if (bitweight == 16) {
    outputbyte = (a<<4) ;
    bitweight = 1 ;
  } else {
    pkbyte(outputbyte + a) ;
    bitweight = 16 ;
  }
}

writepreamble()
{
  integer i ;
  char *comment = "PBMtoPK 2.3 output" ;
  
  pkbyte(247) ;
  pkbyte(89) ;
  pkbyte(strlen(comment)) ;
  for (i = 0 ; i < strlen(comment); i++) pkbyte(xord[comment[i]]) ;
  pkword(designsize) ;
  pkword(checksum) ; /* checksum; calculate if possible */
  pkword(hppp) ;
  pkword(hppp) ;
}

writepostamble()
{
  pkbyte(245) ;
  while ((pkloc % 4 != 0)) pkbyte(246) ;
  fprintf(stderr, "%d bytes written to packed file.\n", pkloc) ;
}

tfmbyte(b)
     integer b ;
{
  if (b < 0) b = b + 256 ;
  putc(b, tfmfile) ;
}

tfmhalfword(a)
     integer a ;
{
  if (a < 0) a = a + 65536 ;
  tfmbyte(a >> 8) ;
  tfmbyte(a & 255) ;
}

tfmword(a)
     integer a ;
{
  tfmbyte((a>>24) & 255) ;
  tfmbyte((a>>16) & 255) ;
  tfmbyte((a>>8) & 255) ;
  tfmbyte(a & 255) ;
}

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) ;
  tfmhalfword(headersize) ;
  tfmhalfword(smallestch) ;
  tfmhalfword(largestch) ;
  tfmhalfword(numwidth) ;
  tfmhalfword(numheight) ;
  tfmhalfword(numdepth) ;
  tfmhalfword(numitalic) ;
  tfmhalfword(0) ; /* lig/kern table */
  tfmhalfword(0) ; /* kern table */
  tfmhalfword(0) ; /* extensible char table */
  tfmhalfword(numparam) ;
  /* header */
  tfmword(checksum) ;
  tfmword(designsize) ;
  if (strlen(codingscheme) > 39) tfmbyte(39) ;
  else tfmbyte(strlen(codingscheme)) ;
  for (i = 0; i < 39; i++)
    if (*codingscheme) tfmbyte(xord[*codingscheme++]) ;
    else tfmbyte(0) ;
  if (strlen(familyname) > 19) tfmbyte(19) ;
  else tfmbyte(strlen(familyname)) ;
  for (i = 0; i < 19; i++)
    if (*familyname) tfmbyte(xord[*familyname++]) ;
    else tfmbyte(0) ;
  /* char_info */
  for (car = smallestch; car <= largestch; car++)
    if (filename[car]) {
      tfmbyte(tfmindex[car]) ;
      tfmbyte((hgtindex[car]<<4) + depindex[car]) ;
      tfmbyte(italindex[car]<<2) ;
      tfmbyte(0) ;
    } else tfmword(0) ;
  /* width */
  for (i = 0; i < numwidth; i++) tfmword(widthtab[i]) ;
  /* height */
  for (i = 0; i < numheight; i++) tfmword(heighttab[i]) ;
  /* depth */
  for (i = 0; i < numdepth; i++) tfmword(depthtab[i]) ;
  /* italic */
  for (i = 0; i < numitalic; i++) tfmword(italictab[i]) ;
  /* no lig_kern, kern, or exten */
  /* param */
  for (i = 0; i < numparam; i++)
    if (i && (!fixrange(parameters[i]))) {
      fprintf(stderr, " Parameter %d out of range (-p)!\n", i);
      jumpout();
    }
    else tfmword(parameters[i]) ;
  fprintf(stderr, "%d bytes written to tfm file.\n", totallength*4) ;
}

readcharacter()
{
  FILE *fp;

  if (!strcmp(filename[car], "-")) fp = stdin;
  else if ((fp = fopen(filename[car], "r")) == NULL) {
    fprintf(stderr, " Can't open pbm file %s!\n", filename[car]);
    jumpout();
  }
  bitmap = pbm_readpbm(fp, &width, &height) ;
  if (fp != stdin) fclose(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 ;
}

int
equal(row1, row2)
     bit *row1, *row2 ;
{
  integer i ;
  
  for (i = 0; i < width; i++)
    if (row1[i] != row2[i]) return (0) ;
  return(1) ;
}

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

  zerorow = pbm_allocrow(width) ;
  onesrow = pbm_allocrow(width) ;
  repeatptr =
    (integer *)malloc((unsigned int)((height+1)*sizeof(integer))) ;
  bitcounts =
    (integer *)malloc((unsigned int)((height*width)*sizeof(integer))) ;
  if (repeatptr == NULL || bitcounts == NULL) {
     fprintf(stderr, " Out of memory while allocating bit counts!\n");
     jumpout() ;
  }
  for (i = 0 ; i < width ; i++) {
    zerorow[i] = PBM_WHITE ;
    onesrow[i] = PBM_BLACK ;
  }
  for (i=0; i < height; i++) {
    if (equal(bitmap[i], zerorow))
      repeatptr[i] = 0 ;
    else if (equal(bitmap[i], onesrow)) 
      repeatptr[i] = 0 ;
    else if (i + 1 < height && equal(bitmap[i],bitmap[i+1]))
      repeatptr[i] = 1 ;
    else
      repeatptr[i] = 0 ;
  }
  i = 0 ;
  while (i < height) {
    k = i ;
    while (repeatptr[k] == 1) k++ ;
    repeatptr[i] = k - i ;
    i = k + 1 ;
  }
  repeatptr[i] = 0 ;
  colptr = width - 1 ;
  repeatflag = currepeat = curptr = count = rowptr = 0 ;
  test = PBM_WHITE ;
  do {
    colptr++ ;
    if (colptr == width) {
      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++ ;
    else {
      bitcounts[curptr++] = count ;
      if (curptr+3 >= height*width) {
	fprintf(stderr, " Out of memory while saving character counts!\n");
	jumpout() ;
      }
      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) {
    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 ;
    pkbyte(flagbyte) ;
    compsize += 28 ;
    pkword(compsize) ;
    pkword(car) ;
    predpkloc = pkloc + compsize ;
    pkword(tfwid) ;
    pkword(hesc<<16) ;
    pkword(vesc<<16) ;
    pkword(width) ;
    pkword(height) ;
    pkword(xoff) ;
    pkword(yoff) ;
  } else if ((hesc > 255) || (width > 255) || (height > 255) ||
	   (xoff > 127) || (yoff > 127) || (xoff < -128) ||
	   (yoff < -128) || (compsize > 1016)) {
    compsize += 13 ;
    flagbyte += (compsize>>16) + 4 ;
    pkbyte(flagbyte) ;
    pkhalfword(compsize & 65535) ;
    pkbyte(car) ;
    predpkloc = pkloc + compsize ;
    pkthreebytes(tfwid) ;
    pkhalfword(hesc) ;
    pkhalfword(width) ;
    pkhalfword(height) ;
    pkhalfword(xoff) ;
    pkhalfword(yoff) ;
  } else {
    compsize += 8 ;
    flagbyte = flagbyte + (compsize>>8) ;
    pkbyte(flagbyte) ;
    pkbyte(compsize & 255) ;
    pkbyte(car) ;
    predpkloc = pkloc + compsize ;
    pkthreebytes(tfwid) ;
    pkbyte(hesc) ;
    pkbyte(width) ;
    pkbyte(height) ;
    pkbyte(xoff) ;
    pkbyte(yoff) ;
  }
  if (dynf != 14) {
    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) pkbyte(outputbyte) ;
  } else {
    buff = 0 ;
    pbit = 8 ;
    i = firston ;
    hbit = width ;
    on = ! firston ;
    state = false ;
    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 += power[pbit] - 1 ;
	  pkbyte(buff) ;
	  buff = 0 ;
	  hbit -= pbit ;
	  count -= pbit ;
	  pbit = 8 ;
	} else if ((count < pbit) && (count < hbit)) {
	  if (on) buff += power[pbit] - power[pbit - count] ;
	  pbit -=  count ;
	  hbit -= count ;
	  count = 0 ;
	} else {
	  if (on) buff += power[pbit] - power[pbit - hbit] ;
	  count -= hbit ;
	  pbit -= hbit ;
	  hbit = width ;
	  if (pbit == 0) {
	    pkbyte(buff) ;
	    buff = 0 ;
	    pbit = 8 ;
	  }
	}
      } while (hbit != width) ;
      if (state && (repeatflag == 0)) {
	count = scount ;
	i = si ;
	on = son ;
	state = false ;
      } else if (! state && (repeatflag > 0)) {
	scount = count ;
	si = i ;
	son = on ;
	state = true ;
      }
    }
    if (pbit != 8) pkbyte(buff) ;
  }
  if (predpkloc != pkloc) {
    fprintf(stderr, " Bad predicted character length: character %d!\n", car);
    jumpout() ;
  }
  pbm_freerow(zerorow); 
  pbm_freerow(onesrow); 
  free((char *)repeatptr);
  free((char *)bitcounts);
}

usage()
{
  fprintf(stderr, " Usage: pbmtopk pkfile[.pk] tfmfile[.tfm] dpi\n") ;
  fprintf(stderr, "                [-s designsize] [-p num param...]\n");
  fprintf(stderr, "                [-C codingscheme ] [-F family]\n");
  fprintf(stderr, "                [-c num | <char>]...\n");
  fprintf(stderr, " <char> is:     [-W tfmwidth] [-H tfmheight] [-D tfmdepth]\n");
  fprintf(stderr, "                [-I ital_corr] [-h horiz] [-v vert]\n") ;
  fprintf(stderr, "                [-x xoffset] [-y yoffset] file\n") ;
  fprintf(stderr, " or:            -f optfile\n") ;
  jumpout() ;
}

checkchar()
{
  if (car < 0 || car >= MAXPKCHAR) {
    fprintf(stderr, " Character must be in range 0 to %d (-c)!\n",
	    MAXPKCHAR-1) ;
    jumpout() ;
  }
}

optionfile(name)
     char *name ;
{
  FILE *fp ;
  char buffer[MAXOPTLINE] ;
  
  if (!strcmp(name, "-")) fp = stdin ;
  else if ((fp = fopen(name, "r")) == NULL) {
    fprintf(stderr, " Can't open option file %s!\n", name) ;
    jumpout() ;
  }
  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) {
	fprintf(stderr, "Bad option file line %s!\n", buffer) ;
	jumpout() ;
      }
    } else if (*here && *here != '%' && *here != '#') {
      char str[NAMELENGTH] ;
      integer i, n;
      
      checkchar() ;
      if (sscanf(here, "%s%n", str, &n) != 1) {
	fprintf(stderr, "Bad option file line %s!\n", buffer) ;
	jumpout() ;
      }
      filename[car] =
	(char *)malloc((unsigned int)(sizeof(char)*(strlen(str)+1))) ;
      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++ ;
    }
  }
  if (fp != stdin) fclose(fp) ;
}

dialog(gargc, gargv)
     int gargc ;
     char **gargv ;
{
  integer i, hesc, vesc, xoff, yoff, tfwid, tfdep, tfhgt, tfital ;
  byte flags ;
  
  if (--gargc < 1) usage() ;
  strcpy(pkname, *++gargv) ;
  add_suffix(pkname, "pk") ;
  
  if (--gargc < 1) usage() ;
  strcpy(tfmname, *++gargv) ;
  add_suffix(tfmname, "tfm") ;
  
  if (--gargc < 1) usage() ;
  resolution = atoi(*++gargv) ;
  if (resolution < 1 || resolution > 32767) {
    fprintf(stderr, " Unlikely resolution %d dpi!\n", resolution);
    jumpout();
  }
  
  car = flags = hesc = vesc = xoff = yoff = tfwid = 0;
  while (++gargv, --gargc) {
    if (gargv[0][0] == '-' && gargv[0][1]) {
      char c, *p;
      c = gargv[0][1] ;
      if (gargv[0][2]) p = *gargv + 2 ;
      else if (++gargv, --gargc) p = *gargv ;
      else 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) {
	  fprintf(stderr, " Design size %f out of range!\n",
		  unfixword(designsize));
	  jumpout() ;
	}
      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 > 30) {
	  fprintf(stderr, " Parameter count %d out of range!\n", numparam);
	  jumpout();
	}
	for (i=0; i<numparam; i++)
	  if (++gargv,--gargc) parameters[i] = fixword(atof(*gargv)) ;
	  else {
	    fprintf(stderr, " Not enough parameters (-p)!\n");
	    jumpout() ;
	  }
	break ;
      default:
	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] = *gargv ;
      charflags[car] = flags ;
      car++ ;
      flags = 0;
    }
  }
}

main(argc, argv)
     int argc ;
     char *argv[] ;
{
  initialize() ;
  dialog(argc, argv) ;
  hppp = round((resolution<<16) / 72.27) ;
  add_suffix(pkname, "pk") ;
  add_suffix(tfmname, "tfm") ;
  if (!strcmp(pkname, "-")) pkfile = stdout;
  else if ((pkfile = fopen(pkname, "w")) == NULL) {
    fprintf(stderr, " Can't open PK file %s!\n", pkname);
    jumpout() ;
  }
  if (!strcmp(tfmname, "-")) tfmfile = stdout ;
  else if ((tfmfile = fopen(tfmname, "w")) == NULL) {
    fprintf(stderr, " Can't open TFM file %s!\n", tfmname);
    jumpout();
  }
  writepreamble() ;
  for (car = 0 ; car < MAXPKCHAR ; car++)
    if (filename[car]) {
      readcharacter() ;
      shipcharacter() ;
    }
  writepostamble() ;
  writetfmfile() ;
  if (pkfile != stdout) fclose(pkfile) ;
  if (tfmfile != stdout) fclose(tfmfile) ;
  exit(0);
}

