Update to groff 1.19.2.
[dragonfly.git] / contrib / groff-1.19 / src / utils / tfmtodit / tfmtodit.cpp
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
129 kern_iterator::kern_iterator(tfm *p)
130 : t(p), c(t->bc), i(-1)
131 {
132 }
133
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           
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
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
199 int tfm::contains(int i)
200 {
201   return i >= bc && i <= ec && char_info[i - bc].width_index != 0;
202 }
203
204 int tfm::get_width(int i)
205 {
206   return width[char_info[i - bc].width_index];
207 }
208
209 int tfm::get_height(int i)
210 {
211   return height[char_info[i - bc].height_index];
212 }
213
214 int tfm::get_depth(int i)
215 {
216   return depth[char_info[i - bc].depth_index];
217 }
218
219 int tfm::get_italic(int i)
220 {
221   return italic[char_info[i - bc].italic_index];
222 }
223
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
234 int tfm::get_checksum()
235 {
236   return cs;
237 }
238
239 int tfm::get_design_size()
240 {
241   return ds;
242 }
243
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   
256 int read2(unsigned char *&s)
257 {
258   int n;
259   n = *s++ << 8;
260   n |= *s++;
261   return n;
262 }
263
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
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);
386   int get_left_adjustment(int i) { return left[i]; }
387   int get_right_adjustment(int i) { return right[i]; }
388 };
389
390 gf::gf()
391 {
392   for (int i = 0; i < 256; i++)
393     left[i] = right[i] = 0;
394 }
395
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
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
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
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
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
596 char_list::char_list(const char *s, char_list *p) : ch(strsave(s)), next(p)
597 {
598 }
599
600
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   
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
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 }