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