169491Sbostic // -*- C++ -*-
269491Sbostic /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
369491Sbostic      Written by James Clark (jjc@jclark.com)
469491Sbostic 
569491Sbostic This file is part of groff.
669491Sbostic 
769491Sbostic groff is free software; you can redistribute it and/or modify it under
869491Sbostic the terms of the GNU General Public License as published by the Free
969491Sbostic Software Foundation; either version 2, or (at your option) any later
1069491Sbostic version.
1169491Sbostic 
1269491Sbostic groff is distributed in the hope that it will be useful, but WITHOUT ANY
1369491Sbostic WARRANTY; without even the implied warranty of MERCHANTABILITY or
1469491Sbostic FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1569491Sbostic for more details.
1669491Sbostic 
1769491Sbostic You should have received a copy of the GNU General Public License along
1869491Sbostic with groff; see the file COPYING.  If not, write to the Free Software
1969491Sbostic Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
2069491Sbostic 
2169491Sbostic /* I have tried to incorporate the changes needed for TeX 3.0 tfm files,
2269491Sbostic but I haven't tested them. */
2369491Sbostic 
2469491Sbostic /* Groff requires more font metric information than TeX.  The reason
2569491Sbostic for this is that TeX has separate Math Italic fonts, whereas groff
2669491Sbostic uses normal italic fonts for math.  The two additional pieces of
2769491Sbostic information required by groff correspond to the two arguments to the
2869491Sbostic math_fit() macro in the Metafont programs for the CM fonts. In the
2969491Sbostic case of a font for which math_fitting is false, these two arguments
3069491Sbostic are normally ignored by Metafont. We need to get hold of these two
3169491Sbostic parameters and put them in the groff font file.
3269491Sbostic 
3369491Sbostic We do this by loading this definition after cmbase when creating cm.base.
3469491Sbostic 
3569491Sbostic def ignore_math_fit(expr left_adjustment,right_adjustment) =
3669491Sbostic  special "adjustment";
3769491Sbostic  numspecial left_adjustment*16/designsize;
3869491Sbostic  numspecial right_adjustment*16/designsize;
3969491Sbostic  enddef;
4069491Sbostic 
4169491Sbostic This puts the two arguments to the math_fit macro into the gf file.
4269491Sbostic (They will appear in the gf file immediately before the character to
4369491Sbostic which they apply.)  We then create a gf file using this cm.base.  Then
4469491Sbostic we run tfmtodit and specify this gf file with the -g option.
4569491Sbostic 
4669491Sbostic This need only be done for a font for which math_fitting is false;
4769491Sbostic When it's true, the left_correction and subscript_correction should
4869491Sbostic both be zero. */
4969491Sbostic 
5069491Sbostic #include <stdio.h>
51*69504Sbostic #include <unistd.h>
5269491Sbostic #include <stdlib.h>
5369491Sbostic #include <math.h>
5469491Sbostic #include <string.h>
5569491Sbostic #include <errno.h>
5669491Sbostic #include "lib.h"
5769491Sbostic #include "errarg.h"
5869491Sbostic #include "error.h"
5969491Sbostic #include "assert.h"
6069491Sbostic #include "cset.h"
6169491Sbostic 
6269491Sbostic /* Values in the tfm file should be multiplied by this. */
6369491Sbostic 
6469491Sbostic #define MULTIPLIER 1
6569491Sbostic 
6669491Sbostic struct char_info_word {
6769491Sbostic   unsigned char width_index;
6869491Sbostic   char height_index;
6969491Sbostic   char depth_index;
7069491Sbostic   char italic_index;
7169491Sbostic   char tag;
7269491Sbostic   unsigned char remainder;
7369491Sbostic };
7469491Sbostic 
7569491Sbostic struct lig_kern_command {
7669491Sbostic   unsigned char skip_byte;
7769491Sbostic   unsigned char next_char;
7869491Sbostic   unsigned char op_byte;
7969491Sbostic   unsigned char remainder;
8069491Sbostic };
8169491Sbostic 
8269491Sbostic class tfm {
8369491Sbostic   int bc;
8469491Sbostic   int ec;
8569491Sbostic   int nw;
8669491Sbostic   int nh;
8769491Sbostic   int nd;
8869491Sbostic   int ni;
8969491Sbostic   int nl;
9069491Sbostic   int nk;
9169491Sbostic   int np;
9269491Sbostic   int cs;
9369491Sbostic   int ds;
9469491Sbostic   char_info_word *char_info;
9569491Sbostic   int *width;
9669491Sbostic   int *height;
9769491Sbostic   int *depth;
9869491Sbostic   int *italic;
9969491Sbostic   lig_kern_command *lig_kern;
10069491Sbostic   int *kern;
10169491Sbostic   int *param;
10269491Sbostic public:
10369491Sbostic   tfm();
10469491Sbostic   ~tfm();
10569491Sbostic   int load(const char *);
10669491Sbostic   int contains(int);
10769491Sbostic   int get_width(int);
10869491Sbostic   int get_height(int);
10969491Sbostic   int get_depth(int);
11069491Sbostic   int get_italic(int);
11169491Sbostic   int get_param(int, int *);
11269491Sbostic   int get_checksum();
11369491Sbostic   int get_design_size();
11469491Sbostic   int get_lig(unsigned char, unsigned char, unsigned char *);
11569491Sbostic   friend class kern_iterator;
11669491Sbostic };
11769491Sbostic 
11869491Sbostic class kern_iterator {
11969491Sbostic   tfm *t;
12069491Sbostic   int c;
12169491Sbostic   int i;
12269491Sbostic public:
12369491Sbostic   kern_iterator(tfm *);
12469491Sbostic   int next(unsigned char *c1, unsigned char *c2, int *k);
12569491Sbostic };
12669491Sbostic 
12769491Sbostic 
kern_iterator(tfm * p)12869491Sbostic kern_iterator::kern_iterator(tfm *p)
12969491Sbostic : t(p), i(-1), c(t->bc)
13069491Sbostic {
13169491Sbostic }
13269491Sbostic 
next(unsigned char * c1,unsigned char * c2,int * k)13369491Sbostic int kern_iterator::next(unsigned char *c1, unsigned char *c2, int *k)
13469491Sbostic {
13569491Sbostic   for (; c <= t->ec; c++)
13669491Sbostic     if (t->char_info[c - t->bc].tag == 1) {
13769491Sbostic       if (i < 0) {
13869491Sbostic 	i = t->char_info[c - t->bc].remainder;
13969491Sbostic 	if (t->lig_kern[i].skip_byte > 128)
14069491Sbostic 	  i = (256*t->lig_kern[i].op_byte
14169491Sbostic 		   + t->lig_kern[i].remainder);
14269491Sbostic       }
14369491Sbostic       for (;;) {
14469491Sbostic 	int skip = t->lig_kern[i].skip_byte;
14569491Sbostic 	if (skip <= 128 && t->lig_kern[i].op_byte >= 128) {
14669491Sbostic 	  *c1 = c;
14769491Sbostic 	  *c2 = t->lig_kern[i].next_char;
14869491Sbostic 	  *k = t->kern[256*(t->lig_kern[i].op_byte - 128)
14969491Sbostic 		       + t->lig_kern[i].remainder];
15069491Sbostic 	  if (skip == 128) {
15169491Sbostic 	    c++;
15269491Sbostic 	    i = -1;
15369491Sbostic 	  }
15469491Sbostic 	  else
15569491Sbostic 	    i += skip + 1;
15669491Sbostic 	  return 1;
15769491Sbostic 	}
15869491Sbostic 	if (skip >= 128)
15969491Sbostic 	  break;
16069491Sbostic 	i += skip + 1;
16169491Sbostic       }
16269491Sbostic       i = -1;
16369491Sbostic     }
16469491Sbostic   return 0;
16569491Sbostic }
16669491Sbostic 
tfm()16769491Sbostic tfm::tfm()
16869491Sbostic : char_info(0), width(0), height(0), depth(0), italic(0), lig_kern(0),
16969491Sbostic   kern(0), param(0)
17069491Sbostic {
17169491Sbostic }
17269491Sbostic 
get_lig(unsigned char c1,unsigned char c2,unsigned char * cp)17369491Sbostic int tfm::get_lig(unsigned char c1, unsigned char c2, unsigned char *cp)
17469491Sbostic {
17569491Sbostic   if (contains(c1) && char_info[c1 - bc].tag == 1) {
17669491Sbostic     int i = char_info[c1 - bc].remainder;
17769491Sbostic     if (lig_kern[i].skip_byte > 128)
17869491Sbostic       i = 256*lig_kern[i].op_byte + lig_kern[i].remainder;
17969491Sbostic     for (;;) {
18069491Sbostic       int skip = lig_kern[i].skip_byte;
18169491Sbostic       if (skip > 128)
18269491Sbostic 	break;
18369491Sbostic       // We are only interested in normal ligatures, for which
18469491Sbostic       // op_byte == 0.
18569491Sbostic       if (lig_kern[i].op_byte == 0
18669491Sbostic 	  && lig_kern[i].next_char == c2) {
18769491Sbostic 	*cp = lig_kern[i].remainder;
18869491Sbostic 	return 1;
18969491Sbostic       }
19069491Sbostic       if (skip == 128)
19169491Sbostic 	break;
19269491Sbostic       i += skip + 1;
19369491Sbostic     }
19469491Sbostic   }
19569491Sbostic   return 0;
19669491Sbostic }
19769491Sbostic 
contains(int i)19869491Sbostic int tfm::contains(int i)
19969491Sbostic {
20069491Sbostic   return i >= bc && i <= ec && char_info[i - bc].width_index != 0;
20169491Sbostic }
20269491Sbostic 
get_width(int i)20369491Sbostic int tfm::get_width(int i)
20469491Sbostic {
20569491Sbostic   return width[char_info[i - bc].width_index];
20669491Sbostic }
20769491Sbostic 
get_height(int i)20869491Sbostic int tfm::get_height(int i)
20969491Sbostic {
21069491Sbostic   return height[char_info[i - bc].height_index];
21169491Sbostic }
21269491Sbostic 
get_depth(int i)21369491Sbostic int tfm::get_depth(int i)
21469491Sbostic {
21569491Sbostic   return depth[char_info[i - bc].depth_index];
21669491Sbostic }
21769491Sbostic 
get_italic(int i)21869491Sbostic int tfm::get_italic(int i)
21969491Sbostic {
22069491Sbostic   return italic[char_info[i - bc].italic_index];
22169491Sbostic }
22269491Sbostic 
get_param(int i,int * p)22369491Sbostic int tfm::get_param(int i, int *p)
22469491Sbostic {
22569491Sbostic   if (i <= 0 || i > np)
22669491Sbostic     return 0;
22769491Sbostic   else {
22869491Sbostic     *p = param[i - 1];
22969491Sbostic     return 1;
23069491Sbostic   }
23169491Sbostic }
23269491Sbostic 
get_checksum()23369491Sbostic int tfm::get_checksum()
23469491Sbostic {
23569491Sbostic   return cs;
23669491Sbostic }
23769491Sbostic 
get_design_size()23869491Sbostic int tfm::get_design_size()
23969491Sbostic {
24069491Sbostic   return ds;
24169491Sbostic }
24269491Sbostic 
~tfm()24369491Sbostic tfm::~tfm()
24469491Sbostic {
24569491Sbostic   a_delete char_info;
24669491Sbostic   a_delete width;
24769491Sbostic   a_delete height;
24869491Sbostic   a_delete depth;
24969491Sbostic   a_delete italic;
25069491Sbostic   a_delete lig_kern;
25169491Sbostic   a_delete kern;
25269491Sbostic   a_delete param;
25369491Sbostic }
25469491Sbostic 
read2(unsigned char * & s)25569491Sbostic int read2(unsigned char *&s)
25669491Sbostic {
25769491Sbostic   int n;
25869491Sbostic   n = *s++ << 8;
25969491Sbostic   n |= *s++;
26069491Sbostic   return n;
26169491Sbostic }
26269491Sbostic 
read4(unsigned char * & s)26369491Sbostic int read4(unsigned char *&s)
26469491Sbostic {
26569491Sbostic   int n;
26669491Sbostic   n = *s++ << 24;
26769491Sbostic   n |= *s++ << 16;
26869491Sbostic   n |= *s++ << 8;
26969491Sbostic   n |= *s++;
27069491Sbostic   return n;
27169491Sbostic }
27269491Sbostic 
27369491Sbostic 
load(const char * file)27469491Sbostic int tfm::load(const char *file)
27569491Sbostic {
27669491Sbostic   errno = 0;
27769491Sbostic   FILE *fp = fopen(file, "r");
27869491Sbostic   if (!fp) {
27969491Sbostic     error("can't open `%1': %2", file, strerror(errno));
28069491Sbostic     return 0;
28169491Sbostic   }
28269491Sbostic   int c1 = getc(fp);
28369491Sbostic   int c2 = getc(fp);
28469491Sbostic   if (c1 == EOF || c2 == EOF) {
28569491Sbostic     fclose(fp);
28669491Sbostic     error("unexpected end of file on `%1'", file);
28769491Sbostic     return 0;
28869491Sbostic   }
28969491Sbostic   int lf = (c1 << 8) + c2;
29069491Sbostic   int toread = lf*4 - 2;
29169491Sbostic   unsigned char *buf = new unsigned char[toread];
29269491Sbostic   if (fread(buf, 1, toread, fp) != toread) {
29369491Sbostic     if (feof(fp))
29469491Sbostic       error("unexpected end of file on `%1'", file);
29569491Sbostic     else
29669491Sbostic       error("error on file `%1'", file);
29769491Sbostic     a_delete buf;
29869491Sbostic     fclose(fp);
29969491Sbostic     return 0;
30069491Sbostic   }
30169491Sbostic   fclose(fp);
30269491Sbostic   if (lf < 6) {
30369491Sbostic     error("bad tfm file `%1': impossibly short", file);
30469491Sbostic     a_delete buf;
30569491Sbostic     return 0;
30669491Sbostic   }
30769491Sbostic   unsigned char *ptr = buf;
30869491Sbostic   int lh = read2(ptr);
30969491Sbostic   bc = read2(ptr);
31069491Sbostic   ec = read2(ptr);
31169491Sbostic   nw = read2(ptr);
31269491Sbostic   nh = read2(ptr);
31369491Sbostic   nd = read2(ptr);
31469491Sbostic   ni = read2(ptr);
31569491Sbostic   nl = read2(ptr);
31669491Sbostic   nk = read2(ptr);
31769491Sbostic   int ne = read2(ptr);
31869491Sbostic   np = read2(ptr);
31969491Sbostic   if (6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np != lf) {
32069491Sbostic     error("bad tfm file `%1': lengths do not sum", file);
32169491Sbostic     a_delete buf;
32269491Sbostic     return 0;
32369491Sbostic   }
32469491Sbostic   if (lh < 2) {
32569491Sbostic     error("bad tfm file `%1': header too short", file);
32669491Sbostic     a_delete buf;
32769491Sbostic     return 0;
32869491Sbostic   }
32969491Sbostic   char_info = new char_info_word[ec - bc + 1];
33069491Sbostic   width = new int[nw];
33169491Sbostic   height = new int[nh];
33269491Sbostic   depth = new int[nd];
33369491Sbostic   italic = new int[ni];
33469491Sbostic   lig_kern = new lig_kern_command[nl];
33569491Sbostic   kern = new int[nk];
33669491Sbostic   param = new int[np];
33769491Sbostic   int i;
33869491Sbostic   cs = read4(ptr);
33969491Sbostic   ds = read4(ptr);
34069491Sbostic   ptr += (lh-2)*4;
34169491Sbostic   for (i = 0; i < ec - bc + 1; i++) {
34269491Sbostic     char_info[i].width_index = *ptr++;
34369491Sbostic     unsigned char tem = *ptr++;
34469491Sbostic     char_info[i].depth_index = tem & 0xf;
34569491Sbostic     char_info[i].height_index = tem >> 4;
34669491Sbostic     tem = *ptr++;
34769491Sbostic     char_info[i].italic_index = tem >> 2;
34869491Sbostic     char_info[i].tag = tem & 3;
34969491Sbostic     char_info[i].remainder = *ptr++;
35069491Sbostic   }
35169491Sbostic   for (i = 0; i < nw; i++)
35269491Sbostic     width[i] = read4(ptr);
35369491Sbostic   for (i = 0; i < nh; i++)
35469491Sbostic     height[i] = read4(ptr);
35569491Sbostic   for (i = 0; i < nd; i++)
35669491Sbostic     depth[i] = read4(ptr);
35769491Sbostic   for (i = 0; i < ni; i++)
35869491Sbostic     italic[i] = read4(ptr);
35969491Sbostic   for (i = 0; i < nl; i++) {
36069491Sbostic     lig_kern[i].skip_byte = *ptr++;
36169491Sbostic     lig_kern[i].next_char = *ptr++;
36269491Sbostic     lig_kern[i].op_byte = *ptr++;
36369491Sbostic     lig_kern[i].remainder = *ptr++;
36469491Sbostic   }
36569491Sbostic   for (i = 0; i < nk; i++)
36669491Sbostic     kern[i] = read4(ptr);
36769491Sbostic   ptr += ne*4;
36869491Sbostic   for (i = 0; i < np; i++)
36969491Sbostic     param[i] = read4(ptr);
37069491Sbostic   assert(ptr == buf + lf*4 - 2);
37169491Sbostic   a_delete buf;
37269491Sbostic   return 1;
37369491Sbostic }
37469491Sbostic 
37569491Sbostic class gf {
37669491Sbostic   int left[256];
37769491Sbostic   int right[256];
37869491Sbostic   static int sread4(int *p, FILE *fp);
37969491Sbostic   static int uread3(int *p, FILE *fp);
38069491Sbostic   static int uread2(int *p, FILE *fp);
38169491Sbostic   static int skip(int n, FILE *fp);
38269491Sbostic public:
38369491Sbostic   gf();
38469491Sbostic   int load(const char *file);
get_left_adjustment(int i)38569491Sbostic   int get_left_adjustment(int i) { return left[i]; }
get_right_adjustment(int i)38669491Sbostic   int get_right_adjustment(int i) { return right[i]; }
38769491Sbostic };
38869491Sbostic 
gf()38969491Sbostic gf::gf()
39069491Sbostic {
39169491Sbostic   for (int i = 0; i < 256; i++)
39269491Sbostic     left[i] = right[i] = 0;
39369491Sbostic }
39469491Sbostic 
load(const char * file)39569491Sbostic int gf::load(const char *file)
39669491Sbostic {
39769491Sbostic   enum {
39869491Sbostic     paint_0 = 0,
39969491Sbostic     paint1 = 64,
40069491Sbostic     boc = 67,
40169491Sbostic     boc1 = 68,
40269491Sbostic     eoc = 69,
40369491Sbostic     skip0 = 70,
40469491Sbostic     skip1 = 71,
40569491Sbostic     new_row_0 = 74,
40669491Sbostic     xxx1 = 239,
40769491Sbostic     yyy = 243,
40869491Sbostic     no_op = 244,
40969491Sbostic     pre = 247,
41069491Sbostic     post = 248
41169491Sbostic   };
41269491Sbostic   int got_an_adjustment = 0;
41369491Sbostic   int pending_adjustment = 0;
41469491Sbostic   int left_adj, right_adj;
41569491Sbostic   const int gf_id_byte = 131;
41669491Sbostic   errno = 0;
41769491Sbostic   FILE *fp = fopen(file, "r");
41869491Sbostic   if (!fp) {
41969491Sbostic     error("can't open `%1': %2", file, strerror(errno));
42069491Sbostic     return 0;
42169491Sbostic   }
42269491Sbostic   if (getc(fp) != pre || getc(fp) != gf_id_byte) {
42369491Sbostic     error("bad gf file");
42469491Sbostic     return 0;
42569491Sbostic   }
42669491Sbostic   int n = getc(fp);
42769491Sbostic   if (n == EOF)
42869491Sbostic     goto eof;
42969491Sbostic   if (!skip(n, fp))
43069491Sbostic     goto eof;
43169491Sbostic   for (;;) {
43269491Sbostic     int op = getc(fp);
43369491Sbostic     if (op == EOF)
43469491Sbostic       goto eof;
43569491Sbostic     if (op == post)
43669491Sbostic       break;
43769491Sbostic     if ((op >= paint_0 && op <= paint_0 + 63)
43869491Sbostic 	|| (op >= new_row_0 && op <= new_row_0 + 164))
43969491Sbostic       continue;
44069491Sbostic     switch (op) {
44169491Sbostic     case no_op:
44269491Sbostic     case eoc:
44369491Sbostic     case skip0:
44469491Sbostic       break;
44569491Sbostic     case paint1:
44669491Sbostic     case skip1:
44769491Sbostic       if (!skip(1, fp))
44869491Sbostic 	goto eof;
44969491Sbostic       break;
45069491Sbostic     case paint1 + 1:
45169491Sbostic     case skip1 + 1:
45269491Sbostic       if (!skip(2, fp))
45369491Sbostic 	goto eof;
45469491Sbostic       break;
45569491Sbostic     case paint1 + 2:
45669491Sbostic     case skip1 + 2:
45769491Sbostic       if (!skip(3, fp))
45869491Sbostic 	goto eof;
45969491Sbostic       break;
46069491Sbostic     case boc:
46169491Sbostic       {
46269491Sbostic 	int code;
46369491Sbostic 	if (!sread4(&code, fp))
46469491Sbostic 	  goto eof;
46569491Sbostic 	if (pending_adjustment) {
46669491Sbostic 	  pending_adjustment = 0;
46769491Sbostic 	  left[code & 0377] = left_adj;
46869491Sbostic 	  right[code & 0377] = right_adj;
46969491Sbostic 	}
47069491Sbostic 	if (!skip(20, fp))
47169491Sbostic 	  goto eof;
47269491Sbostic 	break;
47369491Sbostic       }
47469491Sbostic     case boc1:
47569491Sbostic       {
47669491Sbostic 	int code = getc(fp);
47769491Sbostic 	if (code == EOF)
47869491Sbostic 	  goto eof;
47969491Sbostic 	if (pending_adjustment) {
48069491Sbostic 	  pending_adjustment = 0;
48169491Sbostic 	  left[code] = left_adj;
48269491Sbostic 	  right[code] = right_adj;
48369491Sbostic 	}
48469491Sbostic 	if (!skip(4, fp))
48569491Sbostic 	  goto eof;
48669491Sbostic 	break;
48769491Sbostic       }
48869491Sbostic     case xxx1:
48969491Sbostic       {
49069491Sbostic 	int len = getc(fp);
49169491Sbostic 	if (len == EOF)
49269491Sbostic 	  goto eof;
49369491Sbostic 	char buf[256];
49469491Sbostic 	if (fread(buf, 1, len, fp) != len)
49569491Sbostic 	  goto eof;
49669491Sbostic 	if (len == 10 /* strlen("adjustment") */
49769491Sbostic 	    && memcmp(buf, "adjustment", len) == 0) {
49869491Sbostic 	  int c = getc(fp);
49969491Sbostic 	  if (c != yyy) {
50069491Sbostic 	    if (c != EOF)
50169491Sbostic 	      ungetc(c, fp);
50269491Sbostic 	    break;
50369491Sbostic 	  }
50469491Sbostic 	  if (!sread4(&left_adj, fp))
50569491Sbostic 	    goto eof;
50669491Sbostic 	  c = getc(fp);
50769491Sbostic 	  if (c != yyy) {
50869491Sbostic 	    if (c != EOF)
50969491Sbostic 	      ungetc(c, fp);
51069491Sbostic 	    break;
51169491Sbostic 	  }
51269491Sbostic 	  if (!sread4(&right_adj, fp))
51369491Sbostic 	    goto eof;
51469491Sbostic 	  got_an_adjustment = 1;
51569491Sbostic 	  pending_adjustment = 1;
51669491Sbostic 	}
51769491Sbostic 	break;
51869491Sbostic       }
51969491Sbostic     case xxx1 + 1:
52069491Sbostic       if (!uread2(&n, fp) || !skip(n, fp))
52169491Sbostic 	goto eof;
52269491Sbostic       break;
52369491Sbostic     case xxx1 + 2:
52469491Sbostic       if (!uread3(&n, fp) || !skip(n, fp))
52569491Sbostic 	goto eof;
52669491Sbostic       break;
52769491Sbostic     case xxx1 + 3:
52869491Sbostic       if (!sread4(&n, fp) || !skip(n, fp))
52969491Sbostic 	goto eof;
53069491Sbostic       break;
53169491Sbostic     case yyy:
53269491Sbostic       if (!skip(4, fp))
53369491Sbostic 	goto eof;
53469491Sbostic       break;
53569491Sbostic     default:
53669491Sbostic       fatal("unrecognized opcode `%1'", op);
53769491Sbostic       break;
53869491Sbostic     }
53969491Sbostic   }
54069491Sbostic   if (!got_an_adjustment)
54169491Sbostic     warning("no adjustment specials found in gf file");
54269491Sbostic   return 1;
54369491Sbostic  eof:
54469491Sbostic   error("unexpected end of file");
54569491Sbostic   return 0;
54669491Sbostic }
54769491Sbostic 
sread4(int * p,FILE * fp)54869491Sbostic int gf::sread4(int *p, FILE *fp)
54969491Sbostic {
55069491Sbostic   *p = getc(fp);
55169491Sbostic   if (*p >= 128)
55269491Sbostic     *p -= 256;
55369491Sbostic   *p <<= 8;
55469491Sbostic   *p |= getc(fp);
55569491Sbostic   *p <<= 8;
55669491Sbostic   *p |= getc(fp);
55769491Sbostic   *p <<= 8;
55869491Sbostic   *p |= getc(fp);
55969491Sbostic   return !ferror(fp) && !feof(fp);
56069491Sbostic }
56169491Sbostic 
uread3(int * p,FILE * fp)56269491Sbostic int gf::uread3(int *p, FILE *fp)
56369491Sbostic {
56469491Sbostic   *p = getc(fp);
56569491Sbostic   *p <<= 8;
56669491Sbostic   *p |= getc(fp);
56769491Sbostic   *p <<= 8;
56869491Sbostic   *p |= getc(fp);
56969491Sbostic   return !ferror(fp) && !feof(fp);
57069491Sbostic }
57169491Sbostic 
uread2(int * p,FILE * fp)57269491Sbostic int gf::uread2(int *p, FILE *fp)
57369491Sbostic {
57469491Sbostic   *p = getc(fp);
57569491Sbostic   *p <<= 8;
57669491Sbostic   *p |= getc(fp);
57769491Sbostic   return !ferror(fp) && !feof(fp);
57869491Sbostic }
57969491Sbostic 
skip(int n,FILE * fp)58069491Sbostic int gf::skip(int n, FILE *fp)
58169491Sbostic {
58269491Sbostic   while (--n >= 0)
58369491Sbostic     if (getc(fp) == EOF)
58469491Sbostic       return 0;
58569491Sbostic   return 1;
58669491Sbostic }
58769491Sbostic 
58869491Sbostic 
58969491Sbostic struct char_list {
59069491Sbostic   char *ch;
59169491Sbostic   char_list *next;
59269491Sbostic   char_list(const char *, char_list * = 0);
59369491Sbostic };
59469491Sbostic 
char_list(const char * s,char_list * p)59569491Sbostic char_list::char_list(const char *s, char_list *p) : ch(strsave(s)), next(p)
59669491Sbostic {
59769491Sbostic }
59869491Sbostic 
59969491Sbostic 
read_map(const char * file,char_list ** table)60069491Sbostic int read_map(const char *file, char_list **table)
60169491Sbostic {
60269491Sbostic   errno = 0;
60369491Sbostic   FILE *fp = fopen(file, "r");
60469491Sbostic   if (!fp) {
60569491Sbostic     error("can't open `%1': %2", file, strerror(errno));
60669491Sbostic     return 0;
60769491Sbostic   }
60869491Sbostic   for (int i = 0; i < 256; i++)
60969491Sbostic     table[i] = 0;
61069491Sbostic   char buf[512];
61169491Sbostic   int lineno = 0;
61269491Sbostic   while (fgets(buf, int(sizeof(buf)), fp)) {
61369491Sbostic     lineno++;
61469491Sbostic     char *ptr = buf;
61569491Sbostic     while (csspace(*ptr))
61669491Sbostic       ptr++;
61769491Sbostic     if (*ptr == '\0' || *ptr == '#')
61869491Sbostic       continue;
61969491Sbostic     ptr = strtok(ptr, " \n\t");
62069491Sbostic     if (!ptr)
62169491Sbostic       continue;
62269491Sbostic     int n;
62369491Sbostic     if (sscanf(ptr, "%d", &n) != 1) {
62469491Sbostic       error("%1:%2: bad map file", file, lineno);
62569491Sbostic       fclose(fp);
62669491Sbostic       return 0;
62769491Sbostic     }
62869491Sbostic     if (n < 0 || n > 255) {
62969491Sbostic       error("%1:%2: code out of range", file, lineno);
63069491Sbostic       fclose(fp);
63169491Sbostic       return 0;
63269491Sbostic     }
63369491Sbostic     ptr = strtok(0, " \n\t");
63469491Sbostic     if (!ptr) {
63569491Sbostic       error("%1:%2: missing names", file, lineno);
63669491Sbostic       fclose(fp);
63769491Sbostic       return 0;
63869491Sbostic     }
63969491Sbostic     for (; ptr; ptr = strtok(0, " \n\t"))
64069491Sbostic       table[n] = new char_list(ptr, table[n]);
64169491Sbostic   }
64269491Sbostic   fclose(fp);
64369491Sbostic   return 1;
64469491Sbostic }
64569491Sbostic 
64669491Sbostic 
64769491Sbostic /* Every character that can participate in a ligature appears in the
64869491Sbostic lig_chars table. `ch' gives the full-name of the character, `name'
64969491Sbostic gives the groff name of the character, `i' gives its index in
65069491Sbostic the encoding, which is filled in later  (-1 if it does not appear). */
65169491Sbostic 
65269491Sbostic struct {
65369491Sbostic   const char *ch;
65469491Sbostic   int i;
65569491Sbostic } lig_chars[] = {
65669491Sbostic   "f", -1,
65769491Sbostic   "i", -1,
65869491Sbostic   "l", -1,
65969491Sbostic   "ff", -1,
66069491Sbostic   "fi", -1,
66169491Sbostic   "fl", -1,
66269491Sbostic   "Fi", -1,
66369491Sbostic   "Fl", -1,
66469491Sbostic };
66569491Sbostic 
66669491Sbostic // Indices into lig_chars[].
66769491Sbostic 
66869491Sbostic enum { CH_f, CH_i, CH_l, CH_ff, CH_fi, CH_fl, CH_ffi, CH_ffl };
66969491Sbostic 
67069491Sbostic // Each possible ligature appears in this table.
67169491Sbostic 
67269491Sbostic struct {
67369491Sbostic   unsigned char c1, c2, res;
67469491Sbostic   const char *ch;
67569491Sbostic } lig_table[] = {
67669491Sbostic   CH_f, CH_f, CH_ff, "ff",
67769491Sbostic   CH_f, CH_i, CH_fi, "fi",
67869491Sbostic   CH_f, CH_l, CH_fl, "fl",
67969491Sbostic   CH_ff, CH_i, CH_ffi, "ffi",
68069491Sbostic   CH_ff, CH_l, CH_ffl, "ffl",
68169491Sbostic   };
68269491Sbostic 
68369491Sbostic static void usage();
68469491Sbostic 
main(int argc,char ** argv)68569491Sbostic int main(int argc, char **argv)
68669491Sbostic {
68769491Sbostic   program_name = argv[0];
68869491Sbostic   int special_flag = 0;
68969491Sbostic   int skewchar = -1;
69069491Sbostic   int opt;
69169491Sbostic   const char *gf_file = 0;
69269491Sbostic   while ((opt = getopt(argc, argv, "svg:k:")) != EOF)
69369491Sbostic     switch (opt) {
69469491Sbostic     case 'g':
69569491Sbostic       gf_file = optarg;
69669491Sbostic       break;
69769491Sbostic     case 's':
69869491Sbostic       special_flag = 1;
69969491Sbostic       break;
70069491Sbostic     case 'k':
70169491Sbostic       {
70269491Sbostic 	char *ptr;
70369491Sbostic 	long n = strtol(optarg, &ptr, 0);
70469491Sbostic 	if ((n == 0 && ptr == optarg)
70569491Sbostic 	    || *ptr != '\0'
70669491Sbostic 	    || n < 0
70769491Sbostic 	    || n > UCHAR_MAX)
70869491Sbostic 	  error("invalid skewchar");
70969491Sbostic 	else
71069491Sbostic 	  skewchar = (int)n;
71169491Sbostic 	break;
71269491Sbostic       }
71369491Sbostic     case 'v':
71469491Sbostic       {
71569491Sbostic 	extern const char *version_string;
71669491Sbostic 	fprintf(stderr, "tfmtodit version %s\n", version_string);
71769491Sbostic 	fflush(stderr);
71869491Sbostic 	break;
71969491Sbostic       }
72069491Sbostic     case '?':
72169491Sbostic       usage();
72269491Sbostic       break;
72369491Sbostic     case EOF:
72469491Sbostic       assert(0);
72569491Sbostic     }
72669491Sbostic   if (argc - optind != 3)
72769491Sbostic     usage();
72869491Sbostic   gf g;
72969491Sbostic   if (gf_file) {
73069491Sbostic     if (!g.load(gf_file))
73169491Sbostic       return 1;
73269491Sbostic   }
73369491Sbostic   const char *tfm_file = argv[optind];
73469491Sbostic   const char *map_file = argv[optind + 1];
73569491Sbostic   const char *font_file = argv[optind + 2];
73669491Sbostic   tfm t;
73769491Sbostic   if (!t.load(tfm_file))
73869491Sbostic     return 1;
73969491Sbostic   char_list *table[256];
74069491Sbostic   if (!read_map(map_file, table))
74169491Sbostic     return 1;
74269491Sbostic   errno = 0;
74369491Sbostic   if (!freopen(font_file, "w", stdout)) {
74469491Sbostic     error("can't open `%1' for writing: %2", font_file, strerror(errno));
74569491Sbostic     return 1;
74669491Sbostic   }
74769491Sbostic   printf("name %s\n", font_file);
74869491Sbostic   if (special_flag)
74969491Sbostic     fputs("special\n", stdout);
75069491Sbostic   char *internal_name = strsave(argv[optind]);
75169491Sbostic   int len = strlen(internal_name);
75269491Sbostic   if (len > 4 && strcmp(internal_name + len - 4, ".tfm") == 0)
75369491Sbostic     internal_name[len - 4] = '\0';
75469491Sbostic   char *s = strrchr(internal_name, '/');
75569491Sbostic   printf("internalname %s\n", s ? s + 1 : internal_name);
75669491Sbostic   int n;
75769491Sbostic   if (t.get_param(2, &n)) {
75869491Sbostic     if (n > 0)
75969491Sbostic       printf("spacewidth %d\n", n*MULTIPLIER);
76069491Sbostic   }
76169491Sbostic   if (t.get_param(1, &n) && n != 0)
76269491Sbostic     printf("slant %f\n", atan2(n/double(1<<20), 1.0)*180.0/M_PI);
76369491Sbostic   int xheight;
76469491Sbostic   if (!t.get_param(5, &xheight))
76569491Sbostic     xheight = 0;
76669491Sbostic   int i;
76769491Sbostic   // Print the list of ligatures.
76869491Sbostic   // First find the indices of each character that can participate in
76969491Sbostic   // a ligature.
77069491Sbostic   for (i = 0; i < 256; i++)
77169491Sbostic     for (int j = 0; j < sizeof(lig_chars)/sizeof(lig_chars[0]); j++)
77269491Sbostic       for (char_list *p = table[i]; p; p = p->next)
77369491Sbostic 	if (strcmp(lig_chars[j].ch, p->ch) == 0)
77469491Sbostic 	  lig_chars[j].i = i;
77569491Sbostic   // For each possible ligature, if its participants all exist,
77669491Sbostic   // and it appears as a ligature in the tfm file, include in
77769491Sbostic   // the list of ligatures.
77869491Sbostic   int started = 0;
77969491Sbostic   for (i = 0; i < sizeof(lig_table)/sizeof(lig_table[0]); i++) {
78069491Sbostic     int i1 = lig_chars[lig_table[i].c1].i;
78169491Sbostic     int i2 = lig_chars[lig_table[i].c2].i;
78269491Sbostic     int r = lig_chars[lig_table[i].res].i;
78369491Sbostic     if (i1 >= 0 && i2 >= 0 && r >= 0) {
78469491Sbostic       unsigned char c;
78569491Sbostic       if (t.get_lig(i1, i2, &c) && c == r) {
78669491Sbostic 	if (!started) {
78769491Sbostic 	  started = 1;
78869491Sbostic 	  fputs("ligatures", stdout);
78969491Sbostic 	}
79069491Sbostic 	printf(" %s", lig_table[i].ch);
79169491Sbostic       }
79269491Sbostic     }
79369491Sbostic   }
79469491Sbostic   if (started)
79569491Sbostic     fputs(" 0\n", stdout);
79669491Sbostic   printf("checksum %d\n", t.get_checksum());
79769491Sbostic   printf("designsize %d\n", t.get_design_size());
79869491Sbostic   // Now print out the kerning information.
79969491Sbostic   int had_kern = 0;
80069491Sbostic   kern_iterator iter(&t);
80169491Sbostic   unsigned char c1, c2;
80269491Sbostic   int k;
80369491Sbostic   while (iter.next(&c1, &c2, &k))
80469491Sbostic     if (c2 != skewchar) {
80569491Sbostic       k *= MULTIPLIER;
80669491Sbostic       char_list *q = table[c2];
80769491Sbostic       for (char_list *p1 = table[c1]; p1; p1 = p1->next)
80869491Sbostic 	for (char_list *p2 = q; p2; p2 = p2->next) {
80969491Sbostic 	  if (!had_kern) {
81069491Sbostic 	    printf("kernpairs\n");
81169491Sbostic 	    had_kern = 1;
81269491Sbostic 	  }
81369491Sbostic 	  printf("%s %s %d\n", p1->ch, p2->ch, k);
81469491Sbostic 	}
81569491Sbostic     }
81669491Sbostic   printf("charset\n");
81769491Sbostic   char_list unnamed("---");
81869491Sbostic   for (i = 0; i < 256; i++)
81969491Sbostic     if (t.contains(i)) {
82069491Sbostic       char_list *p = table[i] ? table[i] : &unnamed;
82169491Sbostic       int m[6];
82269491Sbostic       m[0] = t.get_width(i);
82369491Sbostic       m[1] = t.get_height(i);
82469491Sbostic       m[2] = t.get_depth(i);
82569491Sbostic       m[3] = t.get_italic(i);
82669491Sbostic       m[4] = g.get_left_adjustment(i);
82769491Sbostic       m[5] = g.get_right_adjustment(i);
82869491Sbostic       printf("%s\t%d", p->ch, m[0]*MULTIPLIER);
82969491Sbostic       for (int j = int(sizeof(m)/sizeof(m[0])) - 1; j > 0; j--)
83069491Sbostic 	if (m[j] != 0)
83169491Sbostic 	  break;
83269491Sbostic       for (int k = 1; k <= j; k++)
83369491Sbostic 	printf(",%d", m[k]*MULTIPLIER);
83469491Sbostic       int type = 0;
83569491Sbostic       if (m[2] > 0)
83669491Sbostic 	type = 1;
83769491Sbostic       if (m[1] > xheight)
83869491Sbostic 	type += 2;
83969491Sbostic       printf("\t%d\t%04o\n", type, i);
84069491Sbostic       for (p = p->next; p; p = p->next)
84169491Sbostic 	printf("%s\t\"\n", p->ch);
84269491Sbostic     }
84369491Sbostic   return 0;
84469491Sbostic }
84569491Sbostic 
usage()84669491Sbostic static void usage()
84769491Sbostic {
84869491Sbostic   fprintf(stderr, "usage: %s [-sv] [-g gf_file] [-k skewchar] tfm_file map_file font\n",
84969491Sbostic 	  program_name);
85069491Sbostic   exit(1);
85169491Sbostic }
852