xref: /NextBSD/contrib/groff/src/utils/tfmtodit/tfmtodit.cpp (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 // -*- C++ -*-
2 /* Copyright (C) 1989-1992, 2000, 2001, 2004 Free Software Foundation, Inc.
3      Written by James Clark (jjc@jclark.com)
4 
5 This file is part of groff.
6 
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11 
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING.  If not, write to the Free Software
19 Foundation, 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,
22 but I haven't tested them. */
23 
24 /* Groff requires more font metric information than TeX.  The reason
25 for this is that TeX has separate Math Italic fonts, whereas groff
26 uses normal italic fonts for math.  The two additional pieces of
27 information required by groff correspond to the two arguments to the
28 math_fit() macro in the Metafont programs for the CM fonts. In the
29 case of a font for which math_fitting is false, these two arguments
30 are normally ignored by Metafont. We need to get hold of these two
31 parameters and put them in the groff font file.
32 
33 We do this by loading this definition after cmbase when creating cm.base.
34 
35 def 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 
41 This 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
43 which they apply.)  We then create a gf file using this cm.base.  Then
44 we run tfmtodit and specify this gf file with the -g option.
45 
46 This need only be done for a font for which math_fitting is false;
47 When it's true, the left_correction and subscript_correction should
48 both 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 
61 extern "C" const char *Version_string;
62 
63 /* Values in the tfm file should be multiplied by this. */
64 
65 #define MULTIPLIER 1
66 
67 struct 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 
76 struct lig_kern_command {
77   unsigned char skip_byte;
78   unsigned char next_char;
79   unsigned char op_byte;
80   unsigned char remainder;
81 };
82 
83 class 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;
103 public:
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 
119 class kern_iterator {
120   tfm *t;
121   int c;
122   int i;
123 public:
124   kern_iterator(tfm *);
125   int next(unsigned char *c1, unsigned char *c2, int *k);
126 };
127 
128 
kern_iterator(tfm * p)129 kern_iterator::kern_iterator(tfm *p)
130 : t(p), c(t->bc), i(-1)
131 {
132 }
133 
next(unsigned char * c1,unsigned char * c2,int * k)134 int 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 
tfm()168 tfm::tfm()
169 : char_info(0), width(0), height(0), depth(0), italic(0), lig_kern(0),
170   kern(0), param(0)
171 {
172 }
173 
get_lig(unsigned char c1,unsigned char c2,unsigned char * cp)174 int 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 
contains(int i)199 int tfm::contains(int i)
200 {
201   return i >= bc && i <= ec && char_info[i - bc].width_index != 0;
202 }
203 
get_width(int i)204 int tfm::get_width(int i)
205 {
206   return width[char_info[i - bc].width_index];
207 }
208 
get_height(int i)209 int tfm::get_height(int i)
210 {
211   return height[char_info[i - bc].height_index];
212 }
213 
get_depth(int i)214 int tfm::get_depth(int i)
215 {
216   return depth[char_info[i - bc].depth_index];
217 }
218 
get_italic(int i)219 int tfm::get_italic(int i)
220 {
221   return italic[char_info[i - bc].italic_index];
222 }
223 
get_param(int i,int * p)224 int 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 
get_checksum()234 int tfm::get_checksum()
235 {
236   return cs;
237 }
238 
get_design_size()239 int tfm::get_design_size()
240 {
241   return ds;
242 }
243 
~tfm()244 tfm::~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 
read2(unsigned char * & s)256 int read2(unsigned char *&s)
257 {
258   int n;
259   n = *s++ << 8;
260   n |= *s++;
261   return n;
262 }
263 
read4(unsigned char * & s)264 int 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 
load(const char * file)275 int 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 
376 class 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);
383 public:
384   gf();
385   int load(const char *file);
get_left_adjustment(int i)386   int get_left_adjustment(int i) { return left[i]; }
get_right_adjustment(int i)387   int get_right_adjustment(int i) { return right[i]; }
388 };
389 
gf()390 gf::gf()
391 {
392   for (int i = 0; i < 256; i++)
393     left[i] = right[i] = 0;
394 }
395 
load(const char * file)396 int 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 
sread4(int * p,FILE * fp)549 int 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 
uread3(int * p,FILE * fp)563 int 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 
uread2(int * p,FILE * fp)573 int 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 
skip(int n,FILE * fp)581 int 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 
590 struct char_list {
591   char *ch;
592   char_list *next;
593   char_list(const char *, char_list * = 0);
594 };
595 
char_list(const char * s,char_list * p)596 char_list::char_list(const char *s, char_list *p) : ch(strsave(s)), next(p)
597 {
598 }
599 
600 
read_map(const char * file,char_list ** table)601 int 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
649 lig_chars table. `ch' gives the full-name of the character, `name'
650 gives the groff name of the character, `i' gives its index in
651 the encoding, which is filled in later  (-1 if it does not appear). */
652 
653 struct 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 
669 enum { 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 
673 struct 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 
684 static void usage(FILE *stream);
685 
main(int argc,char ** argv)686 int 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 
usage(FILE * stream)870 static 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