1a34904b5Sbostic // -*- C++ -*-
2a34904b5Sbostic /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3a34904b5Sbostic      Written by James Clark (jjc@jclark.com)
4a34904b5Sbostic 
5a34904b5Sbostic This file is part of groff.
6a34904b5Sbostic 
7a34904b5Sbostic groff is free software; you can redistribute it and/or modify it under
8a34904b5Sbostic the terms of the GNU General Public License as published by the Free
9a34904b5Sbostic Software Foundation; either version 2, or (at your option) any later
10a34904b5Sbostic version.
11a34904b5Sbostic 
12a34904b5Sbostic groff is distributed in the hope that it will be useful, but WITHOUT ANY
13a34904b5Sbostic WARRANTY; without even the implied warranty of MERCHANTABILITY or
14a34904b5Sbostic FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15a34904b5Sbostic for more details.
16a34904b5Sbostic 
17a34904b5Sbostic You should have received a copy of the GNU General Public License along
18a34904b5Sbostic with groff; see the file COPYING.  If not, write to the Free Software
19a34904b5Sbostic Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20a34904b5Sbostic 
21a34904b5Sbostic /* I have tried to incorporate the changes needed for TeX 3.0 tfm files,
22a34904b5Sbostic but I haven't tested them. */
23a34904b5Sbostic 
24a34904b5Sbostic /* Groff requires more font metric information than TeX.  The reason
25a34904b5Sbostic for this is that TeX has separate Math Italic fonts, whereas groff
26a34904b5Sbostic uses normal italic fonts for math.  The two additional pieces of
27a34904b5Sbostic information required by groff correspond to the two arguments to the
28a34904b5Sbostic math_fit() macro in the Metafont programs for the CM fonts. In the
29a34904b5Sbostic case of a font for which math_fitting is false, these two arguments
30a34904b5Sbostic are normally ignored by Metafont. We need to get hold of these two
31a34904b5Sbostic parameters and put them in the groff font file.
32a34904b5Sbostic 
33a34904b5Sbostic We do this by loading this definition after cmbase when creating cm.base.
34a34904b5Sbostic 
35a34904b5Sbostic def ignore_math_fit(expr left_adjustment,right_adjustment) =
36a34904b5Sbostic  special "adjustment";
37a34904b5Sbostic  numspecial left_adjustment*16/designsize;
38a34904b5Sbostic  numspecial right_adjustment*16/designsize;
39a34904b5Sbostic  enddef;
40a34904b5Sbostic 
41a34904b5Sbostic This puts the two arguments to the math_fit macro into the gf file.
42a34904b5Sbostic (They will appear in the gf file immediately before the character to
43a34904b5Sbostic which they apply.)  We then create a gf file using this cm.base.  Then
44a34904b5Sbostic we run tfmtodit and specify this gf file with the -g option.
45a34904b5Sbostic 
46a34904b5Sbostic This need only be done for a font for which math_fitting is false;
47a34904b5Sbostic When it's true, the left_correction and subscript_correction should
48a34904b5Sbostic both be zero. */
49a34904b5Sbostic 
50a34904b5Sbostic #include <stdio.h>
51*4fb4e6f3Sbostic #include <unistd.h>
52a34904b5Sbostic #include <stdlib.h>
53a34904b5Sbostic #include <math.h>
54a34904b5Sbostic #include <string.h>
55a34904b5Sbostic #include <errno.h>
56a34904b5Sbostic #include "lib.h"
57a34904b5Sbostic #include "errarg.h"
58a34904b5Sbostic #include "error.h"
59a34904b5Sbostic #include "assert.h"
60a34904b5Sbostic #include "cset.h"
61a34904b5Sbostic 
62a34904b5Sbostic /* Values in the tfm file should be multiplied by this. */
63a34904b5Sbostic 
64a34904b5Sbostic #define MULTIPLIER 1
65a34904b5Sbostic 
66a34904b5Sbostic struct char_info_word {
67a34904b5Sbostic   unsigned char width_index;
68a34904b5Sbostic   char height_index;
69a34904b5Sbostic   char depth_index;
70a34904b5Sbostic   char italic_index;
71a34904b5Sbostic   char tag;
72a34904b5Sbostic   unsigned char remainder;
73a34904b5Sbostic };
74a34904b5Sbostic 
75a34904b5Sbostic struct lig_kern_command {
76a34904b5Sbostic   unsigned char skip_byte;
77a34904b5Sbostic   unsigned char next_char;
78a34904b5Sbostic   unsigned char op_byte;
79a34904b5Sbostic   unsigned char remainder;
80a34904b5Sbostic };
81a34904b5Sbostic 
82a34904b5Sbostic class tfm {
83a34904b5Sbostic   int bc;
84a34904b5Sbostic   int ec;
85a34904b5Sbostic   int nw;
86a34904b5Sbostic   int nh;
87a34904b5Sbostic   int nd;
88a34904b5Sbostic   int ni;
89a34904b5Sbostic   int nl;
90a34904b5Sbostic   int nk;
91a34904b5Sbostic   int np;
92a34904b5Sbostic   int cs;
93a34904b5Sbostic   int ds;
94a34904b5Sbostic   char_info_word *char_info;
95a34904b5Sbostic   int *width;
96a34904b5Sbostic   int *height;
97a34904b5Sbostic   int *depth;
98a34904b5Sbostic   int *italic;
99a34904b5Sbostic   lig_kern_command *lig_kern;
100a34904b5Sbostic   int *kern;
101a34904b5Sbostic   int *param;
102a34904b5Sbostic public:
103a34904b5Sbostic   tfm();
104a34904b5Sbostic   ~tfm();
105a34904b5Sbostic   int load(const char *);
106a34904b5Sbostic   int contains(int);
107a34904b5Sbostic   int get_width(int);
108a34904b5Sbostic   int get_height(int);
109a34904b5Sbostic   int get_depth(int);
110a34904b5Sbostic   int get_italic(int);
111a34904b5Sbostic   int get_param(int, int *);
112a34904b5Sbostic   int get_checksum();
113a34904b5Sbostic   int get_design_size();
114a34904b5Sbostic   int get_lig(unsigned char, unsigned char, unsigned char *);
115a34904b5Sbostic   friend class kern_iterator;
116a34904b5Sbostic };
117a34904b5Sbostic 
118a34904b5Sbostic class kern_iterator {
119a34904b5Sbostic   tfm *t;
120a34904b5Sbostic   int c;
121a34904b5Sbostic   int i;
122a34904b5Sbostic public:
123a34904b5Sbostic   kern_iterator(tfm *);
124a34904b5Sbostic   int next(unsigned char *c1, unsigned char *c2, int *k);
125a34904b5Sbostic };
126a34904b5Sbostic 
127a34904b5Sbostic 
kern_iterator(tfm * p)128a34904b5Sbostic kern_iterator::kern_iterator(tfm *p)
129a34904b5Sbostic : t(p), i(-1), c(t->bc)
130a34904b5Sbostic {
131a34904b5Sbostic }
132a34904b5Sbostic 
next(unsigned char * c1,unsigned char * c2,int * k)133a34904b5Sbostic int kern_iterator::next(unsigned char *c1, unsigned char *c2, int *k)
134a34904b5Sbostic {
135a34904b5Sbostic   for (; c <= t->ec; c++)
136a34904b5Sbostic     if (t->char_info[c - t->bc].tag == 1) {
137a34904b5Sbostic       if (i < 0) {
138a34904b5Sbostic 	i = t->char_info[c - t->bc].remainder;
139a34904b5Sbostic 	if (t->lig_kern[i].skip_byte > 128)
140a34904b5Sbostic 	  i = (256*t->lig_kern[i].op_byte
141a34904b5Sbostic 		   + t->lig_kern[i].remainder);
142a34904b5Sbostic       }
143a34904b5Sbostic       for (;;) {
144a34904b5Sbostic 	int skip = t->lig_kern[i].skip_byte;
145a34904b5Sbostic 	if (skip <= 128 && t->lig_kern[i].op_byte >= 128) {
146a34904b5Sbostic 	  *c1 = c;
147a34904b5Sbostic 	  *c2 = t->lig_kern[i].next_char;
148a34904b5Sbostic 	  *k = t->kern[256*(t->lig_kern[i].op_byte - 128)
149a34904b5Sbostic 		       + t->lig_kern[i].remainder];
150a34904b5Sbostic 	  if (skip == 128) {
151a34904b5Sbostic 	    c++;
152a34904b5Sbostic 	    i = -1;
153a34904b5Sbostic 	  }
154a34904b5Sbostic 	  else
155a34904b5Sbostic 	    i += skip + 1;
156a34904b5Sbostic 	  return 1;
157a34904b5Sbostic 	}
158a34904b5Sbostic 	if (skip >= 128)
159a34904b5Sbostic 	  break;
160a34904b5Sbostic 	i += skip + 1;
161a34904b5Sbostic       }
162a34904b5Sbostic       i = -1;
163a34904b5Sbostic     }
164a34904b5Sbostic   return 0;
165a34904b5Sbostic }
166a34904b5Sbostic 
tfm()167a34904b5Sbostic tfm::tfm()
168a34904b5Sbostic : char_info(0), width(0), height(0), depth(0), italic(0), lig_kern(0),
169a34904b5Sbostic   kern(0), param(0)
170a34904b5Sbostic {
171a34904b5Sbostic }
172a34904b5Sbostic 
get_lig(unsigned char c1,unsigned char c2,unsigned char * cp)173a34904b5Sbostic int tfm::get_lig(unsigned char c1, unsigned char c2, unsigned char *cp)
174a34904b5Sbostic {
175a34904b5Sbostic   if (contains(c1) && char_info[c1 - bc].tag == 1) {
176a34904b5Sbostic     int i = char_info[c1 - bc].remainder;
177a34904b5Sbostic     if (lig_kern[i].skip_byte > 128)
178a34904b5Sbostic       i = 256*lig_kern[i].op_byte + lig_kern[i].remainder;
179a34904b5Sbostic     for (;;) {
180a34904b5Sbostic       int skip = lig_kern[i].skip_byte;
181a34904b5Sbostic       if (skip > 128)
182a34904b5Sbostic 	break;
183a34904b5Sbostic       // We are only interested in normal ligatures, for which
184a34904b5Sbostic       // op_byte == 0.
185a34904b5Sbostic       if (lig_kern[i].op_byte == 0
186a34904b5Sbostic 	  && lig_kern[i].next_char == c2) {
187a34904b5Sbostic 	*cp = lig_kern[i].remainder;
188a34904b5Sbostic 	return 1;
189a34904b5Sbostic       }
190a34904b5Sbostic       if (skip == 128)
191a34904b5Sbostic 	break;
192a34904b5Sbostic       i += skip + 1;
193a34904b5Sbostic     }
194a34904b5Sbostic   }
195a34904b5Sbostic   return 0;
196a34904b5Sbostic }
197a34904b5Sbostic 
contains(int i)198a34904b5Sbostic int tfm::contains(int i)
199a34904b5Sbostic {
200a34904b5Sbostic   return i >= bc && i <= ec && char_info[i - bc].width_index != 0;
201a34904b5Sbostic }
202a34904b5Sbostic 
get_width(int i)203a34904b5Sbostic int tfm::get_width(int i)
204a34904b5Sbostic {
205a34904b5Sbostic   return width[char_info[i - bc].width_index];
206a34904b5Sbostic }
207a34904b5Sbostic 
get_height(int i)208a34904b5Sbostic int tfm::get_height(int i)
209a34904b5Sbostic {
210a34904b5Sbostic   return height[char_info[i - bc].height_index];
211a34904b5Sbostic }
212a34904b5Sbostic 
get_depth(int i)213a34904b5Sbostic int tfm::get_depth(int i)
214a34904b5Sbostic {
215a34904b5Sbostic   return depth[char_info[i - bc].depth_index];
216a34904b5Sbostic }
217a34904b5Sbostic 
get_italic(int i)218a34904b5Sbostic int tfm::get_italic(int i)
219a34904b5Sbostic {
220a34904b5Sbostic   return italic[char_info[i - bc].italic_index];
221a34904b5Sbostic }
222a34904b5Sbostic 
get_param(int i,int * p)223a34904b5Sbostic int tfm::get_param(int i, int *p)
224a34904b5Sbostic {
225a34904b5Sbostic   if (i <= 0 || i > np)
226a34904b5Sbostic     return 0;
227a34904b5Sbostic   else {
228a34904b5Sbostic     *p = param[i - 1];
229a34904b5Sbostic     return 1;
230a34904b5Sbostic   }
231a34904b5Sbostic }
232a34904b5Sbostic 
get_checksum()233a34904b5Sbostic int tfm::get_checksum()
234a34904b5Sbostic {
235a34904b5Sbostic   return cs;
236a34904b5Sbostic }
237a34904b5Sbostic 
get_design_size()238a34904b5Sbostic int tfm::get_design_size()
239a34904b5Sbostic {
240a34904b5Sbostic   return ds;
241a34904b5Sbostic }
242a34904b5Sbostic 
~tfm()243a34904b5Sbostic tfm::~tfm()
244a34904b5Sbostic {
245a34904b5Sbostic   a_delete char_info;
246a34904b5Sbostic   a_delete width;
247a34904b5Sbostic   a_delete height;
248a34904b5Sbostic   a_delete depth;
249a34904b5Sbostic   a_delete italic;
250a34904b5Sbostic   a_delete lig_kern;
251a34904b5Sbostic   a_delete kern;
252a34904b5Sbostic   a_delete param;
253a34904b5Sbostic }
254a34904b5Sbostic 
read2(unsigned char * & s)255a34904b5Sbostic int read2(unsigned char *&s)
256a34904b5Sbostic {
257a34904b5Sbostic   int n;
258a34904b5Sbostic   n = *s++ << 8;
259a34904b5Sbostic   n |= *s++;
260a34904b5Sbostic   return n;
261a34904b5Sbostic }
262a34904b5Sbostic 
read4(unsigned char * & s)263a34904b5Sbostic int read4(unsigned char *&s)
264a34904b5Sbostic {
265a34904b5Sbostic   int n;
266a34904b5Sbostic   n = *s++ << 24;
267a34904b5Sbostic   n |= *s++ << 16;
268a34904b5Sbostic   n |= *s++ << 8;
269a34904b5Sbostic   n |= *s++;
270a34904b5Sbostic   return n;
271a34904b5Sbostic }
272a34904b5Sbostic 
273a34904b5Sbostic 
load(const char * file)274a34904b5Sbostic int tfm::load(const char *file)
275a34904b5Sbostic {
276a34904b5Sbostic   errno = 0;
277a34904b5Sbostic   FILE *fp = fopen(file, "r");
278a34904b5Sbostic   if (!fp) {
279a34904b5Sbostic     error("can't open `%1': %2", file, strerror(errno));
280a34904b5Sbostic     return 0;
281a34904b5Sbostic   }
282a34904b5Sbostic   int c1 = getc(fp);
283a34904b5Sbostic   int c2 = getc(fp);
284a34904b5Sbostic   if (c1 == EOF || c2 == EOF) {
285a34904b5Sbostic     fclose(fp);
286a34904b5Sbostic     error("unexpected end of file on `%1'", file);
287a34904b5Sbostic     return 0;
288a34904b5Sbostic   }
289a34904b5Sbostic   int lf = (c1 << 8) + c2;
290a34904b5Sbostic   int toread = lf*4 - 2;
291a34904b5Sbostic   unsigned char *buf = new unsigned char[toread];
292a34904b5Sbostic   if (fread(buf, 1, toread, fp) != toread) {
293a34904b5Sbostic     if (feof(fp))
294a34904b5Sbostic       error("unexpected end of file on `%1'", file);
295a34904b5Sbostic     else
296a34904b5Sbostic       error("error on file `%1'", file);
297a34904b5Sbostic     a_delete buf;
298a34904b5Sbostic     fclose(fp);
299a34904b5Sbostic     return 0;
300a34904b5Sbostic   }
301a34904b5Sbostic   fclose(fp);
302a34904b5Sbostic   if (lf < 6) {
303a34904b5Sbostic     error("bad tfm file `%1': impossibly short", file);
304a34904b5Sbostic     a_delete buf;
305a34904b5Sbostic     return 0;
306a34904b5Sbostic   }
307a34904b5Sbostic   unsigned char *ptr = buf;
308a34904b5Sbostic   int lh = read2(ptr);
309a34904b5Sbostic   bc = read2(ptr);
310a34904b5Sbostic   ec = read2(ptr);
311a34904b5Sbostic   nw = read2(ptr);
312a34904b5Sbostic   nh = read2(ptr);
313a34904b5Sbostic   nd = read2(ptr);
314a34904b5Sbostic   ni = read2(ptr);
315a34904b5Sbostic   nl = read2(ptr);
316a34904b5Sbostic   nk = read2(ptr);
317a34904b5Sbostic   int ne = read2(ptr);
318a34904b5Sbostic   np = read2(ptr);
319a34904b5Sbostic   if (6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np != lf) {
320a34904b5Sbostic     error("bad tfm file `%1': lengths do not sum", file);
321a34904b5Sbostic     a_delete buf;
322a34904b5Sbostic     return 0;
323a34904b5Sbostic   }
324a34904b5Sbostic   if (lh < 2) {
325a34904b5Sbostic     error("bad tfm file `%1': header too short", file);
326a34904b5Sbostic     a_delete buf;
327a34904b5Sbostic     return 0;
328a34904b5Sbostic   }
329a34904b5Sbostic   char_info = new char_info_word[ec - bc + 1];
330a34904b5Sbostic   width = new int[nw];
331a34904b5Sbostic   height = new int[nh];
332a34904b5Sbostic   depth = new int[nd];
333a34904b5Sbostic   italic = new int[ni];
334a34904b5Sbostic   lig_kern = new lig_kern_command[nl];
335a34904b5Sbostic   kern = new int[nk];
336a34904b5Sbostic   param = new int[np];
337a34904b5Sbostic   int i;
338a34904b5Sbostic   cs = read4(ptr);
339a34904b5Sbostic   ds = read4(ptr);
340a34904b5Sbostic   ptr += (lh-2)*4;
341a34904b5Sbostic   for (i = 0; i < ec - bc + 1; i++) {
342a34904b5Sbostic     char_info[i].width_index = *ptr++;
343a34904b5Sbostic     unsigned char tem = *ptr++;
344a34904b5Sbostic     char_info[i].depth_index = tem & 0xf;
345a34904b5Sbostic     char_info[i].height_index = tem >> 4;
346a34904b5Sbostic     tem = *ptr++;
347a34904b5Sbostic     char_info[i].italic_index = tem >> 2;
348a34904b5Sbostic     char_info[i].tag = tem & 3;
349a34904b5Sbostic     char_info[i].remainder = *ptr++;
350a34904b5Sbostic   }
351a34904b5Sbostic   for (i = 0; i < nw; i++)
352a34904b5Sbostic     width[i] = read4(ptr);
353a34904b5Sbostic   for (i = 0; i < nh; i++)
354a34904b5Sbostic     height[i] = read4(ptr);
355a34904b5Sbostic   for (i = 0; i < nd; i++)
356a34904b5Sbostic     depth[i] = read4(ptr);
357a34904b5Sbostic   for (i = 0; i < ni; i++)
358a34904b5Sbostic     italic[i] = read4(ptr);
359a34904b5Sbostic   for (i = 0; i < nl; i++) {
360a34904b5Sbostic     lig_kern[i].skip_byte = *ptr++;
361a34904b5Sbostic     lig_kern[i].next_char = *ptr++;
362a34904b5Sbostic     lig_kern[i].op_byte = *ptr++;
363a34904b5Sbostic     lig_kern[i].remainder = *ptr++;
364a34904b5Sbostic   }
365a34904b5Sbostic   for (i = 0; i < nk; i++)
366a34904b5Sbostic     kern[i] = read4(ptr);
367a34904b5Sbostic   ptr += ne*4;
368a34904b5Sbostic   for (i = 0; i < np; i++)
369a34904b5Sbostic     param[i] = read4(ptr);
370a34904b5Sbostic   assert(ptr == buf + lf*4 - 2);
371a34904b5Sbostic   a_delete buf;
372a34904b5Sbostic   return 1;
373a34904b5Sbostic }
374a34904b5Sbostic 
375a34904b5Sbostic class gf {
376a34904b5Sbostic   int left[256];
377a34904b5Sbostic   int right[256];
378a34904b5Sbostic   static int sread4(int *p, FILE *fp);
379a34904b5Sbostic   static int uread3(int *p, FILE *fp);
380a34904b5Sbostic   static int uread2(int *p, FILE *fp);
381a34904b5Sbostic   static int skip(int n, FILE *fp);
382a34904b5Sbostic public:
383a34904b5Sbostic   gf();
384a34904b5Sbostic   int load(const char *file);
get_left_adjustment(int i)385a34904b5Sbostic   int get_left_adjustment(int i) { return left[i]; }
get_right_adjustment(int i)386a34904b5Sbostic   int get_right_adjustment(int i) { return right[i]; }
387a34904b5Sbostic };
388a34904b5Sbostic 
gf()389a34904b5Sbostic gf::gf()
390a34904b5Sbostic {
391a34904b5Sbostic   for (int i = 0; i < 256; i++)
392a34904b5Sbostic     left[i] = right[i] = 0;
393a34904b5Sbostic }
394a34904b5Sbostic 
load(const char * file)395a34904b5Sbostic int gf::load(const char *file)
396a34904b5Sbostic {
397a34904b5Sbostic   enum {
398a34904b5Sbostic     paint_0 = 0,
399a34904b5Sbostic     paint1 = 64,
400a34904b5Sbostic     boc = 67,
401a34904b5Sbostic     boc1 = 68,
402a34904b5Sbostic     eoc = 69,
403a34904b5Sbostic     skip0 = 70,
404a34904b5Sbostic     skip1 = 71,
405a34904b5Sbostic     new_row_0 = 74,
406a34904b5Sbostic     xxx1 = 239,
407a34904b5Sbostic     yyy = 243,
408a34904b5Sbostic     no_op = 244,
409a34904b5Sbostic     pre = 247,
410a34904b5Sbostic     post = 248
411a34904b5Sbostic   };
412a34904b5Sbostic   int got_an_adjustment = 0;
413a34904b5Sbostic   int pending_adjustment = 0;
414a34904b5Sbostic   int left_adj, right_adj;
415a34904b5Sbostic   const int gf_id_byte = 131;
416a34904b5Sbostic   errno = 0;
417a34904b5Sbostic   FILE *fp = fopen(file, "r");
418a34904b5Sbostic   if (!fp) {
419a34904b5Sbostic     error("can't open `%1': %2", file, strerror(errno));
420a34904b5Sbostic     return 0;
421a34904b5Sbostic   }
422a34904b5Sbostic   if (getc(fp) != pre || getc(fp) != gf_id_byte) {
423a34904b5Sbostic     error("bad gf file");
424a34904b5Sbostic     return 0;
425a34904b5Sbostic   }
426a34904b5Sbostic   int n = getc(fp);
427a34904b5Sbostic   if (n == EOF)
428a34904b5Sbostic     goto eof;
429a34904b5Sbostic   if (!skip(n, fp))
430a34904b5Sbostic     goto eof;
431a34904b5Sbostic   for (;;) {
432a34904b5Sbostic     int op = getc(fp);
433a34904b5Sbostic     if (op == EOF)
434a34904b5Sbostic       goto eof;
435a34904b5Sbostic     if (op == post)
436a34904b5Sbostic       break;
437a34904b5Sbostic     if ((op >= paint_0 && op <= paint_0 + 63)
438a34904b5Sbostic 	|| (op >= new_row_0 && op <= new_row_0 + 164))
439a34904b5Sbostic       continue;
440a34904b5Sbostic     switch (op) {
441a34904b5Sbostic     case no_op:
442a34904b5Sbostic     case eoc:
443a34904b5Sbostic     case skip0:
444a34904b5Sbostic       break;
445a34904b5Sbostic     case paint1:
446a34904b5Sbostic     case skip1:
447a34904b5Sbostic       if (!skip(1, fp))
448a34904b5Sbostic 	goto eof;
449a34904b5Sbostic       break;
450a34904b5Sbostic     case paint1 + 1:
451a34904b5Sbostic     case skip1 + 1:
452a34904b5Sbostic       if (!skip(2, fp))
453a34904b5Sbostic 	goto eof;
454a34904b5Sbostic       break;
455a34904b5Sbostic     case paint1 + 2:
456a34904b5Sbostic     case skip1 + 2:
457a34904b5Sbostic       if (!skip(3, fp))
458a34904b5Sbostic 	goto eof;
459a34904b5Sbostic       break;
460a34904b5Sbostic     case boc:
461a34904b5Sbostic       {
462a34904b5Sbostic 	int code;
463a34904b5Sbostic 	if (!sread4(&code, fp))
464a34904b5Sbostic 	  goto eof;
465a34904b5Sbostic 	if (pending_adjustment) {
466a34904b5Sbostic 	  pending_adjustment = 0;
467a34904b5Sbostic 	  left[code & 0377] = left_adj;
468a34904b5Sbostic 	  right[code & 0377] = right_adj;
469a34904b5Sbostic 	}
470a34904b5Sbostic 	if (!skip(20, fp))
471a34904b5Sbostic 	  goto eof;
472a34904b5Sbostic 	break;
473a34904b5Sbostic       }
474a34904b5Sbostic     case boc1:
475a34904b5Sbostic       {
476a34904b5Sbostic 	int code = getc(fp);
477a34904b5Sbostic 	if (code == EOF)
478a34904b5Sbostic 	  goto eof;
479a34904b5Sbostic 	if (pending_adjustment) {
480a34904b5Sbostic 	  pending_adjustment = 0;
481a34904b5Sbostic 	  left[code] = left_adj;
482a34904b5Sbostic 	  right[code] = right_adj;
483a34904b5Sbostic 	}
484a34904b5Sbostic 	if (!skip(4, fp))
485a34904b5Sbostic 	  goto eof;
486a34904b5Sbostic 	break;
487a34904b5Sbostic       }
488a34904b5Sbostic     case xxx1:
489a34904b5Sbostic       {
490a34904b5Sbostic 	int len = getc(fp);
491a34904b5Sbostic 	if (len == EOF)
492a34904b5Sbostic 	  goto eof;
493a34904b5Sbostic 	char buf[256];
494a34904b5Sbostic 	if (fread(buf, 1, len, fp) != len)
495a34904b5Sbostic 	  goto eof;
496a34904b5Sbostic 	if (len == 10 /* strlen("adjustment") */
497a34904b5Sbostic 	    && memcmp(buf, "adjustment", len) == 0) {
498a34904b5Sbostic 	  int c = getc(fp);
499a34904b5Sbostic 	  if (c != yyy) {
500a34904b5Sbostic 	    if (c != EOF)
501a34904b5Sbostic 	      ungetc(c, fp);
502a34904b5Sbostic 	    break;
503a34904b5Sbostic 	  }
504a34904b5Sbostic 	  if (!sread4(&left_adj, fp))
505a34904b5Sbostic 	    goto eof;
506a34904b5Sbostic 	  c = getc(fp);
507a34904b5Sbostic 	  if (c != yyy) {
508a34904b5Sbostic 	    if (c != EOF)
509a34904b5Sbostic 	      ungetc(c, fp);
510a34904b5Sbostic 	    break;
511a34904b5Sbostic 	  }
512a34904b5Sbostic 	  if (!sread4(&right_adj, fp))
513a34904b5Sbostic 	    goto eof;
514a34904b5Sbostic 	  got_an_adjustment = 1;
515a34904b5Sbostic 	  pending_adjustment = 1;
516a34904b5Sbostic 	}
517a34904b5Sbostic 	break;
518a34904b5Sbostic       }
519a34904b5Sbostic     case xxx1 + 1:
520a34904b5Sbostic       if (!uread2(&n, fp) || !skip(n, fp))
521a34904b5Sbostic 	goto eof;
522a34904b5Sbostic       break;
523a34904b5Sbostic     case xxx1 + 2:
524a34904b5Sbostic       if (!uread3(&n, fp) || !skip(n, fp))
525a34904b5Sbostic 	goto eof;
526a34904b5Sbostic       break;
527a34904b5Sbostic     case xxx1 + 3:
528a34904b5Sbostic       if (!sread4(&n, fp) || !skip(n, fp))
529a34904b5Sbostic 	goto eof;
530a34904b5Sbostic       break;
531a34904b5Sbostic     case yyy:
532a34904b5Sbostic       if (!skip(4, fp))
533a34904b5Sbostic 	goto eof;
534a34904b5Sbostic       break;
535a34904b5Sbostic     default:
536a34904b5Sbostic       fatal("unrecognized opcode `%1'", op);
537a34904b5Sbostic       break;
538a34904b5Sbostic     }
539a34904b5Sbostic   }
540a34904b5Sbostic   if (!got_an_adjustment)
541a34904b5Sbostic     warning("no adjustment specials found in gf file");
542a34904b5Sbostic   return 1;
543a34904b5Sbostic  eof:
544a34904b5Sbostic   error("unexpected end of file");
545a34904b5Sbostic   return 0;
546a34904b5Sbostic }
547a34904b5Sbostic 
sread4(int * p,FILE * fp)548a34904b5Sbostic int gf::sread4(int *p, FILE *fp)
549a34904b5Sbostic {
550a34904b5Sbostic   *p = getc(fp);
551a34904b5Sbostic   if (*p >= 128)
552a34904b5Sbostic     *p -= 256;
553a34904b5Sbostic   *p <<= 8;
554a34904b5Sbostic   *p |= getc(fp);
555a34904b5Sbostic   *p <<= 8;
556a34904b5Sbostic   *p |= getc(fp);
557a34904b5Sbostic   *p <<= 8;
558a34904b5Sbostic   *p |= getc(fp);
559a34904b5Sbostic   return !ferror(fp) && !feof(fp);
560a34904b5Sbostic }
561a34904b5Sbostic 
uread3(int * p,FILE * fp)562a34904b5Sbostic int gf::uread3(int *p, FILE *fp)
563a34904b5Sbostic {
564a34904b5Sbostic   *p = getc(fp);
565a34904b5Sbostic   *p <<= 8;
566a34904b5Sbostic   *p |= getc(fp);
567a34904b5Sbostic   *p <<= 8;
568a34904b5Sbostic   *p |= getc(fp);
569a34904b5Sbostic   return !ferror(fp) && !feof(fp);
570a34904b5Sbostic }
571a34904b5Sbostic 
uread2(int * p,FILE * fp)572a34904b5Sbostic int gf::uread2(int *p, FILE *fp)
573a34904b5Sbostic {
574a34904b5Sbostic   *p = getc(fp);
575a34904b5Sbostic   *p <<= 8;
576a34904b5Sbostic   *p |= getc(fp);
577a34904b5Sbostic   return !ferror(fp) && !feof(fp);
578a34904b5Sbostic }
579a34904b5Sbostic 
skip(int n,FILE * fp)580a34904b5Sbostic int gf::skip(int n, FILE *fp)
581a34904b5Sbostic {
582a34904b5Sbostic   while (--n >= 0)
583a34904b5Sbostic     if (getc(fp) == EOF)
584a34904b5Sbostic       return 0;
585a34904b5Sbostic   return 1;
586a34904b5Sbostic }
587a34904b5Sbostic 
588a34904b5Sbostic 
589a34904b5Sbostic struct char_list {
590a34904b5Sbostic   char *ch;
591a34904b5Sbostic   char_list *next;
592a34904b5Sbostic   char_list(const char *, char_list * = 0);
593a34904b5Sbostic };
594a34904b5Sbostic 
char_list(const char * s,char_list * p)595a34904b5Sbostic char_list::char_list(const char *s, char_list *p) : ch(strsave(s)), next(p)
596a34904b5Sbostic {
597a34904b5Sbostic }
598a34904b5Sbostic 
599a34904b5Sbostic 
read_map(const char * file,char_list ** table)600a34904b5Sbostic int read_map(const char *file, char_list **table)
601a34904b5Sbostic {
602a34904b5Sbostic   errno = 0;
603a34904b5Sbostic   FILE *fp = fopen(file, "r");
604a34904b5Sbostic   if (!fp) {
605a34904b5Sbostic     error("can't open `%1': %2", file, strerror(errno));
606a34904b5Sbostic     return 0;
607a34904b5Sbostic   }
608a34904b5Sbostic   for (int i = 0; i < 256; i++)
609a34904b5Sbostic     table[i] = 0;
610a34904b5Sbostic   char buf[512];
611a34904b5Sbostic   int lineno = 0;
612a34904b5Sbostic   while (fgets(buf, int(sizeof(buf)), fp)) {
613a34904b5Sbostic     lineno++;
614a34904b5Sbostic     char *ptr = buf;
615a34904b5Sbostic     while (csspace(*ptr))
616a34904b5Sbostic       ptr++;
617a34904b5Sbostic     if (*ptr == '\0' || *ptr == '#')
618a34904b5Sbostic       continue;
619a34904b5Sbostic     ptr = strtok(ptr, " \n\t");
620a34904b5Sbostic     if (!ptr)
621a34904b5Sbostic       continue;
622a34904b5Sbostic     int n;
623a34904b5Sbostic     if (sscanf(ptr, "%d", &n) != 1) {
624a34904b5Sbostic       error("%1:%2: bad map file", file, lineno);
625a34904b5Sbostic       fclose(fp);
626a34904b5Sbostic       return 0;
627a34904b5Sbostic     }
628a34904b5Sbostic     if (n < 0 || n > 255) {
629a34904b5Sbostic       error("%1:%2: code out of range", file, lineno);
630a34904b5Sbostic       fclose(fp);
631a34904b5Sbostic       return 0;
632a34904b5Sbostic     }
633a34904b5Sbostic     ptr = strtok(0, " \n\t");
634a34904b5Sbostic     if (!ptr) {
635a34904b5Sbostic       error("%1:%2: missing names", file, lineno);
636a34904b5Sbostic       fclose(fp);
637a34904b5Sbostic       return 0;
638a34904b5Sbostic     }
639a34904b5Sbostic     for (; ptr; ptr = strtok(0, " \n\t"))
640a34904b5Sbostic       table[n] = new char_list(ptr, table[n]);
641a34904b5Sbostic   }
642a34904b5Sbostic   fclose(fp);
643a34904b5Sbostic   return 1;
644a34904b5Sbostic }
645a34904b5Sbostic 
646a34904b5Sbostic 
647a34904b5Sbostic /* Every character that can participate in a ligature appears in the
648a34904b5Sbostic lig_chars table. `ch' gives the full-name of the character, `name'
649a34904b5Sbostic gives the groff name of the character, `i' gives its index in
650a34904b5Sbostic the encoding, which is filled in later  (-1 if it does not appear). */
651a34904b5Sbostic 
652a34904b5Sbostic struct {
653a34904b5Sbostic   const char *ch;
654a34904b5Sbostic   int i;
655a34904b5Sbostic } lig_chars[] = {
656a34904b5Sbostic   "f", -1,
657a34904b5Sbostic   "i", -1,
658a34904b5Sbostic   "l", -1,
659a34904b5Sbostic   "ff", -1,
660a34904b5Sbostic   "fi", -1,
661a34904b5Sbostic   "fl", -1,
662a34904b5Sbostic   "Fi", -1,
663a34904b5Sbostic   "Fl", -1,
664a34904b5Sbostic };
665a34904b5Sbostic 
666a34904b5Sbostic // Indices into lig_chars[].
667a34904b5Sbostic 
668a34904b5Sbostic enum { CH_f, CH_i, CH_l, CH_ff, CH_fi, CH_fl, CH_ffi, CH_ffl };
669a34904b5Sbostic 
670a34904b5Sbostic // Each possible ligature appears in this table.
671a34904b5Sbostic 
672a34904b5Sbostic struct {
673a34904b5Sbostic   unsigned char c1, c2, res;
674a34904b5Sbostic   const char *ch;
675a34904b5Sbostic } lig_table[] = {
676a34904b5Sbostic   CH_f, CH_f, CH_ff, "ff",
677a34904b5Sbostic   CH_f, CH_i, CH_fi, "fi",
678a34904b5Sbostic   CH_f, CH_l, CH_fl, "fl",
679a34904b5Sbostic   CH_ff, CH_i, CH_ffi, "ffi",
680a34904b5Sbostic   CH_ff, CH_l, CH_ffl, "ffl",
681a34904b5Sbostic   };
682a34904b5Sbostic 
683a34904b5Sbostic static void usage();
684a34904b5Sbostic 
main(int argc,char ** argv)685a34904b5Sbostic int main(int argc, char **argv)
686a34904b5Sbostic {
687a34904b5Sbostic   program_name = argv[0];
688a34904b5Sbostic   int special_flag = 0;
689a34904b5Sbostic   int skewchar = -1;
690a34904b5Sbostic   int opt;
691a34904b5Sbostic   const char *gf_file = 0;
692a34904b5Sbostic   while ((opt = getopt(argc, argv, "svg:k:")) != EOF)
693a34904b5Sbostic     switch (opt) {
694a34904b5Sbostic     case 'g':
695a34904b5Sbostic       gf_file = optarg;
696a34904b5Sbostic       break;
697a34904b5Sbostic     case 's':
698a34904b5Sbostic       special_flag = 1;
699a34904b5Sbostic       break;
700a34904b5Sbostic     case 'k':
701a34904b5Sbostic       {
702a34904b5Sbostic 	char *ptr;
703a34904b5Sbostic 	long n = strtol(optarg, &ptr, 0);
704a34904b5Sbostic 	if ((n == 0 && ptr == optarg)
705a34904b5Sbostic 	    || *ptr != '\0'
706a34904b5Sbostic 	    || n < 0
707a34904b5Sbostic 	    || n > UCHAR_MAX)
708a34904b5Sbostic 	  error("invalid skewchar");
709a34904b5Sbostic 	else
710a34904b5Sbostic 	  skewchar = (int)n;
711a34904b5Sbostic 	break;
712a34904b5Sbostic       }
713a34904b5Sbostic     case 'v':
714a34904b5Sbostic       {
715a34904b5Sbostic 	extern const char *version_string;
716a34904b5Sbostic 	fprintf(stderr, "tfmtodit version %s\n", version_string);
717a34904b5Sbostic 	fflush(stderr);
718a34904b5Sbostic 	break;
719a34904b5Sbostic       }
720a34904b5Sbostic     case '?':
721a34904b5Sbostic       usage();
722a34904b5Sbostic       break;
723a34904b5Sbostic     case EOF:
724a34904b5Sbostic       assert(0);
725a34904b5Sbostic     }
726a34904b5Sbostic   if (argc - optind != 3)
727a34904b5Sbostic     usage();
728a34904b5Sbostic   gf g;
729a34904b5Sbostic   if (gf_file) {
730a34904b5Sbostic     if (!g.load(gf_file))
731a34904b5Sbostic       return 1;
732a34904b5Sbostic   }
733a34904b5Sbostic   const char *tfm_file = argv[optind];
734a34904b5Sbostic   const char *map_file = argv[optind + 1];
735a34904b5Sbostic   const char *font_file = argv[optind + 2];
736a34904b5Sbostic   tfm t;
737a34904b5Sbostic   if (!t.load(tfm_file))
738a34904b5Sbostic     return 1;
739a34904b5Sbostic   char_list *table[256];
740a34904b5Sbostic   if (!read_map(map_file, table))
741a34904b5Sbostic     return 1;
742a34904b5Sbostic   errno = 0;
743a34904b5Sbostic   if (!freopen(font_file, "w", stdout)) {
744a34904b5Sbostic     error("can't open `%1' for writing: %2", font_file, strerror(errno));
745a34904b5Sbostic     return 1;
746a34904b5Sbostic   }
747a34904b5Sbostic   printf("name %s\n", font_file);
748a34904b5Sbostic   if (special_flag)
749a34904b5Sbostic     fputs("special\n", stdout);
750a34904b5Sbostic   char *internal_name = strsave(argv[optind]);
751a34904b5Sbostic   int len = strlen(internal_name);
752a34904b5Sbostic   if (len > 4 && strcmp(internal_name + len - 4, ".tfm") == 0)
753a34904b5Sbostic     internal_name[len - 4] = '\0';
754a34904b5Sbostic   char *s = strrchr(internal_name, '/');
755a34904b5Sbostic   printf("internalname %s\n", s ? s + 1 : internal_name);
756a34904b5Sbostic   int n;
757a34904b5Sbostic   if (t.get_param(2, &n)) {
758a34904b5Sbostic     if (n > 0)
759a34904b5Sbostic       printf("spacewidth %d\n", n*MULTIPLIER);
760a34904b5Sbostic   }
761a34904b5Sbostic   if (t.get_param(1, &n) && n != 0)
762a34904b5Sbostic     printf("slant %f\n", atan2(n/double(1<<20), 1.0)*180.0/M_PI);
763a34904b5Sbostic   int xheight;
764a34904b5Sbostic   if (!t.get_param(5, &xheight))
765a34904b5Sbostic     xheight = 0;
766a34904b5Sbostic   int i;
767a34904b5Sbostic   // Print the list of ligatures.
768a34904b5Sbostic   // First find the indices of each character that can participate in
769a34904b5Sbostic   // a ligature.
770a34904b5Sbostic   for (i = 0; i < 256; i++)
771a34904b5Sbostic     for (int j = 0; j < sizeof(lig_chars)/sizeof(lig_chars[0]); j++)
772a34904b5Sbostic       for (char_list *p = table[i]; p; p = p->next)
773a34904b5Sbostic 	if (strcmp(lig_chars[j].ch, p->ch) == 0)
774a34904b5Sbostic 	  lig_chars[j].i = i;
775a34904b5Sbostic   // For each possible ligature, if its participants all exist,
776a34904b5Sbostic   // and it appears as a ligature in the tfm file, include in
777a34904b5Sbostic   // the list of ligatures.
778a34904b5Sbostic   int started = 0;
779a34904b5Sbostic   for (i = 0; i < sizeof(lig_table)/sizeof(lig_table[0]); i++) {
780a34904b5Sbostic     int i1 = lig_chars[lig_table[i].c1].i;
781a34904b5Sbostic     int i2 = lig_chars[lig_table[i].c2].i;
782a34904b5Sbostic     int r = lig_chars[lig_table[i].res].i;
783a34904b5Sbostic     if (i1 >= 0 && i2 >= 0 && r >= 0) {
784a34904b5Sbostic       unsigned char c;
785a34904b5Sbostic       if (t.get_lig(i1, i2, &c) && c == r) {
786a34904b5Sbostic 	if (!started) {
787a34904b5Sbostic 	  started = 1;
788a34904b5Sbostic 	  fputs("ligatures", stdout);
789a34904b5Sbostic 	}
790a34904b5Sbostic 	printf(" %s", lig_table[i].ch);
791a34904b5Sbostic       }
792a34904b5Sbostic     }
793a34904b5Sbostic   }
794a34904b5Sbostic   if (started)
795a34904b5Sbostic     fputs(" 0\n", stdout);
796a34904b5Sbostic   printf("checksum %d\n", t.get_checksum());
797a34904b5Sbostic   printf("designsize %d\n", t.get_design_size());
798a34904b5Sbostic   // Now print out the kerning information.
799a34904b5Sbostic   int had_kern = 0;
800a34904b5Sbostic   kern_iterator iter(&t);
801a34904b5Sbostic   unsigned char c1, c2;
802a34904b5Sbostic   int k;
803a34904b5Sbostic   while (iter.next(&c1, &c2, &k))
804a34904b5Sbostic     if (c2 != skewchar) {
805a34904b5Sbostic       k *= MULTIPLIER;
806a34904b5Sbostic       char_list *q = table[c2];
807a34904b5Sbostic       for (char_list *p1 = table[c1]; p1; p1 = p1->next)
808a34904b5Sbostic 	for (char_list *p2 = q; p2; p2 = p2->next) {
809a34904b5Sbostic 	  if (!had_kern) {
810a34904b5Sbostic 	    printf("kernpairs\n");
811a34904b5Sbostic 	    had_kern = 1;
812a34904b5Sbostic 	  }
813a34904b5Sbostic 	  printf("%s %s %d\n", p1->ch, p2->ch, k);
814a34904b5Sbostic 	}
815a34904b5Sbostic     }
816a34904b5Sbostic   printf("charset\n");
817a34904b5Sbostic   char_list unnamed("---");
818a34904b5Sbostic   for (i = 0; i < 256; i++)
819a34904b5Sbostic     if (t.contains(i)) {
820a34904b5Sbostic       char_list *p = table[i] ? table[i] : &unnamed;
821a34904b5Sbostic       int m[6];
822a34904b5Sbostic       m[0] = t.get_width(i);
823a34904b5Sbostic       m[1] = t.get_height(i);
824a34904b5Sbostic       m[2] = t.get_depth(i);
825a34904b5Sbostic       m[3] = t.get_italic(i);
826a34904b5Sbostic       m[4] = g.get_left_adjustment(i);
827a34904b5Sbostic       m[5] = g.get_right_adjustment(i);
828a34904b5Sbostic       printf("%s\t%d", p->ch, m[0]*MULTIPLIER);
829a34904b5Sbostic       for (int j = int(sizeof(m)/sizeof(m[0])) - 1; j > 0; j--)
830a34904b5Sbostic 	if (m[j] != 0)
831a34904b5Sbostic 	  break;
832a34904b5Sbostic       for (int k = 1; k <= j; k++)
833a34904b5Sbostic 	printf(",%d", m[k]*MULTIPLIER);
834a34904b5Sbostic       int type = 0;
835a34904b5Sbostic       if (m[2] > 0)
836a34904b5Sbostic 	type = 1;
837a34904b5Sbostic       if (m[1] > xheight)
838a34904b5Sbostic 	type += 2;
839a34904b5Sbostic       printf("\t%d\t%04o\n", type, i);
840a34904b5Sbostic       for (p = p->next; p; p = p->next)
841a34904b5Sbostic 	printf("%s\t\"\n", p->ch);
842a34904b5Sbostic     }
843a34904b5Sbostic   return 0;
844a34904b5Sbostic }
845a34904b5Sbostic 
usage()846a34904b5Sbostic static void usage()
847a34904b5Sbostic {
848a34904b5Sbostic   fprintf(stderr, "usage: %s [-sv] [-g gf_file] [-k skewchar] tfm_file map_file font\n",
849a34904b5Sbostic 	  program_name);
850a34904b5Sbostic   exit(1);
851a34904b5Sbostic }
852