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