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