1114402Sru// -*- C++ -*- 2151497Sru/* Copyright (C) 1989-1992, 2000, 2001, 2004 Free Software Foundation, Inc. 3114402Sru Written by James Clark (jjc@jclark.com) 4114402Sru 5114402SruThis file is part of groff. 6114402Sru 7114402Srugroff is free software; you can redistribute it and/or modify it under 8114402Sruthe terms of the GNU General Public License as published by the Free 9114402SruSoftware Foundation; either version 2, or (at your option) any later 10114402Sruversion. 11114402Sru 12114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY 13114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or 14114402SruFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15114402Srufor more details. 16114402Sru 17114402SruYou should have received a copy of the GNU General Public License along 18114402Sruwith groff; see the file COPYING. If not, write to the Free Software 19151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 20114402Sru 21114402Sru/* I have tried to incorporate the changes needed for TeX 3.0 tfm files, 22114402Srubut I haven't tested them. */ 23114402Sru 24114402Sru/* Groff requires more font metric information than TeX. The reason 25114402Srufor this is that TeX has separate Math Italic fonts, whereas groff 26114402Sruuses normal italic fonts for math. The two additional pieces of 27114402Sruinformation required by groff correspond to the two arguments to the 28114402Srumath_fit() macro in the Metafont programs for the CM fonts. In the 29114402Srucase of a font for which math_fitting is false, these two arguments 30114402Sruare normally ignored by Metafont. We need to get hold of these two 31114402Sruparameters and put them in the groff font file. 32114402Sru 33114402SruWe do this by loading this definition after cmbase when creating cm.base. 34114402Sru 35114402Srudef ignore_math_fit(expr left_adjustment,right_adjustment) = 36114402Sru special "adjustment"; 37114402Sru numspecial left_adjustment*16/designsize; 38114402Sru numspecial right_adjustment*16/designsize; 39114402Sru enddef; 40114402Sru 41114402SruThis puts the two arguments to the math_fit macro into the gf file. 42114402Sru(They will appear in the gf file immediately before the character to 43114402Sruwhich they apply.) We then create a gf file using this cm.base. Then 44114402Sruwe run tfmtodit and specify this gf file with the -g option. 45114402Sru 46114402SruThis need only be done for a font for which math_fitting is false; 47114402SruWhen it's true, the left_correction and subscript_correction should 48114402Sruboth be zero. */ 49114402Sru 50114402Sru#include "lib.h" 51114402Sru 52114402Sru#include <stdlib.h> 53114402Sru#include <math.h> 54114402Sru#include <errno.h> 55114402Sru#include "errarg.h" 56114402Sru#include "error.h" 57114402Sru#include "assert.h" 58114402Sru#include "cset.h" 59114402Sru#include "nonposix.h" 60114402Sru 61114402Sruextern "C" const char *Version_string; 62114402Sru 63114402Sru/* Values in the tfm file should be multiplied by this. */ 64114402Sru 65114402Sru#define MULTIPLIER 1 66114402Sru 67114402Srustruct char_info_word { 68114402Sru unsigned char width_index; 69114402Sru char height_index; 70114402Sru char depth_index; 71114402Sru char italic_index; 72114402Sru char tag; 73114402Sru unsigned char remainder; 74114402Sru}; 75114402Sru 76114402Srustruct lig_kern_command { 77114402Sru unsigned char skip_byte; 78114402Sru unsigned char next_char; 79114402Sru unsigned char op_byte; 80114402Sru unsigned char remainder; 81114402Sru}; 82114402Sru 83114402Sruclass tfm { 84114402Sru int bc; 85114402Sru int ec; 86114402Sru int nw; 87114402Sru int nh; 88114402Sru int nd; 89114402Sru int ni; 90114402Sru int nl; 91114402Sru int nk; 92114402Sru int np; 93114402Sru int cs; 94114402Sru int ds; 95114402Sru char_info_word *char_info; 96114402Sru int *width; 97114402Sru int *height; 98114402Sru int *depth; 99114402Sru int *italic; 100114402Sru lig_kern_command *lig_kern; 101114402Sru int *kern; 102114402Sru int *param; 103114402Srupublic: 104114402Sru tfm(); 105114402Sru ~tfm(); 106114402Sru int load(const char *); 107114402Sru int contains(int); 108114402Sru int get_width(int); 109114402Sru int get_height(int); 110114402Sru int get_depth(int); 111114402Sru int get_italic(int); 112114402Sru int get_param(int, int *); 113114402Sru int get_checksum(); 114114402Sru int get_design_size(); 115114402Sru int get_lig(unsigned char, unsigned char, unsigned char *); 116114402Sru friend class kern_iterator; 117114402Sru}; 118114402Sru 119114402Sruclass kern_iterator { 120114402Sru tfm *t; 121114402Sru int c; 122114402Sru int i; 123114402Srupublic: 124114402Sru kern_iterator(tfm *); 125114402Sru int next(unsigned char *c1, unsigned char *c2, int *k); 126114402Sru}; 127114402Sru 128114402Sru 129114402Srukern_iterator::kern_iterator(tfm *p) 130114402Sru: t(p), c(t->bc), i(-1) 131114402Sru{ 132114402Sru} 133114402Sru 134114402Sruint kern_iterator::next(unsigned char *c1, unsigned char *c2, int *k) 135114402Sru{ 136114402Sru for (; c <= t->ec; c++) 137114402Sru if (t->char_info[c - t->bc].tag == 1) { 138114402Sru if (i < 0) { 139114402Sru i = t->char_info[c - t->bc].remainder; 140114402Sru if (t->lig_kern[i].skip_byte > 128) 141114402Sru i = (256*t->lig_kern[i].op_byte 142114402Sru + t->lig_kern[i].remainder); 143114402Sru } 144114402Sru for (;;) { 145114402Sru int skip = t->lig_kern[i].skip_byte; 146114402Sru if (skip <= 128 && t->lig_kern[i].op_byte >= 128) { 147114402Sru *c1 = c; 148114402Sru *c2 = t->lig_kern[i].next_char; 149114402Sru *k = t->kern[256*(t->lig_kern[i].op_byte - 128) 150114402Sru + t->lig_kern[i].remainder]; 151114402Sru if (skip == 128) { 152114402Sru c++; 153114402Sru i = -1; 154114402Sru } 155114402Sru else 156114402Sru i += skip + 1; 157114402Sru return 1; 158114402Sru } 159114402Sru if (skip >= 128) 160114402Sru break; 161114402Sru i += skip + 1; 162114402Sru } 163114402Sru i = -1; 164114402Sru } 165114402Sru return 0; 166114402Sru} 167114402Sru 168114402Srutfm::tfm() 169114402Sru: char_info(0), width(0), height(0), depth(0), italic(0), lig_kern(0), 170114402Sru kern(0), param(0) 171114402Sru{ 172114402Sru} 173114402Sru 174114402Sruint tfm::get_lig(unsigned char c1, unsigned char c2, unsigned char *cp) 175114402Sru{ 176114402Sru if (contains(c1) && char_info[c1 - bc].tag == 1) { 177114402Sru int i = char_info[c1 - bc].remainder; 178114402Sru if (lig_kern[i].skip_byte > 128) 179114402Sru i = 256*lig_kern[i].op_byte + lig_kern[i].remainder; 180114402Sru for (;;) { 181114402Sru int skip = lig_kern[i].skip_byte; 182114402Sru if (skip > 128) 183114402Sru break; 184114402Sru // We are only interested in normal ligatures, for which 185114402Sru // op_byte == 0. 186114402Sru if (lig_kern[i].op_byte == 0 187114402Sru && lig_kern[i].next_char == c2) { 188114402Sru *cp = lig_kern[i].remainder; 189114402Sru return 1; 190114402Sru } 191114402Sru if (skip == 128) 192114402Sru break; 193114402Sru i += skip + 1; 194114402Sru } 195114402Sru } 196114402Sru return 0; 197114402Sru} 198114402Sru 199114402Sruint tfm::contains(int i) 200114402Sru{ 201114402Sru return i >= bc && i <= ec && char_info[i - bc].width_index != 0; 202114402Sru} 203114402Sru 204114402Sruint tfm::get_width(int i) 205114402Sru{ 206114402Sru return width[char_info[i - bc].width_index]; 207114402Sru} 208114402Sru 209114402Sruint tfm::get_height(int i) 210114402Sru{ 211114402Sru return height[char_info[i - bc].height_index]; 212114402Sru} 213114402Sru 214114402Sruint tfm::get_depth(int i) 215114402Sru{ 216114402Sru return depth[char_info[i - bc].depth_index]; 217114402Sru} 218114402Sru 219114402Sruint tfm::get_italic(int i) 220114402Sru{ 221114402Sru return italic[char_info[i - bc].italic_index]; 222114402Sru} 223114402Sru 224114402Sruint tfm::get_param(int i, int *p) 225114402Sru{ 226114402Sru if (i <= 0 || i > np) 227114402Sru return 0; 228114402Sru else { 229114402Sru *p = param[i - 1]; 230114402Sru return 1; 231114402Sru } 232114402Sru} 233114402Sru 234114402Sruint tfm::get_checksum() 235114402Sru{ 236114402Sru return cs; 237114402Sru} 238114402Sru 239114402Sruint tfm::get_design_size() 240114402Sru{ 241114402Sru return ds; 242114402Sru} 243114402Sru 244114402Srutfm::~tfm() 245114402Sru{ 246114402Sru a_delete char_info; 247114402Sru a_delete width; 248114402Sru a_delete height; 249114402Sru a_delete depth; 250114402Sru a_delete italic; 251114402Sru a_delete lig_kern; 252114402Sru a_delete kern; 253114402Sru a_delete param; 254114402Sru} 255114402Sru 256114402Sruint read2(unsigned char *&s) 257114402Sru{ 258114402Sru int n; 259114402Sru n = *s++ << 8; 260114402Sru n |= *s++; 261114402Sru return n; 262114402Sru} 263114402Sru 264114402Sruint read4(unsigned char *&s) 265114402Sru{ 266114402Sru int n; 267114402Sru n = *s++ << 24; 268114402Sru n |= *s++ << 16; 269114402Sru n |= *s++ << 8; 270114402Sru n |= *s++; 271114402Sru return n; 272114402Sru} 273114402Sru 274114402Sru 275114402Sruint tfm::load(const char *file) 276114402Sru{ 277114402Sru errno = 0; 278114402Sru FILE *fp = fopen(file, FOPEN_RB); 279114402Sru if (!fp) { 280114402Sru error("can't open `%1': %2", file, strerror(errno)); 281114402Sru return 0; 282114402Sru } 283114402Sru int c1 = getc(fp); 284114402Sru int c2 = getc(fp); 285114402Sru if (c1 == EOF || c2 == EOF) { 286114402Sru fclose(fp); 287114402Sru error("unexpected end of file on `%1'", file); 288114402Sru return 0; 289114402Sru } 290114402Sru int lf = (c1 << 8) + c2; 291114402Sru int toread = lf*4 - 2; 292114402Sru unsigned char *buf = new unsigned char[toread]; 293114402Sru if (fread(buf, 1, toread, fp) != (size_t)toread) { 294114402Sru if (feof(fp)) 295114402Sru error("unexpected end of file on `%1'", file); 296114402Sru else 297114402Sru error("error on file `%1'", file); 298114402Sru a_delete buf; 299114402Sru fclose(fp); 300114402Sru return 0; 301114402Sru } 302114402Sru fclose(fp); 303114402Sru if (lf < 6) { 304114402Sru error("bad tfm file `%1': impossibly short", file); 305114402Sru a_delete buf; 306114402Sru return 0; 307114402Sru } 308114402Sru unsigned char *ptr = buf; 309114402Sru int lh = read2(ptr); 310114402Sru bc = read2(ptr); 311114402Sru ec = read2(ptr); 312114402Sru nw = read2(ptr); 313114402Sru nh = read2(ptr); 314114402Sru nd = read2(ptr); 315114402Sru ni = read2(ptr); 316114402Sru nl = read2(ptr); 317114402Sru nk = read2(ptr); 318114402Sru int ne = read2(ptr); 319114402Sru np = read2(ptr); 320114402Sru if (6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np != lf) { 321114402Sru error("bad tfm file `%1': lengths do not sum", file); 322114402Sru a_delete buf; 323114402Sru return 0; 324114402Sru } 325114402Sru if (lh < 2) { 326114402Sru error("bad tfm file `%1': header too short", file); 327114402Sru a_delete buf; 328114402Sru return 0; 329114402Sru } 330114402Sru char_info = new char_info_word[ec - bc + 1]; 331114402Sru width = new int[nw]; 332114402Sru height = new int[nh]; 333114402Sru depth = new int[nd]; 334114402Sru italic = new int[ni]; 335114402Sru lig_kern = new lig_kern_command[nl]; 336114402Sru kern = new int[nk]; 337114402Sru param = new int[np]; 338114402Sru int i; 339114402Sru cs = read4(ptr); 340114402Sru ds = read4(ptr); 341114402Sru ptr += (lh-2)*4; 342114402Sru for (i = 0; i < ec - bc + 1; i++) { 343114402Sru char_info[i].width_index = *ptr++; 344114402Sru unsigned char tem = *ptr++; 345114402Sru char_info[i].depth_index = tem & 0xf; 346114402Sru char_info[i].height_index = tem >> 4; 347114402Sru tem = *ptr++; 348114402Sru char_info[i].italic_index = tem >> 2; 349114402Sru char_info[i].tag = tem & 3; 350114402Sru char_info[i].remainder = *ptr++; 351114402Sru } 352114402Sru for (i = 0; i < nw; i++) 353114402Sru width[i] = read4(ptr); 354114402Sru for (i = 0; i < nh; i++) 355114402Sru height[i] = read4(ptr); 356114402Sru for (i = 0; i < nd; i++) 357114402Sru depth[i] = read4(ptr); 358114402Sru for (i = 0; i < ni; i++) 359114402Sru italic[i] = read4(ptr); 360114402Sru for (i = 0; i < nl; i++) { 361114402Sru lig_kern[i].skip_byte = *ptr++; 362114402Sru lig_kern[i].next_char = *ptr++; 363114402Sru lig_kern[i].op_byte = *ptr++; 364114402Sru lig_kern[i].remainder = *ptr++; 365114402Sru } 366114402Sru for (i = 0; i < nk; i++) 367114402Sru kern[i] = read4(ptr); 368114402Sru ptr += ne*4; 369114402Sru for (i = 0; i < np; i++) 370114402Sru param[i] = read4(ptr); 371114402Sru assert(ptr == buf + lf*4 - 2); 372114402Sru a_delete buf; 373114402Sru return 1; 374114402Sru} 375114402Sru 376114402Sruclass gf { 377114402Sru int left[256]; 378114402Sru int right[256]; 379114402Sru static int sread4(int *p, FILE *fp); 380114402Sru static int uread3(int *p, FILE *fp); 381114402Sru static int uread2(int *p, FILE *fp); 382114402Sru static int skip(int n, FILE *fp); 383114402Srupublic: 384114402Sru gf(); 385114402Sru int load(const char *file); 386114402Sru int get_left_adjustment(int i) { return left[i]; } 387114402Sru int get_right_adjustment(int i) { return right[i]; } 388114402Sru}; 389114402Sru 390114402Srugf::gf() 391114402Sru{ 392114402Sru for (int i = 0; i < 256; i++) 393114402Sru left[i] = right[i] = 0; 394114402Sru} 395114402Sru 396114402Sruint gf::load(const char *file) 397114402Sru{ 398114402Sru enum { 399114402Sru paint_0 = 0, 400114402Sru paint1 = 64, 401114402Sru boc = 67, 402114402Sru boc1 = 68, 403114402Sru eoc = 69, 404114402Sru skip0 = 70, 405114402Sru skip1 = 71, 406114402Sru new_row_0 = 74, 407114402Sru xxx1 = 239, 408114402Sru yyy = 243, 409114402Sru no_op = 244, 410114402Sru pre = 247, 411114402Sru post = 248 412114402Sru }; 413114402Sru int got_an_adjustment = 0; 414114402Sru int pending_adjustment = 0; 415151497Sru int left_adj = 0, right_adj = 0; // pacify compiler 416114402Sru const int gf_id_byte = 131; 417114402Sru errno = 0; 418114402Sru FILE *fp = fopen(file, FOPEN_RB); 419114402Sru if (!fp) { 420114402Sru error("can't open `%1': %2", file, strerror(errno)); 421114402Sru return 0; 422114402Sru } 423114402Sru if (getc(fp) != pre || getc(fp) != gf_id_byte) { 424114402Sru error("bad gf file"); 425114402Sru return 0; 426114402Sru } 427114402Sru int n = getc(fp); 428114402Sru if (n == EOF) 429114402Sru goto eof; 430114402Sru if (!skip(n, fp)) 431114402Sru goto eof; 432114402Sru for (;;) { 433114402Sru int op = getc(fp); 434114402Sru if (op == EOF) 435114402Sru goto eof; 436114402Sru if (op == post) 437114402Sru break; 438114402Sru if ((op >= paint_0 && op <= paint_0 + 63) 439114402Sru || (op >= new_row_0 && op <= new_row_0 + 164)) 440114402Sru continue; 441114402Sru switch (op) { 442114402Sru case no_op: 443114402Sru case eoc: 444114402Sru case skip0: 445114402Sru break; 446114402Sru case paint1: 447114402Sru case skip1: 448114402Sru if (!skip(1, fp)) 449114402Sru goto eof; 450114402Sru break; 451114402Sru case paint1 + 1: 452114402Sru case skip1 + 1: 453114402Sru if (!skip(2, fp)) 454114402Sru goto eof; 455114402Sru break; 456114402Sru case paint1 + 2: 457114402Sru case skip1 + 2: 458114402Sru if (!skip(3, fp)) 459114402Sru goto eof; 460114402Sru break; 461114402Sru case boc: 462114402Sru { 463114402Sru int code; 464114402Sru if (!sread4(&code, fp)) 465114402Sru goto eof; 466114402Sru if (pending_adjustment) { 467114402Sru pending_adjustment = 0; 468114402Sru left[code & 0377] = left_adj; 469114402Sru right[code & 0377] = right_adj; 470114402Sru } 471114402Sru if (!skip(20, fp)) 472114402Sru goto eof; 473114402Sru break; 474114402Sru } 475114402Sru case boc1: 476114402Sru { 477114402Sru int code = getc(fp); 478114402Sru if (code == EOF) 479114402Sru goto eof; 480114402Sru if (pending_adjustment) { 481114402Sru pending_adjustment = 0; 482114402Sru left[code] = left_adj; 483114402Sru right[code] = right_adj; 484114402Sru } 485114402Sru if (!skip(4, fp)) 486114402Sru goto eof; 487114402Sru break; 488114402Sru } 489114402Sru case xxx1: 490114402Sru { 491114402Sru int len = getc(fp); 492114402Sru if (len == EOF) 493114402Sru goto eof; 494114402Sru char buf[256]; 495114402Sru if (fread(buf, 1, len, fp) != (size_t)len) 496114402Sru goto eof; 497114402Sru if (len == 10 /* strlen("adjustment") */ 498114402Sru && memcmp(buf, "adjustment", len) == 0) { 499114402Sru int c = getc(fp); 500114402Sru if (c != yyy) { 501114402Sru if (c != EOF) 502114402Sru ungetc(c, fp); 503114402Sru break; 504114402Sru } 505114402Sru if (!sread4(&left_adj, fp)) 506114402Sru goto eof; 507114402Sru c = getc(fp); 508114402Sru if (c != yyy) { 509114402Sru if (c != EOF) 510114402Sru ungetc(c, fp); 511114402Sru break; 512114402Sru } 513114402Sru if (!sread4(&right_adj, fp)) 514114402Sru goto eof; 515114402Sru got_an_adjustment = 1; 516114402Sru pending_adjustment = 1; 517114402Sru } 518114402Sru break; 519114402Sru } 520114402Sru case xxx1 + 1: 521114402Sru if (!uread2(&n, fp) || !skip(n, fp)) 522114402Sru goto eof; 523114402Sru break; 524114402Sru case xxx1 + 2: 525114402Sru if (!uread3(&n, fp) || !skip(n, fp)) 526114402Sru goto eof; 527114402Sru break; 528114402Sru case xxx1 + 3: 529114402Sru if (!sread4(&n, fp) || !skip(n, fp)) 530114402Sru goto eof; 531114402Sru break; 532114402Sru case yyy: 533114402Sru if (!skip(4, fp)) 534114402Sru goto eof; 535114402Sru break; 536114402Sru default: 537114402Sru fatal("unrecognized opcode `%1'", op); 538114402Sru break; 539114402Sru } 540114402Sru } 541114402Sru if (!got_an_adjustment) 542114402Sru warning("no adjustment specials found in gf file"); 543114402Sru return 1; 544114402Sru eof: 545114402Sru error("unexpected end of file"); 546114402Sru return 0; 547114402Sru} 548114402Sru 549114402Sruint gf::sread4(int *p, FILE *fp) 550114402Sru{ 551114402Sru *p = getc(fp); 552114402Sru if (*p >= 128) 553114402Sru *p -= 256; 554114402Sru *p <<= 8; 555114402Sru *p |= getc(fp); 556114402Sru *p <<= 8; 557114402Sru *p |= getc(fp); 558114402Sru *p <<= 8; 559114402Sru *p |= getc(fp); 560114402Sru return !ferror(fp) && !feof(fp); 561114402Sru} 562114402Sru 563114402Sruint gf::uread3(int *p, FILE *fp) 564114402Sru{ 565114402Sru *p = getc(fp); 566114402Sru *p <<= 8; 567114402Sru *p |= getc(fp); 568114402Sru *p <<= 8; 569114402Sru *p |= getc(fp); 570114402Sru return !ferror(fp) && !feof(fp); 571114402Sru} 572114402Sru 573114402Sruint gf::uread2(int *p, FILE *fp) 574114402Sru{ 575114402Sru *p = getc(fp); 576114402Sru *p <<= 8; 577114402Sru *p |= getc(fp); 578114402Sru return !ferror(fp) && !feof(fp); 579114402Sru} 580114402Sru 581114402Sruint gf::skip(int n, FILE *fp) 582114402Sru{ 583114402Sru while (--n >= 0) 584114402Sru if (getc(fp) == EOF) 585114402Sru return 0; 586114402Sru return 1; 587114402Sru} 588114402Sru 589114402Sru 590114402Srustruct char_list { 591114402Sru char *ch; 592114402Sru char_list *next; 593114402Sru char_list(const char *, char_list * = 0); 594114402Sru}; 595114402Sru 596114402Sruchar_list::char_list(const char *s, char_list *p) : ch(strsave(s)), next(p) 597114402Sru{ 598114402Sru} 599114402Sru 600114402Sru 601114402Sruint read_map(const char *file, char_list **table) 602114402Sru{ 603114402Sru errno = 0; 604114402Sru FILE *fp = fopen(file, "r"); 605114402Sru if (!fp) { 606114402Sru error("can't open `%1': %2", file, strerror(errno)); 607114402Sru return 0; 608114402Sru } 609114402Sru for (int i = 0; i < 256; i++) 610114402Sru table[i] = 0; 611114402Sru char buf[512]; 612114402Sru int lineno = 0; 613114402Sru while (fgets(buf, int(sizeof(buf)), fp)) { 614114402Sru lineno++; 615114402Sru char *ptr = buf; 616114402Sru while (csspace(*ptr)) 617114402Sru ptr++; 618114402Sru if (*ptr == '\0' || *ptr == '#') 619114402Sru continue; 620114402Sru ptr = strtok(ptr, " \n\t"); 621114402Sru if (!ptr) 622114402Sru continue; 623114402Sru int n; 624114402Sru if (sscanf(ptr, "%d", &n) != 1) { 625114402Sru error("%1:%2: bad map file", file, lineno); 626114402Sru fclose(fp); 627114402Sru return 0; 628114402Sru } 629114402Sru if (n < 0 || n > 255) { 630114402Sru error("%1:%2: code out of range", file, lineno); 631114402Sru fclose(fp); 632114402Sru return 0; 633114402Sru } 634114402Sru ptr = strtok(0, " \n\t"); 635114402Sru if (!ptr) { 636114402Sru error("%1:%2: missing names", file, lineno); 637114402Sru fclose(fp); 638114402Sru return 0; 639114402Sru } 640114402Sru for (; ptr; ptr = strtok(0, " \n\t")) 641114402Sru table[n] = new char_list(ptr, table[n]); 642114402Sru } 643114402Sru fclose(fp); 644114402Sru return 1; 645114402Sru} 646114402Sru 647114402Sru 648114402Sru/* Every character that can participate in a ligature appears in the 649114402Srulig_chars table. `ch' gives the full-name of the character, `name' 650114402Srugives the groff name of the character, `i' gives its index in 651114402Sruthe encoding, which is filled in later (-1 if it does not appear). */ 652114402Sru 653151497Srustruct S { 654114402Sru const char *ch; 655114402Sru int i; 656114402Sru} lig_chars[] = { 657114402Sru { "f", -1 }, 658114402Sru { "i", -1 }, 659114402Sru { "l", -1 }, 660114402Sru { "ff", -1 }, 661114402Sru { "fi", -1 }, 662114402Sru { "fl", -1 }, 663114402Sru { "Fi", -1 }, 664114402Sru { "Fl", -1 }, 665114402Sru}; 666114402Sru 667114402Sru// Indices into lig_chars[]. 668114402Sru 669114402Sruenum { CH_f, CH_i, CH_l, CH_ff, CH_fi, CH_fl, CH_ffi, CH_ffl }; 670114402Sru 671114402Sru// Each possible ligature appears in this table. 672114402Sru 673151497Srustruct S2 { 674114402Sru unsigned char c1, c2, res; 675114402Sru const char *ch; 676114402Sru} lig_table[] = { 677114402Sru { CH_f, CH_f, CH_ff, "ff" }, 678114402Sru { CH_f, CH_i, CH_fi, "fi" }, 679114402Sru { CH_f, CH_l, CH_fl, "fl" }, 680114402Sru { CH_ff, CH_i, CH_ffi, "ffi" }, 681114402Sru { CH_ff, CH_l, CH_ffl, "ffl" }, 682114402Sru }; 683114402Sru 684114402Srustatic void usage(FILE *stream); 685114402Sru 686114402Sruint main(int argc, char **argv) 687114402Sru{ 688114402Sru program_name = argv[0]; 689114402Sru int special_flag = 0; 690114402Sru int skewchar = -1; 691114402Sru int opt; 692114402Sru const char *gf_file = 0; 693114402Sru static const struct option long_options[] = { 694114402Sru { "help", no_argument, 0, CHAR_MAX + 1 }, 695114402Sru { "version", no_argument, 0, 'v' }, 696114402Sru { NULL, 0, 0, 0 } 697114402Sru }; 698114402Sru while ((opt = getopt_long(argc, argv, "svg:k:", long_options, NULL)) != EOF) 699114402Sru switch (opt) { 700114402Sru case 'g': 701114402Sru gf_file = optarg; 702114402Sru break; 703114402Sru case 's': 704114402Sru special_flag = 1; 705114402Sru break; 706114402Sru case 'k': 707114402Sru { 708114402Sru char *ptr; 709114402Sru long n = strtol(optarg, &ptr, 0); 710114402Sru if ((n == 0 && ptr == optarg) 711114402Sru || *ptr != '\0' 712114402Sru || n < 0 713114402Sru || n > UCHAR_MAX) 714114402Sru error("invalid skewchar"); 715114402Sru else 716114402Sru skewchar = (int)n; 717114402Sru break; 718114402Sru } 719114402Sru case 'v': 720114402Sru { 721114402Sru printf("GNU tfmtodit (groff) version %s\n", Version_string); 722114402Sru exit(0); 723114402Sru break; 724114402Sru } 725114402Sru case CHAR_MAX + 1: // --help 726114402Sru usage(stdout); 727114402Sru exit(0); 728114402Sru break; 729114402Sru case '?': 730114402Sru usage(stderr); 731114402Sru exit(1); 732114402Sru break; 733114402Sru case EOF: 734114402Sru assert(0); 735114402Sru } 736114402Sru if (argc - optind != 3) { 737114402Sru usage(stderr); 738114402Sru exit(1); 739114402Sru } 740114402Sru gf g; 741114402Sru if (gf_file) { 742114402Sru if (!g.load(gf_file)) 743114402Sru return 1; 744114402Sru } 745114402Sru const char *tfm_file = argv[optind]; 746114402Sru const char *map_file = argv[optind + 1]; 747114402Sru const char *font_file = argv[optind + 2]; 748114402Sru tfm t; 749114402Sru if (!t.load(tfm_file)) 750114402Sru return 1; 751114402Sru char_list *table[256]; 752114402Sru if (!read_map(map_file, table)) 753114402Sru return 1; 754114402Sru errno = 0; 755114402Sru if (!freopen(font_file, "w", stdout)) { 756114402Sru error("can't open `%1' for writing: %2", font_file, strerror(errno)); 757114402Sru return 1; 758114402Sru } 759114402Sru printf("name %s\n", font_file); 760114402Sru if (special_flag) 761114402Sru fputs("special\n", stdout); 762114402Sru char *internal_name = strsave(argv[optind]); 763114402Sru int len = strlen(internal_name); 764114402Sru if (len > 4 && strcmp(internal_name + len - 4, ".tfm") == 0) 765114402Sru internal_name[len - 4] = '\0'; 766114402Sru // DIR_SEPS[] are possible directory separator characters, see nonposix.h. 767114402Sru // We want the rightmost separator of all possible ones. 768114402Sru // Example: d:/foo\\bar. 769114402Sru const char *s = strrchr(internal_name, DIR_SEPS[0]), *s1; 770114402Sru const char *sep = &DIR_SEPS[1]; 771114402Sru while (*sep) 772114402Sru { 773114402Sru s1 = strrchr(internal_name, *sep); 774114402Sru if (s1 && (!s || s1 > s)) 775114402Sru s = s1; 776114402Sru sep++; 777114402Sru } 778114402Sru printf("internalname %s\n", s ? s + 1 : internal_name); 779114402Sru int n; 780114402Sru if (t.get_param(2, &n)) { 781114402Sru if (n > 0) 782114402Sru printf("spacewidth %d\n", n*MULTIPLIER); 783114402Sru } 784114402Sru if (t.get_param(1, &n) && n != 0) 785114402Sru printf("slant %f\n", atan2(n/double(1<<20), 1.0)*180.0/PI); 786114402Sru int xheight; 787114402Sru if (!t.get_param(5, &xheight)) 788114402Sru xheight = 0; 789114402Sru unsigned int i; 790114402Sru // Print the list of ligatures. 791114402Sru // First find the indices of each character that can participate in 792114402Sru // a ligature. 793114402Sru for (i = 0; i < 256; i++) 794114402Sru for (unsigned int j = 0; j < sizeof(lig_chars)/sizeof(lig_chars[0]); j++) 795114402Sru for (char_list *p = table[i]; p; p = p->next) 796114402Sru if (strcmp(lig_chars[j].ch, p->ch) == 0) 797114402Sru lig_chars[j].i = i; 798114402Sru // For each possible ligature, if its participants all exist, 799114402Sru // and it appears as a ligature in the tfm file, include in 800114402Sru // the list of ligatures. 801114402Sru int started = 0; 802114402Sru for (i = 0; i < sizeof(lig_table)/sizeof(lig_table[0]); i++) { 803114402Sru int i1 = lig_chars[lig_table[i].c1].i; 804114402Sru int i2 = lig_chars[lig_table[i].c2].i; 805114402Sru int r = lig_chars[lig_table[i].res].i; 806114402Sru if (i1 >= 0 && i2 >= 0 && r >= 0) { 807114402Sru unsigned char c; 808114402Sru if (t.get_lig(i1, i2, &c) && c == r) { 809114402Sru if (!started) { 810114402Sru started = 1; 811114402Sru fputs("ligatures", stdout); 812114402Sru } 813114402Sru printf(" %s", lig_table[i].ch); 814114402Sru } 815114402Sru } 816114402Sru } 817114402Sru if (started) 818114402Sru fputs(" 0\n", stdout); 819114402Sru printf("checksum %d\n", t.get_checksum()); 820114402Sru printf("designsize %d\n", t.get_design_size()); 821114402Sru // Now print out the kerning information. 822114402Sru int had_kern = 0; 823114402Sru kern_iterator iter(&t); 824114402Sru unsigned char c1, c2; 825114402Sru int k; 826114402Sru while (iter.next(&c1, &c2, &k)) 827114402Sru if (c2 != skewchar) { 828114402Sru k *= MULTIPLIER; 829114402Sru char_list *q = table[c2]; 830114402Sru for (char_list *p1 = table[c1]; p1; p1 = p1->next) 831114402Sru for (char_list *p2 = q; p2; p2 = p2->next) { 832114402Sru if (!had_kern) { 833114402Sru printf("kernpairs\n"); 834114402Sru had_kern = 1; 835114402Sru } 836114402Sru printf("%s %s %d\n", p1->ch, p2->ch, k); 837114402Sru } 838114402Sru } 839114402Sru printf("charset\n"); 840114402Sru char_list unnamed("---"); 841114402Sru for (i = 0; i < 256; i++) 842114402Sru if (t.contains(i)) { 843114402Sru char_list *p = table[i] ? table[i] : &unnamed; 844114402Sru int m[6]; 845114402Sru m[0] = t.get_width(i); 846114402Sru m[1] = t.get_height(i); 847114402Sru m[2] = t.get_depth(i); 848114402Sru m[3] = t.get_italic(i); 849114402Sru m[4] = g.get_left_adjustment(i); 850114402Sru m[5] = g.get_right_adjustment(i); 851114402Sru printf("%s\t%d", p->ch, m[0]*MULTIPLIER); 852114402Sru int j; 853114402Sru for (j = int(sizeof(m)/sizeof(m[0])) - 1; j > 0; j--) 854114402Sru if (m[j] != 0) 855114402Sru break; 856114402Sru for (k = 1; k <= j; k++) 857114402Sru printf(",%d", m[k]*MULTIPLIER); 858114402Sru int type = 0; 859114402Sru if (m[2] > 0) 860114402Sru type = 1; 861114402Sru if (m[1] > xheight) 862114402Sru type += 2; 863114402Sru printf("\t%d\t%04o\n", type, i); 864114402Sru for (p = p->next; p; p = p->next) 865114402Sru printf("%s\t\"\n", p->ch); 866114402Sru } 867114402Sru return 0; 868114402Sru} 869114402Sru 870114402Srustatic void usage(FILE *stream) 871114402Sru{ 872114402Sru fprintf(stream, "usage: %s [-sv] [-g gf_file] [-k skewchar] tfm_file map_file font\n", 873114402Sru program_name); 874114402Sru} 875