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