Sync-to-go: update copyright for 2015
[s-roff.git] / src / ute-tfmtodit / tfmtodit.cpp
blobc44a0ccc9a936f494babee45472024f5ca83d67b
1 /*@
2 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
4 * Copyright (C) 1989 - 1992, 2000, 2001, 2004 Free Software Foundation, Inc.
5 * Written by James Clark (jjc@jclark.com)
7 * This 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.
12 * This 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.
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.
23 * I have tried to incorporate the changes needed for TeX 3.0 tfm files,
24 * but I haven't tested them.
26 * Groff requires more font metric information than TeX. The reason
27 * for this is that TeX has separate Math Italic fonts, whereas groff
28 * uses normal italic fonts for math. The two additional pieces of
29 * information required by groff correspond to the two arguments to the
30 * math_fit() macro in the Metafont programs for the CM fonts. In the
31 * case of a font for which math_fitting is false, these two arguments
32 * are normally ignored by Metafont. We need to get hold of these two
33 * parameters and put them in the groff font file.
35 * We do this by loading this definition after cmbase when creating cm.base.
37 * def 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;
43 * This 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
45 * which they apply.) We then create a gf file using this cm.base. Then
46 * we run tfmtodit and specify this gf file with the -g option.
48 * This need only be done for a font for which math_fitting is false;
49 * When it's true, the left_correction and subscript_correction should
50 * both be zero.
53 #include "config.h"
54 #include "tfmtodit-config.h"
56 #include <assert.h>
57 #include <errno.h>
58 #include <math.h>
59 #include <stdlib.h>
61 #include "cset.h"
62 #include "errarg.h"
63 #include "error.h"
64 #include "lib.h"
65 #include "nonposix.h"
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;
76 struct lig_kern_command {
77 unsigned char skip_byte;
78 unsigned char next_char;
79 unsigned char op_byte;
80 unsigned char remainder;
83 class tfm
85 friend class kern_iterator;
87 int bc;
88 int ec;
89 int nw;
90 int nh;
91 int nd;
92 int ni;
93 int nl;
94 int nk;
95 int np;
96 int cs;
97 int ds;
98 char_info_word *char_info;
99 int *width;
100 int *height;
101 int *depth;
102 int *italic;
103 lig_kern_command *lig_kern;
104 int *kern;
105 int *param;
107 public:
108 tfm();
109 ~tfm();
110 int load(const char *);
111 int contains(int);
112 int get_width(int);
113 int get_height(int);
114 int get_depth(int);
115 int get_italic(int);
116 int get_param(int, int *);
117 int get_checksum();
118 int get_design_size();
119 int get_lig(unsigned char, unsigned char, unsigned char *);
122 class kern_iterator
124 tfm *t;
125 int c;
126 int i;
128 public:
129 kern_iterator(tfm *);
130 int next(unsigned char *c1, unsigned char *c2, int *k);
134 kern_iterator::kern_iterator(tfm *p)
135 : t(p), c(t->bc), i(-1)
139 int kern_iterator::next(unsigned char *c1, unsigned char *c2, int *k)
141 for (; c <= t->ec; c++)
142 if (t->char_info[c - t->bc].tag == 1) {
143 if (i < 0) {
144 i = t->char_info[c - t->bc].remainder;
145 if (t->lig_kern[i].skip_byte > 128)
146 i = (256*t->lig_kern[i].op_byte
147 + t->lig_kern[i].remainder);
149 for (;;) {
150 int skip = t->lig_kern[i].skip_byte;
151 if (skip <= 128 && t->lig_kern[i].op_byte >= 128) {
152 *c1 = c;
153 *c2 = t->lig_kern[i].next_char;
154 *k = t->kern[256*(t->lig_kern[i].op_byte - 128)
155 + t->lig_kern[i].remainder];
156 if (skip == 128) {
157 c++;
158 i = -1;
160 else
161 i += skip + 1;
162 return 1;
164 if (skip >= 128)
165 break;
166 i += skip + 1;
168 i = -1;
170 return 0;
173 tfm::tfm()
174 : char_info(0), width(0), height(0), depth(0), italic(0), lig_kern(0),
175 kern(0), param(0)
179 int tfm::get_lig(unsigned char c1, unsigned char c2, unsigned char *cp)
181 if (contains(c1) && char_info[c1 - bc].tag == 1) {
182 int i = char_info[c1 - bc].remainder;
183 if (lig_kern[i].skip_byte > 128)
184 i = 256*lig_kern[i].op_byte + lig_kern[i].remainder;
185 for (;;) {
186 int skip = lig_kern[i].skip_byte;
187 if (skip > 128)
188 break;
189 // We are only interested in normal ligatures, for which
190 // op_byte == 0.
191 if (lig_kern[i].op_byte == 0
192 && lig_kern[i].next_char == c2) {
193 *cp = lig_kern[i].remainder;
194 return 1;
196 if (skip == 128)
197 break;
198 i += skip + 1;
201 return 0;
204 int tfm::contains(int i)
206 return i >= bc && i <= ec && char_info[i - bc].width_index != 0;
209 int tfm::get_width(int i)
211 return width[char_info[i - bc].width_index];
214 int tfm::get_height(int i)
216 return height[char_info[i - bc].height_index];
219 int tfm::get_depth(int i)
221 return depth[char_info[i - bc].depth_index];
224 int tfm::get_italic(int i)
226 return italic[char_info[i - bc].italic_index];
229 int tfm::get_param(int i, int *p)
231 if (i <= 0 || i > np)
232 return 0;
233 else {
234 *p = param[i - 1];
235 return 1;
239 int tfm::get_checksum()
241 return cs;
244 int tfm::get_design_size()
246 return ds;
249 tfm::~tfm()
251 a_delete char_info;
252 a_delete width;
253 a_delete height;
254 a_delete depth;
255 a_delete italic;
256 a_delete lig_kern;
257 a_delete kern;
258 a_delete param;
261 int read2(unsigned char *&s)
263 int n;
264 n = *s++ << 8;
265 n |= *s++;
266 return n;
269 int read4(unsigned char *&s)
271 int n;
272 n = *s++ << 24;
273 n |= *s++ << 16;
274 n |= *s++ << 8;
275 n |= *s++;
276 return n;
279 int tfm::load(const char *file)
281 errno = 0;
282 FILE *fp = fopen(file, FOPEN_RB);
283 if (!fp) {
284 error("can't open `%1': %2", file, strerror(errno));
285 return 0;
287 int c1 = getc(fp);
288 int c2 = getc(fp);
289 if (c1 == EOF || c2 == EOF) {
290 fclose(fp);
291 error("unexpected end of file on `%1'", file);
292 return 0;
294 int lf = (c1 << 8) + c2;
295 int toread = lf*4 - 2;
296 unsigned char *buf = new unsigned char[toread];
297 if (fread(buf, 1, toread, fp) != (size_t)toread) {
298 if (feof(fp))
299 error("unexpected end of file on `%1'", file);
300 else
301 error("error on file `%1'", file);
302 a_delete buf;
303 fclose(fp);
304 return 0;
306 fclose(fp);
307 if (lf < 6) {
308 error("bad tfm file `%1': impossibly short", file);
309 a_delete buf;
310 return 0;
312 unsigned char *ptr = buf;
313 int lh = read2(ptr);
314 bc = read2(ptr);
315 ec = read2(ptr);
316 nw = read2(ptr);
317 nh = read2(ptr);
318 nd = read2(ptr);
319 ni = read2(ptr);
320 nl = read2(ptr);
321 nk = read2(ptr);
322 int ne = read2(ptr);
323 np = read2(ptr);
324 if (6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np != lf) {
325 error("bad tfm file `%1': lengths do not sum", file);
326 a_delete buf;
327 return 0;
329 if (lh < 2) {
330 error("bad tfm file `%1': header too short", file);
331 a_delete buf;
332 return 0;
334 char_info = new char_info_word[ec - bc + 1];
335 width = new int[nw];
336 height = new int[nh];
337 depth = new int[nd];
338 italic = new int[ni];
339 lig_kern = new lig_kern_command[nl];
340 kern = new int[nk];
341 param = new int[np];
342 int i;
343 cs = read4(ptr);
344 ds = read4(ptr);
345 ptr += (lh-2)*4;
346 for (i = 0; i < ec - bc + 1; i++) {
347 char_info[i].width_index = *ptr++;
348 unsigned char tem = *ptr++;
349 char_info[i].depth_index = tem & 0xf;
350 char_info[i].height_index = tem >> 4;
351 tem = *ptr++;
352 char_info[i].italic_index = tem >> 2;
353 char_info[i].tag = tem & 3;
354 char_info[i].remainder = *ptr++;
356 for (i = 0; i < nw; i++)
357 width[i] = read4(ptr);
358 for (i = 0; i < nh; i++)
359 height[i] = read4(ptr);
360 for (i = 0; i < nd; i++)
361 depth[i] = read4(ptr);
362 for (i = 0; i < ni; i++)
363 italic[i] = read4(ptr);
364 for (i = 0; i < nl; i++) {
365 lig_kern[i].skip_byte = *ptr++;
366 lig_kern[i].next_char = *ptr++;
367 lig_kern[i].op_byte = *ptr++;
368 lig_kern[i].remainder = *ptr++;
370 for (i = 0; i < nk; i++)
371 kern[i] = read4(ptr);
372 ptr += ne*4;
373 for (i = 0; i < np; i++)
374 param[i] = read4(ptr);
375 assert(ptr == buf + lf*4 - 2);
376 a_delete buf;
377 return 1;
380 class gf
382 static int sread4(int *p, FILE *fp);
383 static int uread3(int *p, FILE *fp);
384 static int uread2(int *p, FILE *fp);
385 static int skip(int n, FILE *fp);
387 int left[256];
388 int right[256];
390 public:
391 gf();
392 int load(const char *file);
393 int get_left_adjustment(int i) { return left[i]; }
394 int get_right_adjustment(int i) { return right[i]; }
397 gf::gf()
399 for (int i = 0; i < 256; i++)
400 left[i] = right[i] = 0;
403 int gf::load(const char *file)
405 enum {
406 paint_0 = 0,
407 paint1 = 64,
408 boc = 67,
409 boc1 = 68,
410 eoc = 69,
411 skip0 = 70,
412 skip1 = 71,
413 new_row_0 = 74,
414 xxx1 = 239,
415 yyy = 243,
416 no_op = 244,
417 pre = 247,
418 post = 248
420 int got_an_adjustment = 0;
421 int pending_adjustment = 0;
422 int left_adj = 0, right_adj = 0; // pacify compiler
423 const int gf_id_byte = 131;
424 errno = 0;
425 FILE *fp = fopen(file, FOPEN_RB);
426 if (!fp) {
427 error("can't open `%1': %2", file, strerror(errno));
428 return 0;
430 if (getc(fp) != pre || getc(fp) != gf_id_byte) {
431 error("bad gf file");
432 return 0;
434 int n = getc(fp);
435 if (n == EOF)
436 goto eof;
437 if (!skip(n, fp))
438 goto eof;
439 for (;;) {
440 int op = getc(fp);
441 if (op == EOF)
442 goto eof;
443 if (op == post)
444 break;
445 if ((op >= paint_0 && op <= paint_0 + 63)
446 || (op >= new_row_0 && op <= new_row_0 + 164))
447 continue;
448 switch (op) {
449 case no_op:
450 case eoc:
451 case skip0:
452 break;
453 case paint1:
454 case skip1:
455 if (!skip(1, fp))
456 goto eof;
457 break;
458 case paint1 + 1:
459 case skip1 + 1:
460 if (!skip(2, fp))
461 goto eof;
462 break;
463 case paint1 + 2:
464 case skip1 + 2:
465 if (!skip(3, fp))
466 goto eof;
467 break;
468 case boc:
470 int code;
471 if (!sread4(&code, fp))
472 goto eof;
473 if (pending_adjustment) {
474 pending_adjustment = 0;
475 left[code & 0377] = left_adj;
476 right[code & 0377] = right_adj;
478 if (!skip(20, fp))
479 goto eof;
480 break;
482 case boc1:
484 int code = getc(fp);
485 if (code == EOF)
486 goto eof;
487 if (pending_adjustment) {
488 pending_adjustment = 0;
489 left[code] = left_adj;
490 right[code] = right_adj;
492 if (!skip(4, fp))
493 goto eof;
494 break;
496 case xxx1:
498 int len = getc(fp);
499 if (len == EOF)
500 goto eof;
501 char buf[256];
502 if (fread(buf, 1, len, fp) != (size_t)len)
503 goto eof;
504 if (len == 10 /* strlen("adjustment") */
505 && memcmp(buf, "adjustment", len) == 0) {
506 int c = getc(fp);
507 if (c != yyy) {
508 if (c != EOF)
509 ungetc(c, fp);
510 break;
512 if (!sread4(&left_adj, fp))
513 goto eof;
514 c = getc(fp);
515 if (c != yyy) {
516 if (c != EOF)
517 ungetc(c, fp);
518 break;
520 if (!sread4(&right_adj, fp))
521 goto eof;
522 got_an_adjustment = 1;
523 pending_adjustment = 1;
525 break;
527 case xxx1 + 1:
528 if (!uread2(&n, fp) || !skip(n, fp))
529 goto eof;
530 break;
531 case xxx1 + 2:
532 if (!uread3(&n, fp) || !skip(n, fp))
533 goto eof;
534 break;
535 case xxx1 + 3:
536 if (!sread4(&n, fp) || !skip(n, fp))
537 goto eof;
538 break;
539 case yyy:
540 if (!skip(4, fp))
541 goto eof;
542 break;
543 default:
544 fatal("unrecognized opcode `%1'", op);
545 break;
548 if (!got_an_adjustment)
549 warning("no adjustment specials found in gf file");
550 return 1;
551 eof:
552 error("unexpected end of file");
553 return 0;
556 int gf::sread4(int *p, FILE *fp)
558 *p = getc(fp);
559 if (*p >= 128)
560 *p -= 256;
561 *p <<= 8;
562 *p |= getc(fp);
563 *p <<= 8;
564 *p |= getc(fp);
565 *p <<= 8;
566 *p |= getc(fp);
567 return !ferror(fp) && !feof(fp);
570 int gf::uread3(int *p, FILE *fp)
572 *p = getc(fp);
573 *p <<= 8;
574 *p |= getc(fp);
575 *p <<= 8;
576 *p |= getc(fp);
577 return !ferror(fp) && !feof(fp);
580 int gf::uread2(int *p, FILE *fp)
582 *p = getc(fp);
583 *p <<= 8;
584 *p |= getc(fp);
585 return !ferror(fp) && !feof(fp);
588 int gf::skip(int n, FILE *fp)
590 while (--n >= 0)
591 if (getc(fp) == EOF)
592 return 0;
593 return 1;
596 class char_list
598 public:
599 char *ch;
600 char_list *next;
602 char_list(const char *, char_list * = 0);
605 char_list::char_list(const char *s, char_list *p) : ch(strsave(s)), next(p)
609 int read_map(const char *file, char_list **table)
611 errno = 0;
612 FILE *fp = fopen(file, "r");
613 if (!fp) {
614 error("can't open `%1': %2", file, strerror(errno));
615 return 0;
617 for (int i = 0; i < 256; i++)
618 table[i] = 0;
619 char buf[512];
620 int lineno = 0;
621 while (fgets(buf, int(sizeof(buf)), fp)) {
622 lineno++;
623 char *ptr = buf;
624 while (csspace(*ptr))
625 ptr++;
626 if (*ptr == '\0' || *ptr == '#')
627 continue;
628 ptr = strtok(ptr, " \n\t");
629 if (!ptr)
630 continue;
631 int n;
632 if (sscanf(ptr, "%d", &n) != 1) {
633 error("%1:%2: bad map file", file, lineno);
634 fclose(fp);
635 return 0;
637 if (n < 0 || n > 255) {
638 error("%1:%2: code out of range", file, lineno);
639 fclose(fp);
640 return 0;
642 ptr = strtok(0, " \n\t");
643 if (!ptr) {
644 error("%1:%2: missing names", file, lineno);
645 fclose(fp);
646 return 0;
648 for (; ptr; ptr = strtok(0, " \n\t"))
649 table[n] = new char_list(ptr, table[n]);
651 fclose(fp);
652 return 1;
656 * Every character that can participate in a ligature appears in the
657 * lig_chars table. `ch' gives the full-name of the character, `name'
658 * gives the groff name of the character, `i' gives its index in
659 * the encoding, which is filled in later (-1 if it does not appear).
662 struct S {
663 const char *ch;
664 int i;
665 } lig_chars[] = { // FIXME const
666 { "f", -1 },
667 { "i", -1 },
668 { "l", -1 },
669 { "ff", -1 },
670 { "fi", -1 },
671 { "fl", -1 },
672 { "Fi", -1 },
673 { "Fl", -1 },
676 // Indices into lig_chars[].
678 enum { CH_f, CH_i, CH_l, CH_ff, CH_fi, CH_fl, CH_ffi, CH_ffl };
680 // Each possible ligature appears in this table.
682 struct S2 {
683 unsigned char c1, c2, res;
684 const char *ch;
685 } lig_table[] = { // FIXME const
686 { CH_f, CH_f, CH_ff, "ff" },
687 { CH_f, CH_i, CH_fi, "fi" },
688 { CH_f, CH_l, CH_fl, "fl" },
689 { CH_ff, CH_i, CH_ffi, "ffi" },
690 { CH_ff, CH_l, CH_ffl, "ffl" },
693 static void usage(FILE *stream);
695 int main(int argc, char **argv)
697 program_name = argv[0];
698 int special_flag = 0;
699 int skewchar = -1;
700 int opt;
701 const char *gf_file = 0;
702 static const struct option long_options[] = {
703 { "help", no_argument, 0, CHAR_MAX + 1 },
704 { "version", no_argument, 0, 'v' },
705 { NULL, 0, 0, 0 }
707 while ((opt = getopt_long(argc, argv, "svg:k:", long_options, NULL)) != EOF)
708 switch (opt) {
709 case 'g':
710 gf_file = optarg;
711 break;
712 case 's':
713 special_flag = 1;
714 break;
715 case 'k':
717 char *ptr;
718 long n = strtol(optarg, &ptr, 0);
719 if ((n == 0 && ptr == optarg)
720 || *ptr != '\0'
721 || n < 0
722 || n > UCHAR_MAX)
723 error("invalid skewchar");
724 else
725 skewchar = (int)n;
726 break;
728 case 'v':
730 printf(L_TFMTODIT " (" T_ROFF ") v" VERSION);
731 exit(0);
732 break;
734 case CHAR_MAX + 1: // --help
735 usage(stdout);
736 exit(0);
737 break;
738 case '?':
739 usage(stderr);
740 exit(1);
741 break;
742 case EOF:
743 assert(0);
745 if (argc - optind != 3) {
746 usage(stderr);
747 exit(1);
749 gf g;
750 if (gf_file) {
751 if (!g.load(gf_file))
752 return 1;
754 const char *tfm_file = argv[optind];
755 const char *map_file = argv[optind + 1];
756 const char *font_file = argv[optind + 2];
757 tfm t;
758 if (!t.load(tfm_file))
759 return 1;
760 char_list *table[256];
761 if (!read_map(map_file, table))
762 return 1;
763 errno = 0;
764 if (!freopen(font_file, "w", stdout)) {
765 error("can't open `%1' for writing: %2", font_file, strerror(errno));
766 return 1;
768 printf("name %s\n", font_file);
769 if (special_flag)
770 fputs("special\n", stdout);
771 char *internal_name = strsave(argv[optind]);
772 int len = strlen(internal_name);
773 if (len > 4 && strcmp(internal_name + len - 4, ".tfm") == 0)
774 internal_name[len - 4] = '\0';
775 // DIR_SEPS[] are possible directory separator characters, see nonposix.h.
776 // We want the rightmost separator of all possible ones.
777 // Example: d:/foo\\bar.
778 const char *s = strrchr(internal_name, DIR_SEPS[0]), *s1;
779 const char *sep = &DIR_SEPS[1];
780 while (*sep)
782 s1 = strrchr(internal_name, *sep);
783 if (s1 && (!s || s1 > s))
784 s = s1;
785 sep++;
787 printf("internalname %s\n", s ? s + 1 : internal_name);
788 int n;
789 if (t.get_param(2, &n)) {
790 if (n > 0)
791 printf("spacewidth %d\n", n*MULTIPLIER);
793 if (t.get_param(1, &n) && n != 0)
794 printf("slant %f\n", atan2(n/double(1<<20), 1.0)*180.0/PI);
795 int xheight;
796 if (!t.get_param(5, &xheight))
797 xheight = 0;
798 unsigned int i;
799 // Print the list of ligatures.
800 // First find the indices of each character that can participate in
801 // a ligature.
802 for (i = 0; i < 256; i++)
803 for (unsigned int j = 0; j < sizeof(lig_chars)/sizeof(lig_chars[0]); j++)
804 for (char_list *p = table[i]; p; p = p->next)
805 if (strcmp(lig_chars[j].ch, p->ch) == 0)
806 lig_chars[j].i = i;
807 // For each possible ligature, if its participants all exist,
808 // and it appears as a ligature in the tfm file, include in
809 // the list of ligatures.
810 int started = 0;
811 for (i = 0; i < sizeof(lig_table)/sizeof(lig_table[0]); i++) {
812 int i1 = lig_chars[lig_table[i].c1].i;
813 int i2 = lig_chars[lig_table[i].c2].i;
814 int r = lig_chars[lig_table[i].res].i;
815 if (i1 >= 0 && i2 >= 0 && r >= 0) {
816 unsigned char c;
817 if (t.get_lig(i1, i2, &c) && c == r) {
818 if (!started) {
819 started = 1;
820 fputs("ligatures", stdout);
822 printf(" %s", lig_table[i].ch);
826 if (started)
827 fputs(" 0\n", stdout);
828 printf("checksum %d\n", t.get_checksum());
829 printf("designsize %d\n", t.get_design_size());
830 // Now print out the kerning information.
831 int had_kern = 0;
832 kern_iterator iter(&t);
833 unsigned char c1, c2;
834 int k;
835 while (iter.next(&c1, &c2, &k))
836 if (c2 != skewchar) {
837 k *= MULTIPLIER;
838 char_list *q = table[c2];
839 for (char_list *p1 = table[c1]; p1; p1 = p1->next)
840 for (char_list *p2 = q; p2; p2 = p2->next) {
841 if (!had_kern) {
842 printf("kernpairs\n");
843 had_kern = 1;
845 printf("%s %s %d\n", p1->ch, p2->ch, k);
848 printf("charset\n");
849 char_list unnamed("---");
850 for (i = 0; i < 256; i++)
851 if (t.contains(i)) {
852 char_list *p = table[i] ? table[i] : &unnamed;
853 int m[6];
854 m[0] = t.get_width(i);
855 m[1] = t.get_height(i);
856 m[2] = t.get_depth(i);
857 m[3] = t.get_italic(i);
858 m[4] = g.get_left_adjustment(i);
859 m[5] = g.get_right_adjustment(i);
860 printf("%s\t%d", p->ch, m[0]*MULTIPLIER);
861 int j;
862 for (j = int(sizeof(m)/sizeof(m[0])) - 1; j > 0; j--)
863 if (m[j] != 0)
864 break;
865 for (k = 1; k <= j; k++)
866 printf(",%d", m[k]*MULTIPLIER);
867 int type = 0;
868 if (m[2] > 0)
869 type = 1;
870 if (m[1] > xheight)
871 type += 2;
872 printf("\t%d\t%04o\n", type, i);
873 for (p = p->next; p; p = p->next)
874 printf("%s\t\"\n", p->ch);
876 return 0;
879 static void usage(FILE *stream)
881 fprintf(stream,
882 "Synopsis: %s [-sv] [-g gf_file] [-k skewchar] tfm_file map_file font\n",
883 program_name);
886 // s-it2-mode