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