1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989-1992, 2000, 2001, 2004 Free Software Foundation, Inc.
3114402Sru     Written by James Clark (jjc@jclark.com)
4114402Sru
5114402SruThis file is part of groff.
6114402Sru
7114402Srugroff is free software; you can redistribute it and/or modify it under
8114402Sruthe terms of the GNU General Public License as published by the Free
9114402SruSoftware Foundation; either version 2, or (at your option) any later
10114402Sruversion.
11114402Sru
12114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
13114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
14114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15114402Srufor more details.
16114402Sru
17114402SruYou should have received a copy of the GNU General Public License along
18114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
19151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
20114402Sru
21114402Sru/* I have tried to incorporate the changes needed for TeX 3.0 tfm files,
22114402Srubut I haven't tested them. */
23114402Sru
24114402Sru/* Groff requires more font metric information than TeX.  The reason
25114402Srufor this is that TeX has separate Math Italic fonts, whereas groff
26114402Sruuses normal italic fonts for math.  The two additional pieces of
27114402Sruinformation required by groff correspond to the two arguments to the
28114402Srumath_fit() macro in the Metafont programs for the CM fonts. In the
29114402Srucase of a font for which math_fitting is false, these two arguments
30114402Sruare normally ignored by Metafont. We need to get hold of these two
31114402Sruparameters and put them in the groff font file.
32114402Sru
33114402SruWe do this by loading this definition after cmbase when creating cm.base.
34114402Sru
35114402Srudef ignore_math_fit(expr left_adjustment,right_adjustment) =
36114402Sru special "adjustment";
37114402Sru numspecial left_adjustment*16/designsize;
38114402Sru numspecial right_adjustment*16/designsize;
39114402Sru enddef;
40114402Sru
41114402SruThis puts the two arguments to the math_fit macro into the gf file.
42114402Sru(They will appear in the gf file immediately before the character to
43114402Sruwhich they apply.)  We then create a gf file using this cm.base.  Then
44114402Sruwe run tfmtodit and specify this gf file with the -g option.
45114402Sru
46114402SruThis need only be done for a font for which math_fitting is false;
47114402SruWhen it's true, the left_correction and subscript_correction should
48114402Sruboth be zero. */
49114402Sru
50114402Sru#include "lib.h"
51114402Sru
52114402Sru#include <stdlib.h>
53114402Sru#include <math.h>
54114402Sru#include <errno.h>
55114402Sru#include "errarg.h"
56114402Sru#include "error.h"
57114402Sru#include "assert.h"
58114402Sru#include "cset.h"
59114402Sru#include "nonposix.h"
60114402Sru
61114402Sruextern "C" const char *Version_string;
62114402Sru
63114402Sru/* Values in the tfm file should be multiplied by this. */
64114402Sru
65114402Sru#define MULTIPLIER 1
66114402Sru
67114402Srustruct char_info_word {
68114402Sru  unsigned char width_index;
69114402Sru  char height_index;
70114402Sru  char depth_index;
71114402Sru  char italic_index;
72114402Sru  char tag;
73114402Sru  unsigned char remainder;
74114402Sru};
75114402Sru
76114402Srustruct lig_kern_command {
77114402Sru  unsigned char skip_byte;
78114402Sru  unsigned char next_char;
79114402Sru  unsigned char op_byte;
80114402Sru  unsigned char remainder;
81114402Sru};
82114402Sru
83114402Sruclass tfm {
84114402Sru  int bc;
85114402Sru  int ec;
86114402Sru  int nw;
87114402Sru  int nh;
88114402Sru  int nd;
89114402Sru  int ni;
90114402Sru  int nl;
91114402Sru  int nk;
92114402Sru  int np;
93114402Sru  int cs;
94114402Sru  int ds;
95114402Sru  char_info_word *char_info;
96114402Sru  int *width;
97114402Sru  int *height;
98114402Sru  int *depth;
99114402Sru  int *italic;
100114402Sru  lig_kern_command *lig_kern;
101114402Sru  int *kern;
102114402Sru  int *param;
103114402Srupublic:
104114402Sru  tfm();
105114402Sru  ~tfm();
106114402Sru  int load(const char *);
107114402Sru  int contains(int);
108114402Sru  int get_width(int);
109114402Sru  int get_height(int);
110114402Sru  int get_depth(int);
111114402Sru  int get_italic(int);
112114402Sru  int get_param(int, int *);
113114402Sru  int get_checksum();
114114402Sru  int get_design_size();
115114402Sru  int get_lig(unsigned char, unsigned char, unsigned char *);
116114402Sru  friend class kern_iterator;
117114402Sru};
118114402Sru
119114402Sruclass kern_iterator {
120114402Sru  tfm *t;
121114402Sru  int c;
122114402Sru  int i;
123114402Srupublic:
124114402Sru  kern_iterator(tfm *);
125114402Sru  int next(unsigned char *c1, unsigned char *c2, int *k);
126114402Sru};
127114402Sru
128114402Sru
129114402Srukern_iterator::kern_iterator(tfm *p)
130114402Sru: t(p), c(t->bc), i(-1)
131114402Sru{
132114402Sru}
133114402Sru
134114402Sruint kern_iterator::next(unsigned char *c1, unsigned char *c2, int *k)
135114402Sru{
136114402Sru  for (; c <= t->ec; c++)
137114402Sru    if (t->char_info[c - t->bc].tag == 1) {
138114402Sru      if (i < 0) {
139114402Sru	i = t->char_info[c - t->bc].remainder;
140114402Sru	if (t->lig_kern[i].skip_byte > 128)
141114402Sru	  i = (256*t->lig_kern[i].op_byte
142114402Sru		   + t->lig_kern[i].remainder);
143114402Sru      }
144114402Sru      for (;;) {
145114402Sru	int skip = t->lig_kern[i].skip_byte;
146114402Sru	if (skip <= 128 && t->lig_kern[i].op_byte >= 128) {
147114402Sru	  *c1 = c;
148114402Sru	  *c2 = t->lig_kern[i].next_char;
149114402Sru	  *k = t->kern[256*(t->lig_kern[i].op_byte - 128)
150114402Sru		       + t->lig_kern[i].remainder];
151114402Sru	  if (skip == 128) {
152114402Sru	    c++;
153114402Sru	    i = -1;
154114402Sru	  }
155114402Sru	  else
156114402Sru	    i += skip + 1;
157114402Sru	  return 1;
158114402Sru	}
159114402Sru	if (skip >= 128)
160114402Sru	  break;
161114402Sru	i += skip + 1;
162114402Sru      }
163114402Sru      i = -1;
164114402Sru    }
165114402Sru  return 0;
166114402Sru}
167114402Sru
168114402Srutfm::tfm()
169114402Sru: char_info(0), width(0), height(0), depth(0), italic(0), lig_kern(0),
170114402Sru  kern(0), param(0)
171114402Sru{
172114402Sru}
173114402Sru
174114402Sruint tfm::get_lig(unsigned char c1, unsigned char c2, unsigned char *cp)
175114402Sru{
176114402Sru  if (contains(c1) && char_info[c1 - bc].tag == 1) {
177114402Sru    int i = char_info[c1 - bc].remainder;
178114402Sru    if (lig_kern[i].skip_byte > 128)
179114402Sru      i = 256*lig_kern[i].op_byte + lig_kern[i].remainder;
180114402Sru    for (;;) {
181114402Sru      int skip = lig_kern[i].skip_byte;
182114402Sru      if (skip > 128)
183114402Sru	break;
184114402Sru      // We are only interested in normal ligatures, for which
185114402Sru      // op_byte == 0.
186114402Sru      if (lig_kern[i].op_byte == 0
187114402Sru	  && lig_kern[i].next_char == c2) {
188114402Sru	*cp = lig_kern[i].remainder;
189114402Sru	return 1;
190114402Sru      }
191114402Sru      if (skip == 128)
192114402Sru	break;
193114402Sru      i += skip + 1;
194114402Sru    }
195114402Sru  }
196114402Sru  return 0;
197114402Sru}
198114402Sru
199114402Sruint tfm::contains(int i)
200114402Sru{
201114402Sru  return i >= bc && i <= ec && char_info[i - bc].width_index != 0;
202114402Sru}
203114402Sru
204114402Sruint tfm::get_width(int i)
205114402Sru{
206114402Sru  return width[char_info[i - bc].width_index];
207114402Sru}
208114402Sru
209114402Sruint tfm::get_height(int i)
210114402Sru{
211114402Sru  return height[char_info[i - bc].height_index];
212114402Sru}
213114402Sru
214114402Sruint tfm::get_depth(int i)
215114402Sru{
216114402Sru  return depth[char_info[i - bc].depth_index];
217114402Sru}
218114402Sru
219114402Sruint tfm::get_italic(int i)
220114402Sru{
221114402Sru  return italic[char_info[i - bc].italic_index];
222114402Sru}
223114402Sru
224114402Sruint tfm::get_param(int i, int *p)
225114402Sru{
226114402Sru  if (i <= 0 || i > np)
227114402Sru    return 0;
228114402Sru  else {
229114402Sru    *p = param[i - 1];
230114402Sru    return 1;
231114402Sru  }
232114402Sru}
233114402Sru
234114402Sruint tfm::get_checksum()
235114402Sru{
236114402Sru  return cs;
237114402Sru}
238114402Sru
239114402Sruint tfm::get_design_size()
240114402Sru{
241114402Sru  return ds;
242114402Sru}
243114402Sru
244114402Srutfm::~tfm()
245114402Sru{
246114402Sru  a_delete char_info;
247114402Sru  a_delete width;
248114402Sru  a_delete height;
249114402Sru  a_delete depth;
250114402Sru  a_delete italic;
251114402Sru  a_delete lig_kern;
252114402Sru  a_delete kern;
253114402Sru  a_delete param;
254114402Sru}
255114402Sru
256114402Sruint read2(unsigned char *&s)
257114402Sru{
258114402Sru  int n;
259114402Sru  n = *s++ << 8;
260114402Sru  n |= *s++;
261114402Sru  return n;
262114402Sru}
263114402Sru
264114402Sruint read4(unsigned char *&s)
265114402Sru{
266114402Sru  int n;
267114402Sru  n = *s++ << 24;
268114402Sru  n |= *s++ << 16;
269114402Sru  n |= *s++ << 8;
270114402Sru  n |= *s++;
271114402Sru  return n;
272114402Sru}
273114402Sru
274114402Sru
275114402Sruint tfm::load(const char *file)
276114402Sru{
277114402Sru  errno = 0;
278114402Sru  FILE *fp = fopen(file, FOPEN_RB);
279114402Sru  if (!fp) {
280114402Sru    error("can't open `%1': %2", file, strerror(errno));
281114402Sru    return 0;
282114402Sru  }
283114402Sru  int c1 = getc(fp);
284114402Sru  int c2 = getc(fp);
285114402Sru  if (c1 == EOF || c2 == EOF) {
286114402Sru    fclose(fp);
287114402Sru    error("unexpected end of file on `%1'", file);
288114402Sru    return 0;
289114402Sru  }
290114402Sru  int lf = (c1 << 8) + c2;
291114402Sru  int toread = lf*4 - 2;
292114402Sru  unsigned char *buf = new unsigned char[toread];
293114402Sru  if (fread(buf, 1, toread, fp) != (size_t)toread) {
294114402Sru    if (feof(fp))
295114402Sru      error("unexpected end of file on `%1'", file);
296114402Sru    else
297114402Sru      error("error on file `%1'", file);
298114402Sru    a_delete buf;
299114402Sru    fclose(fp);
300114402Sru    return 0;
301114402Sru  }
302114402Sru  fclose(fp);
303114402Sru  if (lf < 6) {
304114402Sru    error("bad tfm file `%1': impossibly short", file);
305114402Sru    a_delete buf;
306114402Sru    return 0;
307114402Sru  }
308114402Sru  unsigned char *ptr = buf;
309114402Sru  int lh = read2(ptr);
310114402Sru  bc = read2(ptr);
311114402Sru  ec = read2(ptr);
312114402Sru  nw = read2(ptr);
313114402Sru  nh = read2(ptr);
314114402Sru  nd = read2(ptr);
315114402Sru  ni = read2(ptr);
316114402Sru  nl = read2(ptr);
317114402Sru  nk = read2(ptr);
318114402Sru  int ne = read2(ptr);
319114402Sru  np = read2(ptr);
320114402Sru  if (6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np != lf) {
321114402Sru    error("bad tfm file `%1': lengths do not sum", file);
322114402Sru    a_delete buf;
323114402Sru    return 0;
324114402Sru  }
325114402Sru  if (lh < 2) {
326114402Sru    error("bad tfm file `%1': header too short", file);
327114402Sru    a_delete buf;
328114402Sru    return 0;
329114402Sru  }
330114402Sru  char_info = new char_info_word[ec - bc + 1];
331114402Sru  width = new int[nw];
332114402Sru  height = new int[nh];
333114402Sru  depth = new int[nd];
334114402Sru  italic = new int[ni];
335114402Sru  lig_kern = new lig_kern_command[nl];
336114402Sru  kern = new int[nk];
337114402Sru  param = new int[np];
338114402Sru  int i;
339114402Sru  cs = read4(ptr);
340114402Sru  ds = read4(ptr);
341114402Sru  ptr += (lh-2)*4;
342114402Sru  for (i = 0; i < ec - bc + 1; i++) {
343114402Sru    char_info[i].width_index = *ptr++;
344114402Sru    unsigned char tem = *ptr++;
345114402Sru    char_info[i].depth_index = tem & 0xf;
346114402Sru    char_info[i].height_index = tem >> 4;
347114402Sru    tem = *ptr++;
348114402Sru    char_info[i].italic_index = tem >> 2;
349114402Sru    char_info[i].tag = tem & 3;
350114402Sru    char_info[i].remainder = *ptr++;
351114402Sru  }
352114402Sru  for (i = 0; i < nw; i++)
353114402Sru    width[i] = read4(ptr);
354114402Sru  for (i = 0; i < nh; i++)
355114402Sru    height[i] = read4(ptr);
356114402Sru  for (i = 0; i < nd; i++)
357114402Sru    depth[i] = read4(ptr);
358114402Sru  for (i = 0; i < ni; i++)
359114402Sru    italic[i] = read4(ptr);
360114402Sru  for (i = 0; i < nl; i++) {
361114402Sru    lig_kern[i].skip_byte = *ptr++;
362114402Sru    lig_kern[i].next_char = *ptr++;
363114402Sru    lig_kern[i].op_byte = *ptr++;
364114402Sru    lig_kern[i].remainder = *ptr++;
365114402Sru  }
366114402Sru  for (i = 0; i < nk; i++)
367114402Sru    kern[i] = read4(ptr);
368114402Sru  ptr += ne*4;
369114402Sru  for (i = 0; i < np; i++)
370114402Sru    param[i] = read4(ptr);
371114402Sru  assert(ptr == buf + lf*4 - 2);
372114402Sru  a_delete buf;
373114402Sru  return 1;
374114402Sru}
375114402Sru
376114402Sruclass gf {
377114402Sru  int left[256];
378114402Sru  int right[256];
379114402Sru  static int sread4(int *p, FILE *fp);
380114402Sru  static int uread3(int *p, FILE *fp);
381114402Sru  static int uread2(int *p, FILE *fp);
382114402Sru  static int skip(int n, FILE *fp);
383114402Srupublic:
384114402Sru  gf();
385114402Sru  int load(const char *file);
386114402Sru  int get_left_adjustment(int i) { return left[i]; }
387114402Sru  int get_right_adjustment(int i) { return right[i]; }
388114402Sru};
389114402Sru
390114402Srugf::gf()
391114402Sru{
392114402Sru  for (int i = 0; i < 256; i++)
393114402Sru    left[i] = right[i] = 0;
394114402Sru}
395114402Sru
396114402Sruint gf::load(const char *file)
397114402Sru{
398114402Sru  enum {
399114402Sru    paint_0 = 0,
400114402Sru    paint1 = 64,
401114402Sru    boc = 67,
402114402Sru    boc1 = 68,
403114402Sru    eoc = 69,
404114402Sru    skip0 = 70,
405114402Sru    skip1 = 71,
406114402Sru    new_row_0 = 74,
407114402Sru    xxx1 = 239,
408114402Sru    yyy = 243,
409114402Sru    no_op = 244,
410114402Sru    pre = 247,
411114402Sru    post = 248
412114402Sru  };
413114402Sru  int got_an_adjustment = 0;
414114402Sru  int pending_adjustment = 0;
415151497Sru  int left_adj = 0, right_adj = 0;	// pacify compiler
416114402Sru  const int gf_id_byte = 131;
417114402Sru  errno = 0;
418114402Sru  FILE *fp = fopen(file, FOPEN_RB);
419114402Sru  if (!fp) {
420114402Sru    error("can't open `%1': %2", file, strerror(errno));
421114402Sru    return 0;
422114402Sru  }
423114402Sru  if (getc(fp) != pre || getc(fp) != gf_id_byte) {
424114402Sru    error("bad gf file");
425114402Sru    return 0;
426114402Sru  }
427114402Sru  int n = getc(fp);
428114402Sru  if (n == EOF)
429114402Sru    goto eof;
430114402Sru  if (!skip(n, fp))
431114402Sru    goto eof;
432114402Sru  for (;;) {
433114402Sru    int op = getc(fp);
434114402Sru    if (op == EOF)
435114402Sru      goto eof;
436114402Sru    if (op == post)
437114402Sru      break;
438114402Sru    if ((op >= paint_0 && op <= paint_0 + 63)
439114402Sru	|| (op >= new_row_0 && op <= new_row_0 + 164))
440114402Sru      continue;
441114402Sru    switch (op) {
442114402Sru    case no_op:
443114402Sru    case eoc:
444114402Sru    case skip0:
445114402Sru      break;
446114402Sru    case paint1:
447114402Sru    case skip1:
448114402Sru      if (!skip(1, fp))
449114402Sru	goto eof;
450114402Sru      break;
451114402Sru    case paint1 + 1:
452114402Sru    case skip1 + 1:
453114402Sru      if (!skip(2, fp))
454114402Sru	goto eof;
455114402Sru      break;
456114402Sru    case paint1 + 2:
457114402Sru    case skip1 + 2:
458114402Sru      if (!skip(3, fp))
459114402Sru	goto eof;
460114402Sru      break;
461114402Sru    case boc:
462114402Sru      {
463114402Sru	int code;
464114402Sru	if (!sread4(&code, fp))
465114402Sru	  goto eof;
466114402Sru	if (pending_adjustment) {
467114402Sru	  pending_adjustment = 0;
468114402Sru	  left[code & 0377] = left_adj;
469114402Sru	  right[code & 0377] = right_adj;
470114402Sru	}
471114402Sru	if (!skip(20, fp))
472114402Sru	  goto eof;
473114402Sru	break;
474114402Sru      }
475114402Sru    case boc1:
476114402Sru      {
477114402Sru	int code = getc(fp);
478114402Sru	if (code == EOF)
479114402Sru	  goto eof;
480114402Sru	if (pending_adjustment) {
481114402Sru	  pending_adjustment = 0;
482114402Sru	  left[code] = left_adj;
483114402Sru	  right[code] = right_adj;
484114402Sru	}
485114402Sru	if (!skip(4, fp))
486114402Sru	  goto eof;
487114402Sru	break;
488114402Sru      }
489114402Sru    case xxx1:
490114402Sru      {
491114402Sru	int len = getc(fp);
492114402Sru	if (len == EOF)
493114402Sru	  goto eof;
494114402Sru	char buf[256];
495114402Sru	if (fread(buf, 1, len, fp) != (size_t)len)
496114402Sru	  goto eof;
497114402Sru	if (len == 10 /* strlen("adjustment") */
498114402Sru	    && memcmp(buf, "adjustment", len) == 0) {
499114402Sru	  int c = getc(fp);
500114402Sru	  if (c != yyy) {
501114402Sru	    if (c != EOF)
502114402Sru	      ungetc(c, fp);
503114402Sru	    break;
504114402Sru	  }
505114402Sru	  if (!sread4(&left_adj, fp))
506114402Sru	    goto eof;
507114402Sru	  c = getc(fp);
508114402Sru	  if (c != yyy) {
509114402Sru	    if (c != EOF)
510114402Sru	      ungetc(c, fp);
511114402Sru	    break;
512114402Sru	  }
513114402Sru	  if (!sread4(&right_adj, fp))
514114402Sru	    goto eof;
515114402Sru	  got_an_adjustment = 1;
516114402Sru	  pending_adjustment = 1;
517114402Sru	}
518114402Sru	break;
519114402Sru      }
520114402Sru    case xxx1 + 1:
521114402Sru      if (!uread2(&n, fp) || !skip(n, fp))
522114402Sru	goto eof;
523114402Sru      break;
524114402Sru    case xxx1 + 2:
525114402Sru      if (!uread3(&n, fp) || !skip(n, fp))
526114402Sru	goto eof;
527114402Sru      break;
528114402Sru    case xxx1 + 3:
529114402Sru      if (!sread4(&n, fp) || !skip(n, fp))
530114402Sru	goto eof;
531114402Sru      break;
532114402Sru    case yyy:
533114402Sru      if (!skip(4, fp))
534114402Sru	goto eof;
535114402Sru      break;
536114402Sru    default:
537114402Sru      fatal("unrecognized opcode `%1'", op);
538114402Sru      break;
539114402Sru    }
540114402Sru  }
541114402Sru  if (!got_an_adjustment)
542114402Sru    warning("no adjustment specials found in gf file");
543114402Sru  return 1;
544114402Sru eof:
545114402Sru  error("unexpected end of file");
546114402Sru  return 0;
547114402Sru}
548114402Sru
549114402Sruint gf::sread4(int *p, FILE *fp)
550114402Sru{
551114402Sru  *p = getc(fp);
552114402Sru  if (*p >= 128)
553114402Sru    *p -= 256;
554114402Sru  *p <<= 8;
555114402Sru  *p |= getc(fp);
556114402Sru  *p <<= 8;
557114402Sru  *p |= getc(fp);
558114402Sru  *p <<= 8;
559114402Sru  *p |= getc(fp);
560114402Sru  return !ferror(fp) && !feof(fp);
561114402Sru}
562114402Sru
563114402Sruint gf::uread3(int *p, FILE *fp)
564114402Sru{
565114402Sru  *p = getc(fp);
566114402Sru  *p <<= 8;
567114402Sru  *p |= getc(fp);
568114402Sru  *p <<= 8;
569114402Sru  *p |= getc(fp);
570114402Sru  return !ferror(fp) && !feof(fp);
571114402Sru}
572114402Sru
573114402Sruint gf::uread2(int *p, FILE *fp)
574114402Sru{
575114402Sru  *p = getc(fp);
576114402Sru  *p <<= 8;
577114402Sru  *p |= getc(fp);
578114402Sru  return !ferror(fp) && !feof(fp);
579114402Sru}
580114402Sru
581114402Sruint gf::skip(int n, FILE *fp)
582114402Sru{
583114402Sru  while (--n >= 0)
584114402Sru    if (getc(fp) == EOF)
585114402Sru      return 0;
586114402Sru  return 1;
587114402Sru}
588114402Sru
589114402Sru
590114402Srustruct char_list {
591114402Sru  char *ch;
592114402Sru  char_list *next;
593114402Sru  char_list(const char *, char_list * = 0);
594114402Sru};
595114402Sru
596114402Sruchar_list::char_list(const char *s, char_list *p) : ch(strsave(s)), next(p)
597114402Sru{
598114402Sru}
599114402Sru
600114402Sru
601114402Sruint read_map(const char *file, char_list **table)
602114402Sru{
603114402Sru  errno = 0;
604114402Sru  FILE *fp = fopen(file, "r");
605114402Sru  if (!fp) {
606114402Sru    error("can't open `%1': %2", file, strerror(errno));
607114402Sru    return 0;
608114402Sru  }
609114402Sru  for (int i = 0; i < 256; i++)
610114402Sru    table[i] = 0;
611114402Sru  char buf[512];
612114402Sru  int lineno = 0;
613114402Sru  while (fgets(buf, int(sizeof(buf)), fp)) {
614114402Sru    lineno++;
615114402Sru    char *ptr = buf;
616114402Sru    while (csspace(*ptr))
617114402Sru      ptr++;
618114402Sru    if (*ptr == '\0' || *ptr == '#')
619114402Sru      continue;
620114402Sru    ptr = strtok(ptr, " \n\t");
621114402Sru    if (!ptr)
622114402Sru      continue;
623114402Sru    int n;
624114402Sru    if (sscanf(ptr, "%d", &n) != 1) {
625114402Sru      error("%1:%2: bad map file", file, lineno);
626114402Sru      fclose(fp);
627114402Sru      return 0;
628114402Sru    }
629114402Sru    if (n < 0 || n > 255) {
630114402Sru      error("%1:%2: code out of range", file, lineno);
631114402Sru      fclose(fp);
632114402Sru      return 0;
633114402Sru    }
634114402Sru    ptr = strtok(0, " \n\t");
635114402Sru    if (!ptr) {
636114402Sru      error("%1:%2: missing names", file, lineno);
637114402Sru      fclose(fp);
638114402Sru      return 0;
639114402Sru    }
640114402Sru    for (; ptr; ptr = strtok(0, " \n\t"))
641114402Sru      table[n] = new char_list(ptr, table[n]);
642114402Sru  }
643114402Sru  fclose(fp);
644114402Sru  return 1;
645114402Sru}
646114402Sru
647114402Sru
648114402Sru/* Every character that can participate in a ligature appears in the
649114402Srulig_chars table. `ch' gives the full-name of the character, `name'
650114402Srugives the groff name of the character, `i' gives its index in
651114402Sruthe encoding, which is filled in later  (-1 if it does not appear). */
652114402Sru
653151497Srustruct S {
654114402Sru  const char *ch;
655114402Sru  int i;
656114402Sru} lig_chars[] = {
657114402Sru  { "f", -1 },
658114402Sru  { "i", -1 },
659114402Sru  { "l", -1 },
660114402Sru  { "ff", -1 },
661114402Sru  { "fi", -1 },
662114402Sru  { "fl", -1 },
663114402Sru  { "Fi", -1 },
664114402Sru  { "Fl", -1 },
665114402Sru};
666114402Sru
667114402Sru// Indices into lig_chars[].
668114402Sru
669114402Sruenum { CH_f, CH_i, CH_l, CH_ff, CH_fi, CH_fl, CH_ffi, CH_ffl };
670114402Sru
671114402Sru// Each possible ligature appears in this table.
672114402Sru
673151497Srustruct S2 {
674114402Sru  unsigned char c1, c2, res;
675114402Sru  const char *ch;
676114402Sru} lig_table[] = {
677114402Sru  { CH_f, CH_f, CH_ff, "ff" },
678114402Sru  { CH_f, CH_i, CH_fi, "fi" },
679114402Sru  { CH_f, CH_l, CH_fl, "fl" },
680114402Sru  { CH_ff, CH_i, CH_ffi, "ffi" },
681114402Sru  { CH_ff, CH_l, CH_ffl, "ffl" },
682114402Sru  };
683114402Sru
684114402Srustatic void usage(FILE *stream);
685114402Sru
686114402Sruint main(int argc, char **argv)
687114402Sru{
688114402Sru  program_name = argv[0];
689114402Sru  int special_flag = 0;
690114402Sru  int skewchar = -1;
691114402Sru  int opt;
692114402Sru  const char *gf_file = 0;
693114402Sru  static const struct option long_options[] = {
694114402Sru    { "help", no_argument, 0, CHAR_MAX + 1 },
695114402Sru    { "version", no_argument, 0, 'v' },
696114402Sru    { NULL, 0, 0, 0 }
697114402Sru  };
698114402Sru  while ((opt = getopt_long(argc, argv, "svg:k:", long_options, NULL)) != EOF)
699114402Sru    switch (opt) {
700114402Sru    case 'g':
701114402Sru      gf_file = optarg;
702114402Sru      break;
703114402Sru    case 's':
704114402Sru      special_flag = 1;
705114402Sru      break;
706114402Sru    case 'k':
707114402Sru      {
708114402Sru	char *ptr;
709114402Sru	long n = strtol(optarg, &ptr, 0);
710114402Sru	if ((n == 0 && ptr == optarg)
711114402Sru	    || *ptr != '\0'
712114402Sru	    || n < 0
713114402Sru	    || n > UCHAR_MAX)
714114402Sru	  error("invalid skewchar");
715114402Sru	else
716114402Sru	  skewchar = (int)n;
717114402Sru	break;
718114402Sru      }
719114402Sru    case 'v':
720114402Sru      {
721114402Sru	printf("GNU tfmtodit (groff) version %s\n", Version_string);
722114402Sru	exit(0);
723114402Sru	break;
724114402Sru      }
725114402Sru    case CHAR_MAX + 1: // --help
726114402Sru      usage(stdout);
727114402Sru      exit(0);
728114402Sru      break;
729114402Sru    case '?':
730114402Sru      usage(stderr);
731114402Sru      exit(1);
732114402Sru      break;
733114402Sru    case EOF:
734114402Sru      assert(0);
735114402Sru    }
736114402Sru  if (argc - optind != 3) {
737114402Sru    usage(stderr);
738114402Sru    exit(1);
739114402Sru  }
740114402Sru  gf g;
741114402Sru  if (gf_file) {
742114402Sru    if (!g.load(gf_file))
743114402Sru      return 1;
744114402Sru  }
745114402Sru  const char *tfm_file = argv[optind];
746114402Sru  const char *map_file = argv[optind + 1];
747114402Sru  const char *font_file = argv[optind + 2];
748114402Sru  tfm t;
749114402Sru  if (!t.load(tfm_file))
750114402Sru    return 1;
751114402Sru  char_list *table[256];
752114402Sru  if (!read_map(map_file, table))
753114402Sru    return 1;
754114402Sru  errno = 0;
755114402Sru  if (!freopen(font_file, "w", stdout)) {
756114402Sru    error("can't open `%1' for writing: %2", font_file, strerror(errno));
757114402Sru    return 1;
758114402Sru  }
759114402Sru  printf("name %s\n", font_file);
760114402Sru  if (special_flag)
761114402Sru    fputs("special\n", stdout);
762114402Sru  char *internal_name = strsave(argv[optind]);
763114402Sru  int len = strlen(internal_name);
764114402Sru  if (len > 4 && strcmp(internal_name + len - 4, ".tfm") == 0)
765114402Sru    internal_name[len - 4] = '\0';
766114402Sru  // DIR_SEPS[] are possible directory separator characters, see nonposix.h.
767114402Sru  // We want the rightmost separator of all possible ones.
768114402Sru  // Example: d:/foo\\bar.
769114402Sru  const char *s = strrchr(internal_name, DIR_SEPS[0]), *s1;
770114402Sru  const char *sep = &DIR_SEPS[1];
771114402Sru  while (*sep)
772114402Sru    {
773114402Sru      s1 = strrchr(internal_name, *sep);
774114402Sru      if (s1 && (!s || s1 > s))
775114402Sru	s = s1;
776114402Sru      sep++;
777114402Sru    }
778114402Sru  printf("internalname %s\n", s ? s + 1 : internal_name);
779114402Sru  int n;
780114402Sru  if (t.get_param(2, &n)) {
781114402Sru    if (n > 0)
782114402Sru      printf("spacewidth %d\n", n*MULTIPLIER);
783114402Sru  }
784114402Sru  if (t.get_param(1, &n) && n != 0)
785114402Sru    printf("slant %f\n", atan2(n/double(1<<20), 1.0)*180.0/PI);
786114402Sru  int xheight;
787114402Sru  if (!t.get_param(5, &xheight))
788114402Sru    xheight = 0;
789114402Sru  unsigned int i;
790114402Sru  // Print the list of ligatures.
791114402Sru  // First find the indices of each character that can participate in
792114402Sru  // a ligature.
793114402Sru  for (i = 0; i < 256; i++)
794114402Sru    for (unsigned int j = 0; j < sizeof(lig_chars)/sizeof(lig_chars[0]); j++)
795114402Sru      for (char_list *p = table[i]; p; p = p->next)
796114402Sru	if (strcmp(lig_chars[j].ch, p->ch) == 0)
797114402Sru	  lig_chars[j].i = i;
798114402Sru  // For each possible ligature, if its participants all exist,
799114402Sru  // and it appears as a ligature in the tfm file, include in
800114402Sru  // the list of ligatures.
801114402Sru  int started = 0;
802114402Sru  for (i = 0; i < sizeof(lig_table)/sizeof(lig_table[0]); i++) {
803114402Sru    int i1 = lig_chars[lig_table[i].c1].i;
804114402Sru    int i2 = lig_chars[lig_table[i].c2].i;
805114402Sru    int r = lig_chars[lig_table[i].res].i;
806114402Sru    if (i1 >= 0 && i2 >= 0 && r >= 0) {
807114402Sru      unsigned char c;
808114402Sru      if (t.get_lig(i1, i2, &c) && c == r) {
809114402Sru	if (!started) {
810114402Sru	  started = 1;
811114402Sru	  fputs("ligatures", stdout);
812114402Sru	}
813114402Sru	printf(" %s", lig_table[i].ch);
814114402Sru      }
815114402Sru    }
816114402Sru  }
817114402Sru  if (started)
818114402Sru    fputs(" 0\n", stdout);
819114402Sru  printf("checksum %d\n", t.get_checksum());
820114402Sru  printf("designsize %d\n", t.get_design_size());
821114402Sru  // Now print out the kerning information.
822114402Sru  int had_kern = 0;
823114402Sru  kern_iterator iter(&t);
824114402Sru  unsigned char c1, c2;
825114402Sru  int k;
826114402Sru  while (iter.next(&c1, &c2, &k))
827114402Sru    if (c2 != skewchar) {
828114402Sru      k *= MULTIPLIER;
829114402Sru      char_list *q = table[c2];
830114402Sru      for (char_list *p1 = table[c1]; p1; p1 = p1->next)
831114402Sru	for (char_list *p2 = q; p2; p2 = p2->next) {
832114402Sru	  if (!had_kern) {
833114402Sru	    printf("kernpairs\n");
834114402Sru	    had_kern = 1;
835114402Sru	  }
836114402Sru	  printf("%s %s %d\n", p1->ch, p2->ch, k);
837114402Sru	}
838114402Sru    }
839114402Sru  printf("charset\n");
840114402Sru  char_list unnamed("---");
841114402Sru  for (i = 0; i < 256; i++)
842114402Sru    if (t.contains(i)) {
843114402Sru      char_list *p = table[i] ? table[i] : &unnamed;
844114402Sru      int m[6];
845114402Sru      m[0] = t.get_width(i);
846114402Sru      m[1] = t.get_height(i);
847114402Sru      m[2] = t.get_depth(i);
848114402Sru      m[3] = t.get_italic(i);
849114402Sru      m[4] = g.get_left_adjustment(i);
850114402Sru      m[5] = g.get_right_adjustment(i);
851114402Sru      printf("%s\t%d", p->ch, m[0]*MULTIPLIER);
852114402Sru      int j;
853114402Sru      for (j = int(sizeof(m)/sizeof(m[0])) - 1; j > 0; j--)
854114402Sru	if (m[j] != 0)
855114402Sru	  break;
856114402Sru      for (k = 1; k <= j; k++)
857114402Sru	printf(",%d", m[k]*MULTIPLIER);
858114402Sru      int type = 0;
859114402Sru      if (m[2] > 0)
860114402Sru	type = 1;
861114402Sru      if (m[1] > xheight)
862114402Sru	type += 2;
863114402Sru      printf("\t%d\t%04o\n", type, i);
864114402Sru      for (p = p->next; p; p = p->next)
865114402Sru	printf("%s\t\"\n", p->ch);
866114402Sru    }
867114402Sru  return 0;
868114402Sru}
869114402Sru
870114402Srustatic void usage(FILE *stream)
871114402Sru{
872114402Sru  fprintf(stream, "usage: %s [-sv] [-g gf_file] [-k skewchar] tfm_file map_file font\n",
873114402Sru	  program_name);
874114402Sru}
875