1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1994, 2000, 2001, 2003, 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/*
22114402SruTODO
23114402Srudevise new names for useful characters
24114402Sruoption to specify symbol sets to look in
25114402Sruput filename in error messages (or fix lib)
26114402Sru*/
27114402Sru
28114402Sru#include "lib.h"
29114402Sru
30151497Sru#include <stdio.h>
31114402Sru#include <stdlib.h>
32151497Sru#include <string.h>
33151497Sru#include <ctype.h>
34114402Sru#include <math.h>
35114402Sru#include <errno.h>
36114402Sru#include "assert.h"
37114402Sru#include "posix.h"
38114402Sru#include "errarg.h"
39114402Sru#include "error.h"
40114402Sru#include "cset.h"
41114402Sru#include "nonposix.h"
42151497Sru#include "unicode.h"
43114402Sru
44114402Sruextern "C" const char *Version_string;
45151497Sruextern const char *hp_msl_to_unicode_code(const char *);
46114402Sru
47114402Sru#define SIZEOF(v) (sizeof(v)/sizeof(v[0]))
48151497Sru#define equal(a, b) (strcmp(a, b) == 0)
49151497Sru// only valid if is_uname(c) has returned true
50151497Sru#define is_decomposed(c) strchr(c, '_')
51114402Sru
52151497Sru#define NO       0
53151497Sru#define YES      1
54114402Sru
55151497Sru#define MSL      0
56151497Sru#define SYMSET   1
57151497Sru#define UNICODE  2
58151497Sru
59151497Sru#define UNNAMED "---"
60151497Sru
61151497Srustatic double multiplier = 3.0;	// make Agfa-based unitwidth an integer
62151497Sru
63114402Sruinline
64114402Sruint scale(int n)
65114402Sru{
66151497Sru  return int(n * multiplier + 0.5);
67114402Sru}
68114402Sru
69114402Sru// tags in TFM file
70114402Sru
71114402Sruenum tag_type {
72114402Sru  min_tag = 400,
73114402Sru  type_tag = 400,
74151497Sru  copyright_tag = 401,
75151497Sru  comment_tag = 402,
76151497Sru  charcode_tag = 403,		// MSL for Intellifont, Unicode for TrueType
77114402Sru  symbol_set_tag = 404,
78151497Sru  unique_identifier_tag = 405,
79114402Sru  inches_per_point_tag = 406,
80151497Sru  nominal_point_size_tag = 407,
81114402Sru  design_units_per_em_tag = 408,
82114402Sru  posture_tag = 409,
83151497Sru  type_structure_tag = 410,
84114402Sru  stroke_weight_tag = 411,
85114402Sru  spacing_tag = 412,
86114402Sru  slant_tag = 413,
87114402Sru  appearance_width_tag = 414,
88151497Sru  serif_style_tag = 415,
89151497Sru  font_name_tag = 417,
90151497Sru  typeface_source_tag = 418,
91151497Sru  average_width_tag = 419,
92151497Sru  max_width_tag = 420,
93114402Sru  word_spacing_tag = 421,
94151497Sru  recommended_line_spacing_tag = 422,
95151497Sru  cap_height_tag = 423,
96114402Sru  x_height_tag = 424,
97151497Sru  max_ascent_tag = 425,
98151497Sru  max_descent_tag = 426,
99114402Sru  lower_ascent_tag = 427,
100114402Sru  lower_descent_tag = 428,
101151497Sru  underscore_depth_tag = 429,
102151497Sru  underscore_thickness_tag = 430,
103151497Sru  uppercase_accent_height_tag = 431,
104151497Sru  lowercase_accent_height_tag = 432,
105114402Sru  width_tag = 433,
106151497Sru  vertical_escapement_tag = 434,
107114402Sru  left_extent_tag = 435,
108114402Sru  right_extent_tag = 436,
109114402Sru  ascent_tag = 437,
110114402Sru  descent_tag = 438,
111114402Sru  pair_kern_tag = 439,
112151497Sru  sector_kern_tag = 440,
113151497Sru  track_kern_tag = 441,
114114402Sru  typeface_tag = 442,
115151497Sru  panose_tag = 443,
116114402Sru  max_tag = 443
117151497Sru};
118114402Sru
119151497Sruconst char *tag_name[] = {
120151497Sru  "Symbol Set",
121151497Sru  "Font Type"		// MSL for Intellifont, Unicode for TrueType
122151497Sru};
123151497Sru
124114402Sru// types in TFM file
125114402Sruenum {
126151497Sru  BYTE_TYPE = 1,
127151497Sru  ASCII_TYPE = 2,		// NUL-terminated string
128114402Sru  USHORT_TYPE = 3,
129151497Sru  LONG_TYPE = 4,		// unused
130151497Sru  RATIONAL_TYPE = 5,		// 8-byte numerator + 8-byte denominator
131151497Sru  SIGNED_BYTE_TYPE = 16,	// unused
132151497Sru  SIGNED_SHORT_TYPE = 17,
133151497Sru  SIGNED_LONG_TYPE = 18		// unused
134151497Sru};
135114402Sru
136114402Srutypedef unsigned char byte;
137114402Srutypedef unsigned short uint16;
138114402Srutypedef short int16;
139114402Srutypedef unsigned int uint32;
140114402Sru
141114402Sruclass File {
142114402Srupublic:
143114402Sru  File(const char *);
144114402Sru  void skip(int n);
145114402Sru  byte get_byte();
146114402Sru  uint16 get_uint16();
147114402Sru  uint32 get_uint32();
148151497Sru  uint32 get_uint32(char *orig);
149114402Sru  void seek(uint32 n);
150114402Sruprivate:
151114402Sru  unsigned char *buf_;
152114402Sru  const unsigned char *ptr_;
153114402Sru  const unsigned char *end_;
154114402Sru};
155114402Sru
156114402Srustruct entry {
157114402Sru  char present;
158114402Sru  uint16 type;
159114402Sru  uint32 count;
160114402Sru  uint32 value;
161151497Sru  char orig_value[4];
162114402Sru  entry() : present(0) { }
163114402Sru};
164114402Sru
165114402Srustruct char_info {
166151497Sru  uint16 charcode;
167114402Sru  uint16 width;
168114402Sru  int16 ascent;
169114402Sru  int16 descent;
170114402Sru  int16 left_extent;
171114402Sru  uint16 right_extent;
172114402Sru  uint16 symbol_set;
173114402Sru  unsigned char code;
174114402Sru};
175114402Sru
176151497Sruconst uint16 NO_GLYPH = 0xffff;
177114402Sruconst uint16 NO_SYMBOL_SET = 0;
178114402Sru
179114402Srustruct name_list {
180114402Sru  char *name;
181114402Sru  name_list *next;
182114402Sru  name_list(const char *s, name_list *p) : name(strsave(s)), next(p) { }
183114402Sru  ~name_list() { a_delete name; }
184114402Sru};
185114402Sru
186114402Srustruct symbol_set {
187114402Sru  uint16 select;
188114402Sru  uint16 index[256];
189114402Sru};
190114402Sru
191114402Sru#define SYMBOL_SET(n, c) ((n) * 32 + ((c) - 64))
192114402Sru
193114402Sruuint16 text_symbol_sets[] = {
194151497Sru  SYMBOL_SET(19, 'U'),		// Windows Latin 1 ("ANSI", code page 1252)
195151497Sru  SYMBOL_SET(9, 'E'),		// Windows Latin 2, Code Page 1250
196151497Sru  SYMBOL_SET(5, 'T'),		// Code Page 1254
197151497Sru  SYMBOL_SET(7, 'J'),		// Desktop
198114402Sru  SYMBOL_SET(6, 'J'),		// Microsoft Publishing
199151497Sru  SYMBOL_SET(0, 'N'),		// Latin 1 (subset of 19U,
200151497Sru				// so we should never get here)
201151497Sru  SYMBOL_SET(2, 'N'),		// Latin 2 (subset of 9E,
202151497Sru				// so we should never get here)
203151497Sru  SYMBOL_SET(8, 'U'),		// HP Roman 8
204151497Sru  SYMBOL_SET(10, 'J'),		// PS Standard
205151497Sru  SYMBOL_SET(9, 'U'),		// Windows 3.0 "ANSI"
206151497Sru  SYMBOL_SET(1, 'U'),		// U.S. Legal
207151497Sru
208151497Sru  SYMBOL_SET(12, 'J'),		// MC Text
209151497Sru  SYMBOL_SET(10, 'U'),		// PC Code Page 437
210151497Sru  SYMBOL_SET(11, 'U'),		// PC Code Page 437N
211151497Sru  SYMBOL_SET(17, 'U'),		// PC Code Page 852
212151497Sru  SYMBOL_SET(12, 'U'),		// PC Code Page 850
213151497Sru  SYMBOL_SET(9, 'T'),		// PC Code Page 437T
214114402Sru  0
215151497Sru};
216114402Sru
217114402Sruuint16 special_symbol_sets[] = {
218151497Sru  SYMBOL_SET(8, 'M'),		// Math 8
219151497Sru  SYMBOL_SET(5, 'M'),		// PS Math
220151497Sru  SYMBOL_SET(15, 'U'),		// Pi font
221151497Sru  SYMBOL_SET(13, 'J'),		// Ventura International
222151497Sru  SYMBOL_SET(19, 'M'),		// Symbol font
223151497Sru  SYMBOL_SET(579, 'L'),		// Wingdings
224114402Sru  0
225151497Sru};
226114402Sru
227114402Sruentry tags[max_tag + 1 - min_tag];
228114402Sru
229114402Sruchar_info *char_table;
230151497Sruuint32 nchars = 0;
231114402Sru
232151497Sruunsigned int charcode_name_table_size = 0;
233151497Sruname_list **charcode_name_table = NULL;
234114402Sru
235151497Srusymbol_set *symbol_set_table;
236114402Sruunsigned int n_symbol_sets;
237114402Sru
238151497Srustatic int debug_flag = NO;
239151497Srustatic int special_flag = NO;	// not a special font
240151497Srustatic int italic_flag = NO;	// don't add italic correction
241114402Srustatic int italic_sep;
242151497Srustatic int all_flag = NO;	// don't include glyphs not in mapfile
243151497Srustatic int quiet_flag = NO;	// don't suppress warnings about symbols not found
244114402Sru
245151497Srustatic char *hp_msl_to_ucode_name(int);
246151497Srustatic char *unicode_to_ucode_name(int);
247151497Srustatic int is_uname(char *);
248151497Srustatic char *show_symset(unsigned int);
249151497Srustatic void usage(FILE *);
250114402Srustatic void usage();
251114402Srustatic const char *xbasename(const char *);
252114402Srustatic void read_tags(File &);
253151497Srustatic int check_type();
254151497Srustatic void check_units(File &, const int, double *, double *);
255151497Srustatic int read_map(const char *, const int);
256114402Srustatic void require_tag(tag_type);
257151497Srustatic void dump_ascii(File &, tag_type);
258151497Srustatic void dump_tags(File &);
259151497Srustatic void dump_symbol_sets(File &);
260151497Srustatic void dump_symbols(int);
261151497Srustatic void output_font_name(File &);
262114402Srustatic void output_spacewidth();
263114402Srustatic void output_pclweight();
264114402Srustatic void output_pclproportional();
265114402Srustatic void read_and_output_pcltypeface(File &);
266114402Srustatic void output_pclstyle();
267114402Srustatic void output_slant();
268114402Srustatic void output_ligatures();
269114402Srustatic void read_symbol_sets(File &);
270114402Srustatic void read_and_output_kernpairs(File &);
271151497Srustatic void output_charset(const int);
272151497Srustatic void read_char_table(File &);
273114402Sru
274114402Sruinline
275114402Sruentry &tag_info(tag_type t)
276114402Sru{
277114402Sru  return tags[t - min_tag];
278114402Sru}
279114402Sru
280151497Sruint
281151497Srumain(int argc, char **argv)
282114402Sru{
283114402Sru  program_name = argv[0];
284114402Sru
285114402Sru  int opt;
286151497Sru  int res = 1200;		// PCL unit of measure for cursor moves
287151497Sru  int scalesize = 4;		// LaserJet 4 only allows 1/4 point increments
288151497Sru  int unitwidth = 6350;
289151497Sru  double ppi;			// points per inch
290151497Sru  double upem;			// design units per em
291114402Sru
292114402Sru  static const struct option long_options[] = {
293114402Sru    { "help", no_argument, 0, CHAR_MAX + 1 },
294114402Sru    { "version", no_argument, 0, 'v' },
295114402Sru    { NULL, 0, 0, 0 }
296114402Sru  };
297151497Sru  while ((opt = getopt_long(argc, argv, "adsqvi:", long_options, NULL)) != EOF) {
298114402Sru    switch (opt) {
299151497Sru    case 'a':
300151497Sru      all_flag = YES;
301151497Sru      break;
302114402Sru    case 'd':
303151497Sru      debug_flag = YES;
304114402Sru      break;
305114402Sru    case 's':
306151497Sru      special_flag = YES;
307114402Sru      break;
308114402Sru    case 'i':
309151497Sru      italic_flag = YES;
310151497Sru      italic_sep = atoi(optarg);	// design units
311114402Sru      break;
312151497Sru    case 'q':
313151497Sru      quiet_flag = YES;		// suppress warnings about symbols not found
314151497Sru      break;
315114402Sru    case 'v':
316151497Sru      printf("GNU hpftodit (groff) version %s\n", Version_string);
317151497Sru      exit(0);
318114402Sru      break;
319114402Sru    case CHAR_MAX + 1: // --help
320114402Sru      usage(stdout);
321114402Sru      exit(0);
322114402Sru      break;
323114402Sru    case '?':
324114402Sru      usage();
325114402Sru      break;
326114402Sru    default:
327114402Sru      assert(0);
328114402Sru    }
329114402Sru  }
330151497Sru
331151497Sru  if (debug_flag && argc - optind < 1)
332114402Sru    usage();
333151497Sru  else if (!debug_flag && argc - optind != 3)
334151497Sru    usage();
335114402Sru  File f(argv[optind]);
336151497Sru  read_tags(f);
337151497Sru  int tfm_type = check_type();
338151497Sru  if (debug_flag)
339151497Sru    dump_tags(f);
340151497Sru  if (!debug_flag && !read_map(argv[optind + 1], tfm_type))
341114402Sru    exit(1);
342151497Sru  else if (debug_flag && argc - optind > 1)
343151497Sru    read_map(argv[optind + 1], tfm_type);
344151497Sru  current_filename = NULL;
345114402Sru  current_lineno = -1;		// no line numbers
346151497Sru  if (!debug_flag && !equal(argv[optind + 2], "-"))
347151497Sru    if (freopen(argv[optind + 2], "w", stdout) == NULL)
348151497Sru      fatal("cannot open `%1': %2", argv[optind + 2], strerror(errno));
349114402Sru  current_filename = argv[optind];
350151497Sru
351151497Sru  check_units(f, tfm_type, &ppi, &upem);
352151497Sru  if (tfm_type == UNICODE)	// don't calculate for Intellifont TFMs
353151497Sru    multiplier = double(res) / upem / ppi * unitwidth / scalesize;
354151497Sru  if (italic_flag)
355151497Sru    // convert from thousandths of an em to design units
356151497Sru    italic_sep = int(italic_sep * upem / 1000 + 0.5);
357151497Sru
358114402Sru  read_char_table(f);
359151497Sru  if (nchars == 0)
360151497Sru    fatal("no characters");
361151497Sru
362151497Sru  if (!debug_flag) {
363151497Sru    output_font_name(f);
364151497Sru    printf("name %s\n", xbasename(argv[optind + 2]));
365151497Sru    if (special_flag)
366151497Sru      printf("special\n");
367151497Sru    output_spacewidth();
368151497Sru    output_slant();
369151497Sru    read_and_output_pcltypeface(f);
370151497Sru    output_pclproportional();
371151497Sru    output_pclweight();
372151497Sru    output_pclstyle();
373151497Sru  }
374114402Sru  read_symbol_sets(f);
375151497Sru  if (debug_flag)
376151497Sru    dump_symbols(tfm_type);
377151497Sru  else {
378151497Sru    output_ligatures();
379151497Sru    read_and_output_kernpairs(f);
380151497Sru    output_charset(tfm_type);
381151497Sru  }
382114402Sru  return 0;
383114402Sru}
384114402Sru
385151497Srustatic void
386151497Sruusage(FILE *stream)
387114402Sru{
388151497Sru  fprintf(stream,
389151497Sru	  "usage: %s [-s] [-a] [-q] [-i n] tfm_file map_file output_font\n"
390151497Sru	  "       %s -d tfm_file [map_file]\n",
391151497Sru	  program_name, program_name);
392114402Sru}
393151497Sru
394151497Srustatic void
395151497Sruusage()
396114402Sru{
397114402Sru  usage(stderr);
398114402Sru  exit(1);
399114402Sru}
400114402Sru
401114402SruFile::File(const char *s)
402114402Sru{
403114402Sru  // We need to read the file in binary mode because hpftodit relies
404114402Sru  // on byte counts.
405114402Sru  int fd = open(s, O_RDONLY | O_BINARY);
406114402Sru  if (fd < 0)
407114402Sru    fatal("cannot open `%1': %2", s, strerror(errno));
408114402Sru  current_filename = s;
409114402Sru  struct stat sb;
410114402Sru  if (fstat(fd, &sb) < 0)
411114402Sru    fatal("cannot stat: %1", strerror(errno));
412114402Sru  if (!S_ISREG(sb.st_mode))
413114402Sru    fatal("not a regular file");
414114402Sru  buf_ = new unsigned char[sb.st_size];
415114402Sru  long nread = read(fd, buf_, sb.st_size);
416114402Sru  if (nread < 0)
417114402Sru    fatal("read error: %1", strerror(errno));
418114402Sru  if (nread != sb.st_size)
419114402Sru    fatal("read unexpected number of bytes");
420114402Sru  ptr_ = buf_;
421114402Sru  end_ = buf_ + sb.st_size;
422114402Sru}
423114402Sru
424151497Sruvoid
425151497SruFile::skip(int n)
426114402Sru{
427114402Sru  if (end_ - ptr_ < n)
428114402Sru    fatal("unexpected end of file");
429114402Sru  ptr_ += n;
430114402Sru}
431114402Sru
432151497Sruvoid
433151497SruFile::seek(uint32 n)
434114402Sru{
435151497Sru  if (uint32(end_ - buf_) < n)
436114402Sru    fatal("unexpected end of file");
437114402Sru  ptr_ = buf_ + n;
438114402Sru}
439114402Sru
440151497Srubyte
441151497SruFile::get_byte()
442114402Sru{
443114402Sru  if (ptr_ >= end_)
444114402Sru    fatal("unexpected end of file");
445114402Sru  return *ptr_++;
446114402Sru}
447114402Sru
448151497Sruuint16
449151497SruFile::get_uint16()
450114402Sru{
451114402Sru  if (end_ - ptr_ < 2)
452114402Sru    fatal("unexpected end of file");
453114402Sru  uint16 n = *ptr_++;
454114402Sru  return n + (*ptr_++ << 8);
455114402Sru}
456114402Sru
457151497Sruuint32
458151497SruFile::get_uint32()
459114402Sru{
460114402Sru  if (end_ - ptr_ < 4)
461114402Sru    fatal("unexpected end of file");
462114402Sru  uint32 n = *ptr_++;
463114402Sru  for (int i = 0; i < 3; i++)
464114402Sru    n += *ptr_++ << (i + 1)*8;
465114402Sru  return n;
466114402Sru}
467114402Sru
468151497Sruuint32
469151497SruFile::get_uint32(char *orig)
470114402Sru{
471151497Sru  if (end_ - ptr_ < 4)
472151497Sru    fatal("unexpected end of file");
473151497Sru  unsigned char v = *ptr_++;
474151497Sru  uint32 n = v;
475151497Sru  orig[0] = v;
476151497Sru  for (int i = 1; i < 4; i++) {
477151497Sru    v = *ptr_++;
478151497Sru    orig[i] = v;
479151497Sru    n += v << i*8;
480151497Sru  }
481151497Sru  return n;
482151497Sru}
483151497Sru
484151497Srustatic void
485151497Sruread_tags(File &f)
486151497Sru{
487114402Sru  if (f.get_byte() != 'I' || f.get_byte() != 'I')
488114402Sru    fatal("not an Intel format TFM file");
489114402Sru  f.skip(6);
490114402Sru  uint16 ntags = f.get_uint16();
491114402Sru  entry dummy;
492114402Sru  for (uint16 i = 0; i < ntags; i++) {
493114402Sru    uint16 tag = f.get_uint16();
494114402Sru    entry *p;
495114402Sru    if (min_tag <= tag && tag <= max_tag)
496114402Sru      p = tags + (tag - min_tag);
497114402Sru    else
498114402Sru      p = &dummy;
499114402Sru    p->present = 1;
500114402Sru    p->type = f.get_uint16();
501114402Sru    p->count = f.get_uint32();
502151497Sru    p->value = f.get_uint32(p->orig_value);
503114402Sru  }
504114402Sru}
505114402Sru
506151497Srustatic int
507151497Srucheck_type()
508114402Sru{
509114402Sru  require_tag(type_tag);
510151497Sru  int tfm_type = tag_info(type_tag).value;
511151497Sru  switch (tfm_type) {
512151497Sru    case MSL:
513151497Sru    case UNICODE:
514151497Sru      break;
515151497Sru    case SYMSET:
516151497Sru      fatal("cannot handle Symbol Set TFM files");
517151497Sru      break;
518151497Sru    default:
519151497Sru      fatal("unknown type tag %1", tfm_type);
520114402Sru  }
521151497Sru  return tfm_type;
522114402Sru}
523114402Sru
524151497Srustatic void
525151497Srucheck_units(File &f, const int tfm_type, double *ppi, double *upem)
526114402Sru{
527114402Sru  require_tag(design_units_per_em_tag);
528114402Sru  f.seek(tag_info(design_units_per_em_tag).value);
529114402Sru  uint32 num = f.get_uint32();
530114402Sru  uint32 den = f.get_uint32();
531151497Sru  if (tfm_type == MSL && (num != 8782 || den != 1))
532114402Sru    fatal("design units per em != 8782/1");
533151497Sru  *upem = double(num) / den;
534114402Sru  require_tag(inches_per_point_tag);
535114402Sru  f.seek(tag_info(inches_per_point_tag).value);
536114402Sru  num = f.get_uint32();
537114402Sru  den = f.get_uint32();
538151497Sru  if (tfm_type == MSL && (num != 100 || den != 7231))
539114402Sru    fatal("inches per point not 100/7231");
540151497Sru  *ppi = double(den) / num;
541114402Sru}
542114402Sru
543151497Srustatic void
544151497Srurequire_tag(tag_type t)
545114402Sru{
546114402Sru  if (!tag_info(t).present)
547114402Sru    fatal("tag %1 missing", int(t));
548114402Sru}
549114402Sru
550151497Sru// put a human-readable font name in the file
551151497Srustatic void
552151497Sruoutput_font_name(File &f)
553114402Sru{
554151497Sru  char *p;
555151497Sru
556151497Sru  if (!tag_info(font_name_tag).present)
557151497Sru    return;
558151497Sru  int count = tag_info(font_name_tag).count;
559151497Sru  char *font_name = new char[count];
560151497Sru
561151497Sru  if (count > 4) {	// value is a file offset to the string
562151497Sru    f.seek(tag_info(font_name_tag).value);
563151497Sru    int n = count;
564151497Sru    p = font_name;
565151497Sru    while (--n)
566151497Sru      *p++ = f.get_byte();
567151497Sru  }
568151497Sru  else			// orig_value contains the string
569151497Sru    sprintf(font_name, "%.*s",
570151497Sru	    count, tag_info(font_name_tag).orig_value);
571151497Sru
572151497Sru  // remove any trailing space
573151497Sru  p = font_name + count - 1;
574151497Sru  while (csspace(*--p))
575151497Sru    ;
576151497Sru  *(p + 1) = '\0';
577151497Sru  printf("# %s\n", font_name);
578151497Sru  delete font_name;
579151497Sru}
580151497Sru
581151497Srustatic void
582151497Sruoutput_spacewidth()
583151497Sru{
584114402Sru  require_tag(word_spacing_tag);
585114402Sru  printf("spacewidth %d\n", scale(tag_info(word_spacing_tag).value));
586114402Sru}
587114402Sru
588151497Srustatic void
589151497Sruread_symbol_sets(File &f)
590114402Sru{
591114402Sru  uint32 symbol_set_dir_length = tag_info(symbol_set_tag).count;
592151497Sru  uint16 *symbol_set_selectors;
593114402Sru  n_symbol_sets = symbol_set_dir_length/14;
594114402Sru  symbol_set_table = new symbol_set[n_symbol_sets];
595114402Sru  unsigned int i;
596151497Sru
597151497Sru  for (i = 0; i < nchars; i++)
598151497Sru    char_table[i].symbol_set = NO_SYMBOL_SET;
599151497Sru
600114402Sru  for (i = 0; i < n_symbol_sets; i++) {
601114402Sru    f.seek(tag_info(symbol_set_tag).value + i*14);
602151497Sru    (void)f.get_uint32();		// offset to symbol set name
603151497Sru    uint32 off1 = f.get_uint32();	// offset to selection string
604151497Sru    uint32 off2 = f.get_uint32();	// offset to symbol set index array
605151497Sru
606114402Sru    f.seek(off1);
607151497Sru    uint16 kind = 0;			// HP-GL "Kind 1" symbol set value
608114402Sru    unsigned int j;
609114402Sru    for (j = 0; j < off2 - off1; j++) {
610114402Sru      unsigned char c = f.get_byte();
611151497Sru      if ('0' <= c && c <= '9')		// value
612114402Sru	kind = kind*10 + (c - '0');
613151497Sru      else if ('A' <= c && c <= 'Z')	// terminator
614114402Sru	kind = kind*32 + (c - 64);
615114402Sru    }
616114402Sru    symbol_set_table[i].select = kind;
617114402Sru    for (j = 0; j < 256; j++)
618114402Sru      symbol_set_table[i].index[j] = f.get_uint16();
619114402Sru  }
620114402Sru
621151497Sru  symbol_set_selectors = (special_flag ? special_symbol_sets
622151497Sru				       : text_symbol_sets);
623114402Sru  for (i = 0; symbol_set_selectors[i] != 0; i++) {
624114402Sru    unsigned int j;
625114402Sru    for (j = 0; j < n_symbol_sets; j++)
626114402Sru      if (symbol_set_table[j].select == symbol_set_selectors[i])
627114402Sru	break;
628114402Sru    if (j < n_symbol_sets) {
629114402Sru      for (int k = 0; k < 256; k++) {
630151497Sru	uint16 idx = symbol_set_table[j].index[k];
631151497Sru	if (idx != NO_GLYPH
632151497Sru	    && char_table[idx].symbol_set == NO_SYMBOL_SET) {
633151497Sru	  char_table[idx].symbol_set = symbol_set_table[j].select;
634151497Sru	  char_table[idx].code = k;
635114402Sru	}
636114402Sru      }
637114402Sru    }
638114402Sru  }
639151497Sru
640151497Sru  if (all_flag)
641151497Sru    return;
642151497Sru
643151497Sru  symbol_set_selectors = (special_flag ? text_symbol_sets
644151497Sru				       : special_symbol_sets);
645151497Sru  for (i = 0; symbol_set_selectors[i] != 0; i++) {
646151497Sru    unsigned int j;
647151497Sru    for (j = 0; j < n_symbol_sets; j++)
648151497Sru      if (symbol_set_table[j].select == symbol_set_selectors[i])
649151497Sru	break;
650151497Sru    if (j < n_symbol_sets) {
651151497Sru      for (int k = 0; k < 256; k++) {
652151497Sru	uint16 idx = symbol_set_table[j].index[k];
653151497Sru	if (idx != NO_GLYPH
654151497Sru	    && char_table[idx].symbol_set == NO_SYMBOL_SET) {
655151497Sru	  char_table[idx].symbol_set = symbol_set_table[j].select;
656151497Sru	  char_table[idx].code = k;
657151497Sru	}
658151497Sru      }
659151497Sru    }
660151497Sru  }
661151497Sru  return;
662114402Sru}
663114402Sru
664151497Srustatic void
665151497Sruread_char_table(File &f)
666114402Sru{
667151497Sru  require_tag(charcode_tag);
668151497Sru  nchars = tag_info(charcode_tag).count;
669114402Sru  char_table = new char_info[nchars];
670114402Sru
671151497Sru  f.seek(tag_info(charcode_tag).value);
672114402Sru  uint32 i;
673114402Sru  for (i = 0; i < nchars; i++)
674151497Sru    char_table[i].charcode = f.get_uint16();
675151497Sru
676114402Sru  require_tag(width_tag);
677114402Sru  f.seek(tag_info(width_tag).value);
678114402Sru  for (i = 0; i < nchars; i++)
679114402Sru    char_table[i].width = f.get_uint16();
680114402Sru
681114402Sru  require_tag(ascent_tag);
682114402Sru  f.seek(tag_info(ascent_tag).value);
683114402Sru  for (i = 0; i < nchars; i++) {
684114402Sru    char_table[i].ascent = f.get_uint16();
685114402Sru    if (char_table[i].ascent < 0)
686114402Sru      char_table[i].ascent = 0;
687114402Sru  }
688114402Sru
689114402Sru  require_tag(descent_tag);
690114402Sru  f.seek(tag_info(descent_tag).value);
691114402Sru  for (i = 0; i < nchars; i++) {
692114402Sru    char_table[i].descent = f.get_uint16();
693114402Sru    if (char_table[i].descent > 0)
694114402Sru      char_table[i].descent = 0;
695114402Sru  }
696114402Sru
697114402Sru  require_tag(left_extent_tag);
698114402Sru  f.seek(tag_info(left_extent_tag).value);
699114402Sru  for (i = 0; i < nchars; i++)
700114402Sru    char_table[i].left_extent = int16(f.get_uint16());
701114402Sru
702114402Sru  require_tag(right_extent_tag);
703114402Sru  f.seek(tag_info(right_extent_tag).value);
704114402Sru  for (i = 0; i < nchars; i++)
705114402Sru    char_table[i].right_extent = f.get_uint16();
706114402Sru}
707114402Sru
708151497Srustatic void
709151497Sruoutput_pclweight()
710114402Sru{
711114402Sru  require_tag(stroke_weight_tag);
712114402Sru  int stroke_weight = tag_info(stroke_weight_tag).value;
713114402Sru  int pcl_stroke_weight;
714114402Sru  if (stroke_weight < 128)
715114402Sru    pcl_stroke_weight = -3;
716114402Sru  else if (stroke_weight == 128)
717114402Sru    pcl_stroke_weight = 0;
718114402Sru  else if (stroke_weight <= 145)
719114402Sru    pcl_stroke_weight = 1;
720114402Sru  else if (stroke_weight <= 179)
721114402Sru    pcl_stroke_weight = 3;
722114402Sru  else
723114402Sru    pcl_stroke_weight = 4;
724114402Sru  printf("pclweight %d\n", pcl_stroke_weight);
725114402Sru}
726114402Sru
727151497Srustatic void
728151497Sruoutput_pclproportional()
729114402Sru{
730114402Sru  require_tag(spacing_tag);
731114402Sru  printf("pclproportional %d\n", tag_info(spacing_tag).value == 0);
732114402Sru}
733114402Sru
734151497Srustatic void
735151497Sruread_and_output_pcltypeface(File &f)
736114402Sru{
737114402Sru  printf("pcltypeface ");
738114402Sru  require_tag(typeface_tag);
739151497Sru  if (tag_info(typeface_tag).count > 4) {
740151497Sru    f.seek(tag_info(typeface_tag).value);
741151497Sru    for (uint32 i = 0; i < tag_info(typeface_tag).count; i++) {
742151497Sru      unsigned char c = f.get_byte();
743151497Sru      if (c == '\0')
744151497Sru	break;
745151497Sru      putchar(c);
746151497Sru    }
747114402Sru  }
748151497Sru  else
749151497Sru    printf("%.4s", tag_info(typeface_tag).orig_value);
750114402Sru  printf("\n");
751114402Sru}
752114402Sru
753151497Srustatic void
754151497Sruoutput_pclstyle()
755114402Sru{
756114402Sru  unsigned pcl_style = 0;
757114402Sru  // older tfms don't have the posture tag
758114402Sru  if (tag_info(posture_tag).present) {
759114402Sru    if (tag_info(posture_tag).value)
760114402Sru      pcl_style |= 1;
761114402Sru  }
762114402Sru  else {
763114402Sru    require_tag(slant_tag);
764114402Sru    if (tag_info(slant_tag).value != 0)
765114402Sru      pcl_style |= 1;
766114402Sru  }
767114402Sru  require_tag(appearance_width_tag);
768114402Sru  if (tag_info(appearance_width_tag).value < 100) // guess
769114402Sru    pcl_style |= 4;
770114402Sru  printf("pclstyle %d\n", pcl_style);
771114402Sru}
772114402Sru
773151497Srustatic void
774151497Sruoutput_slant()
775114402Sru{
776114402Sru  require_tag(slant_tag);
777114402Sru  int slant = int16(tag_info(slant_tag).value);
778114402Sru  if (slant != 0)
779114402Sru    printf("slant %f\n", slant/100.0);
780114402Sru}
781114402Sru
782151497Srustatic void
783151497Sruoutput_ligatures()
784114402Sru{
785114402Sru  // don't use ligatures for fixed space font
786114402Sru  require_tag(spacing_tag);
787114402Sru  if (tag_info(spacing_tag).value != 0)
788114402Sru    return;
789114402Sru  static const char *ligature_names[] = {
790114402Sru    "fi", "fl", "ff", "ffi", "ffl"
791114402Sru    };
792114402Sru
793114402Sru  static const char *ligature_chars[] = {
794114402Sru    "fi", "fl", "ff", "Fi", "Fl"
795114402Sru    };
796151497Sru
797114402Sru  unsigned ligature_mask = 0;
798114402Sru  unsigned int i;
799114402Sru  for (i = 0; i < nchars; i++) {
800151497Sru    uint16 charcode = char_table[i].charcode;
801151497Sru    if (charcode < charcode_name_table_size
802114402Sru	&& char_table[i].symbol_set != NO_SYMBOL_SET) {
803151497Sru      for (name_list *p = charcode_name_table[charcode]; p; p = p->next)
804114402Sru	for (unsigned int j = 0; j < SIZEOF(ligature_chars); j++)
805114402Sru	  if (strcmp(p->name, ligature_chars[j]) == 0) {
806114402Sru	    ligature_mask |= 1 << j;
807114402Sru	    break;
808114402Sru	  }
809114402Sru      }
810114402Sru    }
811114402Sru  if (ligature_mask) {
812114402Sru    printf("ligatures");
813114402Sru    for (i = 0; i < SIZEOF(ligature_names); i++)
814114402Sru      if (ligature_mask & (1 << i))
815114402Sru	printf(" %s", ligature_names[i]);
816114402Sru    printf(" 0\n");
817114402Sru  }
818114402Sru}
819114402Sru
820151497Srustatic void
821151497Sruread_and_output_kernpairs(File &f)
822114402Sru{
823114402Sru  if (tag_info(pair_kern_tag).present) {
824114402Sru    printf("kernpairs\n");
825114402Sru    f.seek(tag_info(pair_kern_tag).value);
826114402Sru    uint16 n_pairs = f.get_uint16();
827114402Sru    for (int i = 0; i < n_pairs; i++) {
828114402Sru      uint16 i1 = f.get_uint16();
829114402Sru      uint16 i2 = f.get_uint16();
830114402Sru      int16 val = int16(f.get_uint16());
831114402Sru      if (char_table[i1].symbol_set != NO_SYMBOL_SET
832114402Sru	  && char_table[i2].symbol_set != NO_SYMBOL_SET
833151497Sru	  && char_table[i1].charcode < charcode_name_table_size
834151497Sru	  && char_table[i2].charcode < charcode_name_table_size) {
835151497Sru	for (name_list *p = charcode_name_table[char_table[i1].charcode];
836114402Sru	     p;
837114402Sru	     p = p->next)
838151497Sru	  for (name_list *q = charcode_name_table[char_table[i2].charcode];
839114402Sru	       q;
840114402Sru	       q = q->next)
841151497Sru	    if (!equal(p->name, UNNAMED) && !equal(q->name, UNNAMED))
842151497Sru		printf("%s %s %d\n", p->name, q->name, scale(val));
843114402Sru      }
844114402Sru    }
845114402Sru  }
846114402Sru}
847114402Sru
848151497Srustatic void
849151497Sruoutput_charset(const int tfm_type)
850114402Sru{
851114402Sru  require_tag(slant_tag);
852114402Sru  double slant_angle = int16(tag_info(slant_tag).value)*PI/18000.0;
853114402Sru  double slant = sin(slant_angle)/cos(slant_angle);
854114402Sru
855151497Sru  if (italic_flag)
856151497Sru    require_tag(x_height_tag);
857114402Sru  require_tag(lower_ascent_tag);
858114402Sru  require_tag(lower_descent_tag);
859114402Sru
860114402Sru  printf("charset\n");
861114402Sru  unsigned int i;
862114402Sru  for (i = 0; i < nchars; i++) {
863151497Sru    uint16 charcode = char_table[i].charcode;
864151497Sru
865151497Sru    // the glyph is bound to one of the searched symbol sets
866151497Sru    if (char_table[i].symbol_set != NO_SYMBOL_SET) {
867151497Sru      // the character was in the map file
868151497Sru      if (charcode < charcode_name_table_size && charcode_name_table[charcode])
869151497Sru	printf("%s", charcode_name_table[charcode]->name);
870151497Sru      else if (!all_flag)
871151497Sru	continue;
872151497Sru      else if (tfm_type == MSL)
873222083Sbenl	fputs(hp_msl_to_ucode_name(charcode), stdout);
874151497Sru      else
875222083Sbenl	fputs(unicode_to_ucode_name(charcode), stdout);
876151497Sru
877151497Sru      printf("\t%d,%d",
878151497Sru	     scale(char_table[i].width), scale(char_table[i].ascent));
879151497Sru
880151497Sru      int depth = scale(-char_table[i].descent);
881151497Sru      if (depth < 0)
882151497Sru	depth = 0;
883151497Sru      int italic_correction = 0;
884151497Sru      int left_italic_correction = 0;
885151497Sru      int subscript_correction = 0;
886151497Sru
887151497Sru      if (italic_flag) {
888151497Sru	italic_correction = scale(char_table[i].right_extent
889151497Sru				  - char_table[i].width
890151497Sru				  + italic_sep);
891151497Sru	if (italic_correction < 0)
892151497Sru	  italic_correction = 0;
893151497Sru	subscript_correction = int((tag_info(x_height_tag).value
894151497Sru				    * slant * .8) + .5);
895151497Sru	if (subscript_correction > italic_correction)
896151497Sru	  subscript_correction = italic_correction;
897151497Sru	left_italic_correction = scale(italic_sep
898151497Sru				       - char_table[i].left_extent);
899114402Sru      }
900151497Sru
901151497Sru      if (subscript_correction != 0)
902151497Sru	printf(",%d,%d,%d,%d",
903151497Sru	       depth, italic_correction, left_italic_correction,
904151497Sru	       subscript_correction);
905151497Sru      else if (left_italic_correction != 0)
906151497Sru	printf(",%d,%d,%d", depth, italic_correction, left_italic_correction);
907151497Sru      else if (italic_correction != 0)
908151497Sru	printf(",%d,%d", depth, italic_correction);
909151497Sru      else if (depth != 0)
910151497Sru	printf(",%d", depth);
911151497Sru      // This is fairly arbitrary.  Fortunately it doesn't much matter.
912151497Sru      unsigned type = 0;
913151497Sru      if (char_table[i].ascent > int16(tag_info(lower_ascent_tag).value)*9/10)
914151497Sru	type |= 2;
915151497Sru      if (char_table[i].descent < int16(tag_info(lower_descent_tag).value)*9/10)
916151497Sru	type |= 1;
917151497Sru      printf("\t%d\t%d", type,
918151497Sru	     char_table[i].symbol_set*256 + char_table[i].code);
919151497Sru
920151497Sru      if (tfm_type == UNICODE) {
921151497Sru	if (charcode >= 0xE000 && charcode <= 0xF8FF)
922151497Sru	  printf("\t-- HP PUA U+%04X", charcode);
923151497Sru	else
924151497Sru	  printf("\t-- U+%04X", charcode);
925151497Sru      }
926114402Sru      else
927151497Sru	printf("\t-- MSL %4d", charcode);
928151497Sru      printf(" (%3s %3d)\n",
929151497Sru	     show_symset(char_table[i].symbol_set), char_table[i].code);
930151497Sru
931151497Sru      if (charcode < charcode_name_table_size
932151497Sru	  && charcode_name_table[charcode])
933151497Sru	for (name_list *p = charcode_name_table[charcode]->next;
934151497Sru	     p; p = p->next)
935151497Sru	  printf("%s\t\"\n", p->name);
936114402Sru    }
937151497Sru    // warnings about characters in mapfile not found in TFM
938151497Sru    else if (charcode < charcode_name_table_size
939151497Sru	     && charcode_name_table[charcode]) {
940151497Sru      char *name = charcode_name_table[charcode]->name;
941151497Sru      // don't warn about Unicode or unnamed glyphs
942151497Sru      //  that aren't in the the TFM file
943151497Sru      if (tfm_type == UNICODE && !quiet_flag && !equal(name, UNNAMED)
944151497Sru	  && !is_uname(name)) {
945151497Sru	fprintf(stderr, "%s: warning: symbol U+%04X (%s",
946151497Sru		program_name, charcode, name);
947151497Sru	for (name_list *p = charcode_name_table[charcode]->next;
948151497Sru	     p; p = p->next)
949151497Sru	  fprintf(stderr, ", %s", p->name);
950151497Sru	fprintf(stderr, ") not in any searched symbol set\n");
951151497Sru      }
952151497Sru      else if (!quiet_flag && !equal(name, UNNAMED) && !is_uname(name)) {
953151497Sru	fprintf(stderr, "%s: warning: symbol MSL %d (%s",
954151497Sru		program_name, charcode, name);
955151497Sru	for (name_list *p = charcode_name_table[charcode]->next;
956151497Sru	     p; p = p->next)
957151497Sru	  fprintf(stderr, ", %s", p->name);
958151497Sru	fprintf(stderr, ") not in any searched symbol set\n");
959151497Sru      }
960151497Sru    }
961114402Sru  }
962114402Sru}
963114402Sru
964151497Sru#define em_fract(a) (upem >= 0 ? double(a)/upem : 0)
965151497Sru
966151497Srustatic void
967151497Srudump_tags(File &f)
968114402Sru{
969151497Sru  double upem = -1.0;
970151497Sru
971151497Sru  printf("TFM tags\n"
972151497Sru	 "\n"
973151497Sru	 "tag# type count value\n"
974151497Sru	 "---------------------\n");
975151497Sru
976151497Sru  for (int i = min_tag; i <= max_tag; i++) {
977114402Sru    enum tag_type t = tag_type(i);
978114402Sru    if (tag_info(t).present) {
979151497Sru      printf("%4d %4d %5d", i, tag_info(t).type, tag_info(t).count);
980151497Sru      switch (tag_info(t).type) {
981151497Sru      case BYTE_TYPE:
982151497Sru      case USHORT_TYPE:
983151497Sru	printf(" %5u", tag_info(t).value);
984151497Sru	switch (i) {
985151497Sru	case type_tag:
986151497Sru	  printf(" Font Type ");
987151497Sru	  switch (tag_info(t).value) {
988151497Sru	  case MSL:
989151497Sru	  case SYMSET:
990151497Sru	    printf("(Intellifont)");
991151497Sru	    break;
992151497Sru	  case UNICODE:
993151497Sru	    printf("(TrueType)");
994151497Sru	  }
995151497Sru	  break;
996151497Sru	case charcode_tag:
997151497Sru	  printf(" Number of Symbols (%u)", tag_info(t).count);
998151497Sru	  break;
999151497Sru	case symbol_set_tag:
1000151497Sru	  printf(" Symbol Sets (%u): ",
1001151497Sru		 tag_info(symbol_set_tag).count / 14);
1002151497Sru	  dump_symbol_sets(f);
1003151497Sru	  break;
1004151497Sru	case type_structure_tag:
1005151497Sru	  printf(" Type Structure (%u)", tag_info(t).value);
1006151497Sru	  break;
1007151497Sru	case stroke_weight_tag:
1008151497Sru	  printf(" Stroke Weight (%u)", tag_info(t).value);
1009151497Sru	  break;
1010151497Sru	case spacing_tag:
1011151497Sru	  printf(" Spacing ");
1012151497Sru	  switch (tag_info(t).value) {
1013151497Sru	  case 0:
1014151497Sru	    printf("(Proportional)");
1015151497Sru	    break;
1016151497Sru	  case 1:
1017151497Sru	    printf("(Fixed Pitch: %u DU: %.2f em)", tag_info(t).value,
1018151497Sru		   em_fract(tag_info(t).value));
1019151497Sru	    break;
1020151497Sru	  }
1021151497Sru	  break;
1022151497Sru	case appearance_width_tag:
1023151497Sru	  printf(" Appearance Width (%u)", tag_info(t).value);
1024151497Sru	  break;
1025151497Sru	case serif_style_tag:
1026151497Sru	  printf(" Serif Style (%u)", tag_info(t).value);
1027151497Sru	  break;
1028151497Sru	case posture_tag:
1029151497Sru	  printf(" Posture (%s)", tag_info(t).value == 0
1030151497Sru				  ? "Upright"
1031151497Sru				  : tag_info(t).value == 1
1032151497Sru				    ? "Italic"
1033151497Sru				    : "Alternate Italic");
1034151497Sru	  break;
1035151497Sru	case max_width_tag:
1036151497Sru	  printf(" Maximum Width (%u DU: %.2f em)", tag_info(t).value,
1037151497Sru		 em_fract(tag_info(t).value));
1038151497Sru	  break;
1039151497Sru	case word_spacing_tag:
1040151497Sru	  printf(" Interword Spacing (%u DU: %.2f em)", tag_info(t).value,
1041151497Sru		 em_fract(tag_info(t).value));
1042151497Sru	  break;
1043151497Sru	case recommended_line_spacing_tag:
1044151497Sru	  printf(" Recommended Line Spacing (%u DU: %.2f em)", tag_info(t).value,
1045151497Sru		 em_fract(tag_info(t).value));
1046151497Sru	  break;
1047151497Sru	case x_height_tag:
1048151497Sru	  printf(" x-Height (%u DU: %.2f em)", tag_info(t).value,
1049151497Sru		 em_fract(tag_info(t).value));
1050151497Sru	  break;
1051151497Sru	case cap_height_tag:
1052151497Sru	  printf(" Cap Height (%u DU: %.2f em)", tag_info(t).value,
1053151497Sru		 em_fract(tag_info(t).value));
1054151497Sru	  break;
1055151497Sru	case max_ascent_tag:
1056151497Sru	  printf(" Maximum Ascent (%u DU: %.2f em)", tag_info(t).value,
1057151497Sru		 em_fract(tag_info(t).value));
1058151497Sru	  break;
1059151497Sru	case lower_ascent_tag:
1060151497Sru	  printf(" Lowercase Ascent (%u DU: %.2f em)", tag_info(t).value,
1061151497Sru		 em_fract(tag_info(t).value));
1062151497Sru	  break;
1063151497Sru	case underscore_thickness_tag:
1064151497Sru	  printf(" Underscore Thickness (%u DU: %.2f em)", tag_info(t).value,
1065151497Sru		 em_fract(tag_info(t).value));
1066151497Sru	  break;
1067151497Sru	case uppercase_accent_height_tag:
1068151497Sru	  printf(" Uppercase Accent Height (%u DU: %.2f em)", tag_info(t).value,
1069151497Sru		 em_fract(tag_info(t).value));
1070151497Sru	  break;
1071151497Sru	case lowercase_accent_height_tag:
1072151497Sru	  printf(" Lowercase Accent Height (%u DU: %.2f em)", tag_info(t).value,
1073151497Sru		 em_fract(tag_info(t).value));
1074151497Sru	  break;
1075151497Sru	case width_tag:
1076151497Sru	  printf(" Horizontal Escapement array");
1077151497Sru	  break;
1078151497Sru	case vertical_escapement_tag:
1079151497Sru	  printf(" Vertical Escapement array");
1080151497Sru	  break;
1081151497Sru	case right_extent_tag:
1082151497Sru	  printf(" Right Extent array");
1083151497Sru	  break;
1084151497Sru	case ascent_tag:
1085151497Sru	  printf(" Character Ascent array");
1086151497Sru	  break;
1087151497Sru	case pair_kern_tag:
1088151497Sru	  f.seek(tag_info(t).value);
1089151497Sru	  printf(" Kern Pairs (%u)", f.get_uint16());
1090151497Sru	  break;
1091151497Sru	case panose_tag:
1092151497Sru	  printf(" PANOSE Classification array");
1093151497Sru	  break;
1094151497Sru	}
1095151497Sru	break;
1096151497Sru      case SIGNED_SHORT_TYPE:
1097151497Sru	printf(" %5d", int16(tag_info(t).value));
1098151497Sru	switch (i) {
1099151497Sru	case slant_tag:
1100151497Sru	  printf(" Slant (%.2f degrees)", double(tag_info(t).value) / 100);
1101151497Sru	  break;
1102151497Sru	case max_descent_tag:
1103151497Sru	  printf(" Maximum Descent (%d DU: %.2f em)", int16(tag_info(t).value),
1104151497Sru		 em_fract(int16(tag_info(t).value)));
1105151497Sru	  break;
1106151497Sru	case lower_descent_tag:
1107151497Sru	  printf(" Lowercase Descent (%d DU: %.2f em)", int16(tag_info(t).value),
1108151497Sru		 em_fract(int16(tag_info(t).value)));
1109151497Sru	  break;
1110151497Sru	case underscore_depth_tag:
1111151497Sru	  printf(" Underscore Depth (%d DU: %.2f em)", int16(tag_info(t).value),
1112151497Sru		 em_fract(int16(tag_info(t).value)));
1113151497Sru	  break;
1114151497Sru	case left_extent_tag:
1115151497Sru	  printf(" Left Extent array");
1116151497Sru	  break;
1117151497Sru	// The type of this tag has changed from SHORT to SIGNED SHORT
1118151497Sru	// in TFM version 1.3.0.
1119151497Sru	case ascent_tag:
1120151497Sru	  printf(" Character Ascent array");
1121151497Sru	  break;
1122151497Sru	case descent_tag:
1123151497Sru	  printf(" Character Descent array");
1124151497Sru	  break;
1125151497Sru	}
1126151497Sru	break;
1127151497Sru      case RATIONAL_TYPE:
1128151497Sru	printf(" %5u", tag_info(t).value);
1129151497Sru	switch (i) {
1130151497Sru	case inches_per_point_tag:
1131151497Sru	  printf(" Inches per Point");
1132151497Sru	  break;
1133151497Sru	case nominal_point_size_tag:
1134151497Sru	  printf(" Nominal Point Size");
1135151497Sru	  break;
1136151497Sru	case design_units_per_em_tag:
1137151497Sru	  printf(" Design Units per Em");
1138151497Sru	  break;
1139151497Sru	case average_width_tag:
1140151497Sru	  printf(" Average Width");
1141151497Sru	  break;
1142151497Sru	}
1143151497Sru	if (tag_info(t).count == 1) {
1144151497Sru	  f.seek(tag_info(t).value);
1145151497Sru	  uint32 num = f.get_uint32();
1146151497Sru	  uint32 den = f.get_uint32();
1147151497Sru	  if (i == design_units_per_em_tag)
1148151497Sru	    upem = double(num) / den;
1149151497Sru	  printf(" (%u/%u = %g)", num, den, double(num)/den);
1150151497Sru	}
1151151497Sru	break;
1152151497Sru      case ASCII_TYPE:
1153151497Sru	printf(" %5u ", tag_info(t).value);
1154151497Sru	switch (i) {
1155151497Sru	case comment_tag:
1156151497Sru	  printf("Comment ");
1157151497Sru	  break;
1158151497Sru	case copyright_tag:
1159151497Sru	  printf("Copyright ");
1160151497Sru	  break;
1161151497Sru	case unique_identifier_tag:
1162151497Sru	  printf("Unique ID ");
1163151497Sru	  break;
1164151497Sru	case font_name_tag:
1165151497Sru	  printf("Typeface Name ");
1166151497Sru	  break;
1167151497Sru	case typeface_source_tag:
1168151497Sru	  printf("Typeface Source ");
1169151497Sru	  break;
1170151497Sru	case typeface_tag:
1171151497Sru	  printf("PCL Typeface ");
1172151497Sru	  break;
1173151497Sru	}
1174151497Sru	dump_ascii(f, t);
1175114402Sru      }
1176151497Sru      putchar('\n');
1177114402Sru    }
1178114402Sru  }
1179151497Sru  putchar('\n');
1180114402Sru}
1181151497Sru#undef em_fract
1182114402Sru
1183151497Srustatic void
1184151497Srudump_ascii(File &f, tag_type t)
1185114402Sru{
1186151497Sru  putchar('"');
1187151497Sru  if (tag_info(t).count > 4) {
1188151497Sru    int count = tag_info(t).count;
1189151497Sru    f.seek(tag_info(t).value);
1190151497Sru    while (--count)
1191151497Sru      printf("%c", f.get_byte());
1192151497Sru  }
1193151497Sru  else
1194151497Sru    printf("%.4s", tag_info(t).orig_value);
1195151497Sru  putchar('"');
1196151497Sru}
1197151497Sru
1198151497Srustatic void
1199151497Srudump_symbol_sets(File &f)
1200151497Sru{
1201151497Sru  uint32 symbol_set_dir_length = tag_info(symbol_set_tag).count;
1202151497Sru  uint32 num_symbol_sets = symbol_set_dir_length / 14;
1203151497Sru
1204151497Sru  for (uint32 i = 0; i < num_symbol_sets; i++) {
1205151497Sru    f.seek(tag_info(symbol_set_tag).value + i * 14);
1206151497Sru    (void)f.get_uint32();		// offset to symbol set name
1207151497Sru    uint32 off1 = f.get_uint32();	// offset to selection string
1208151497Sru    uint32 off2 = f.get_uint32();	// offset to symbol set index array
1209151497Sru    f.seek(off1);
1210151497Sru    for (uint32 j = 0; j < off2 - off1; j++) {
1211151497Sru      unsigned char c = f.get_byte();
1212151497Sru      if ('0' <= c && c <= '9')
1213151497Sru	putchar(c);
1214151497Sru      else if ('A' <= c && c <= 'Z')
1215151497Sru	printf(i < num_symbol_sets - 1 ? "%c," : "%c", c);
1216151497Sru    }
1217151497Sru  }
1218151497Sru}
1219151497Sru
1220151497Srustatic void
1221151497Srudump_symbols(int tfm_type)
1222151497Sru{
1223151497Sru  printf("Symbols:\n"
1224151497Sru	 "\n"
1225151497Sru	 " glyph id#     symbol set  name(s)\n"
1226151497Sru	 "----------------------------------\n");
1227151497Sru  for (uint32 i = 0; i < nchars; i++) {
1228151497Sru    uint16 charcode = char_table[i].charcode;
1229151497Sru    if (charcode < charcode_name_table_size
1230151497Sru	&& charcode_name_table[charcode]) {
1231151497Sru      if (char_table[i].symbol_set != NO_SYMBOL_SET) {
1232151497Sru	printf(tfm_type == UNICODE ? "%4d (U+%04X)   (%3s %3d)  %s"
1233151497Sru				   : "%4d (MSL %4d) (%3s %3d)  %s",
1234151497Sru	       i, charcode,
1235151497Sru	       show_symset(char_table[i].symbol_set),
1236151497Sru	       char_table[i].code,
1237151497Sru	       charcode_name_table[charcode]->name);
1238151497Sru	for (name_list *p = charcode_name_table[charcode]->next;
1239151497Sru	      p; p = p->next)
1240151497Sru	  printf(", %s", p->name);
1241151497Sru	putchar('\n');
1242151497Sru      }
1243151497Sru    }
1244151497Sru    else {
1245151497Sru      printf(tfm_type == UNICODE ? "%4d (U+%04X)   "
1246151497Sru				 : "%4d (MSL %4d) ",
1247151497Sru	     i, charcode);
1248151497Sru      if (char_table[i].symbol_set != NO_SYMBOL_SET)
1249151497Sru	printf("(%3s %3d)",
1250151497Sru	       show_symset(char_table[i].symbol_set), char_table[i].code);
1251151497Sru      putchar('\n');
1252151497Sru    }
1253151497Sru  }
1254151497Sru  putchar('\n');
1255151497Sru}
1256151497Sru
1257151497Srustatic char *
1258151497Srushow_symset(unsigned int symset)
1259151497Sru{
1260151497Sru   static char symset_str[8];
1261151497Sru
1262151497Sru   sprintf(symset_str, "%d%c", symset / 32, (symset & 31) + 64);
1263151497Sru   return symset_str;
1264151497Sru}
1265151497Sru
1266151497Srustatic char *
1267151497Sruhp_msl_to_ucode_name(int msl)
1268151497Sru{
1269151497Sru  char codestr[8];
1270151497Sru
1271151497Sru  sprintf(codestr, "%d", msl);
1272151497Sru  const char *ustr = hp_msl_to_unicode_code(codestr);
1273151497Sru  if (ustr == NULL)
1274151497Sru    ustr = UNNAMED;
1275151497Sru  else {
1276151497Sru    char *nonum;
1277151497Sru    int ucode = int(strtol(ustr, &nonum, 16));
1278151497Sru    // don't allow PUA code points as Unicode names
1279151497Sru    if (ucode >= 0xE000 && ucode <= 0xF8FF)
1280151497Sru      ustr = UNNAMED;
1281151497Sru  }
1282151497Sru  if (!equal(ustr, UNNAMED)) {
1283151497Sru    const char *uname_decomposed = decompose_unicode(ustr);
1284151497Sru    if (uname_decomposed)
1285151497Sru      // 1st char is the number of components
1286151497Sru      ustr = uname_decomposed + 1;
1287151497Sru  }
1288151497Sru  char *value = new char[strlen(ustr) + 1];
1289151497Sru  sprintf(value, equal(ustr, UNNAMED) ? ustr : "u%s", ustr);
1290151497Sru  return value;
1291151497Sru}
1292151497Sru
1293151497Srustatic char *
1294151497Sruunicode_to_ucode_name(int ucode)
1295151497Sru{
1296151497Sru  const char *ustr;
1297151497Sru  char codestr[8];
1298151497Sru
1299151497Sru  // don't allow PUA code points as Unicode names
1300151497Sru  if (ucode >= 0xE000 && ucode <= 0xF8FF)
1301151497Sru    ustr = UNNAMED;
1302151497Sru  else {
1303151497Sru    sprintf(codestr, "%04X", ucode);
1304151497Sru    ustr = codestr;
1305151497Sru  }
1306151497Sru  if (!equal(ustr, UNNAMED)) {
1307151497Sru    const char *uname_decomposed = decompose_unicode(ustr);
1308151497Sru    if (uname_decomposed)
1309151497Sru      // 1st char is the number of components
1310151497Sru      ustr = uname_decomposed + 1;
1311151497Sru  }
1312151497Sru  char *value = new char[strlen(ustr) + 1];
1313151497Sru  sprintf(value, equal(ustr, UNNAMED) ? ustr : "u%s", ustr);
1314151497Sru  return value;
1315151497Sru}
1316151497Sru
1317151497Srustatic int
1318151497Sruis_uname(char *name)
1319151497Sru{
1320151497Sru  size_t i;
1321151497Sru  size_t len = strlen(name);
1322151497Sru  if (len % 5)
1323151497Sru    return 0;
1324151497Sru
1325151497Sru  if (name[0] != 'u')
1326151497Sru    return 0;
1327151497Sru  for (i = 1; i < 4; i++)
1328151497Sru    if (!csxdigit(name[i]))
1329151497Sru      return 0;
1330151497Sru  for (i = 5; i < len; i++)
1331151497Sru    if (i % 5 ? !csxdigit(name[i]) : name[i] != '_')
1332151497Sru      return 0;
1333151497Sru
1334151497Sru  return 1;
1335151497Sru}
1336151497Sru
1337151497Srustatic int
1338151497Sruread_map(const char *file, const int tfm_type)
1339151497Sru{
1340114402Sru  errno = 0;
1341114402Sru  FILE *fp = fopen(file, "r");
1342114402Sru  if (!fp) {
1343114402Sru    error("can't open `%1': %2", file, strerror(errno));
1344114402Sru    return 0;
1345114402Sru  }
1346114402Sru  current_filename = file;
1347114402Sru  char buf[512];
1348114402Sru  current_lineno = 0;
1349151497Sru  char *nonum;
1350114402Sru  while (fgets(buf, int(sizeof(buf)), fp)) {
1351114402Sru    current_lineno++;
1352114402Sru    char *ptr = buf;
1353114402Sru    while (csspace(*ptr))
1354114402Sru      ptr++;
1355114402Sru    if (*ptr == '\0' || *ptr == '#')
1356114402Sru      continue;
1357114402Sru    ptr = strtok(ptr, " \n\t");
1358114402Sru    if (!ptr)
1359114402Sru      continue;
1360151497Sru
1361151497Sru    int msl_code = int(strtol(ptr, &nonum, 10));
1362151497Sru    if (*nonum != '\0') {
1363151497Sru      if (csxdigit(*nonum))
1364151497Sru	error("bad MSL map: got hex code (%1)", ptr);
1365151497Sru      else if (ptr == nonum)
1366151497Sru	error("bad MSL map: bad MSL code (%1)", ptr);
1367151497Sru      else
1368151497Sru	error("bad MSL map");
1369114402Sru      fclose(fp);
1370114402Sru      return 0;
1371114402Sru    }
1372151497Sru
1373151497Sru    ptr = strtok(NULL, " \n\t");
1374151497Sru    if (!ptr)
1375151497Sru      continue;
1376151497Sru    int unicode = int(strtol(ptr, &nonum, 16));
1377151497Sru    if (*nonum != '\0') {
1378151497Sru      if (ptr == nonum)
1379151497Sru	error("bad Unicode value (%1)", ptr);
1380151497Sru      else
1381151497Sru	error("bad Unicode map");
1382114402Sru      fclose(fp);
1383114402Sru      return 0;
1384114402Sru    }
1385151497Sru    if (strlen(ptr) != 4) {
1386151497Sru      error("bad Unicode value (%1)", ptr);
1387151497Sru      return 0;
1388151497Sru    }
1389151497Sru
1390151497Sru    int n = tfm_type == MSL ? msl_code : unicode;
1391151497Sru    if (tfm_type == UNICODE && n > 0xFFFF) {
1392151497Sru      // greatest value supported by TFM files
1393151497Sru      error("bad Unicode value (%1): greatest value is 0xFFFF", ptr);
1394151497Sru      fclose(fp);
1395151497Sru      return 0;
1396151497Sru    }
1397151497Sru    else if (n < 0) {
1398151497Sru      error("negative code value (%1)", ptr);
1399151497Sru      fclose(fp);
1400151497Sru      return 0;
1401151497Sru    }
1402151497Sru
1403151497Sru    ptr = strtok(NULL, " \n\t");
1404151497Sru    if (!ptr) {					// groff name
1405151497Sru      error("missing name(s)");
1406151497Sru      fclose(fp);
1407151497Sru      return 0;
1408151497Sru    }
1409151497Sru    // leave decomposed Unicode values alone
1410151497Sru    else if (is_uname(ptr) && !is_decomposed(ptr))
1411151497Sru      ptr = unicode_to_ucode_name(strtol(ptr + 1, &nonum, 16));
1412151497Sru
1413151497Sru    if (size_t(n) >= charcode_name_table_size) {
1414151497Sru      size_t old_size = charcode_name_table_size;
1415151497Sru      name_list **old_table = charcode_name_table;
1416151497Sru      charcode_name_table_size = n + 256;
1417151497Sru      charcode_name_table = new name_list *[charcode_name_table_size];
1418114402Sru      if (old_table) {
1419151497Sru	memcpy(charcode_name_table, old_table, old_size*sizeof(name_list *));
1420114402Sru	a_delete old_table;
1421114402Sru      }
1422151497Sru      for (size_t i = old_size; i < charcode_name_table_size; i++)
1423151497Sru	charcode_name_table[i] = NULL;
1424114402Sru    }
1425151497Sru
1426151497Sru    // a '#' that isn't the first groff name begins a comment
1427151497Sru    for (int names = 1; ptr; ptr = strtok(NULL, " \n\t")) {
1428151497Sru      if (names++ > 1 && *ptr == '#')
1429151497Sru	break;
1430151497Sru      charcode_name_table[n] = new name_list(ptr, charcode_name_table[n]);
1431114402Sru    }
1432114402Sru  }
1433114402Sru  fclose(fp);
1434114402Sru  return 1;
1435114402Sru}
1436114402Sru
1437151497Srustatic const char *
1438151497Sruxbasename(const char *s)
1439114402Sru{
1440114402Sru  // DIR_SEPS[] are possible directory separator characters, see
1441114402Sru  // nonposix.h.  We want the rightmost separator of all possible
1442114402Sru  // ones.  Example: d:/foo\\bar.
1443114402Sru  const char *b = strrchr(s, DIR_SEPS[0]), *b1;
1444114402Sru  const char *sep = &DIR_SEPS[1];
1445114402Sru
1446114402Sru  while (*sep)
1447114402Sru    {
1448114402Sru      b1 = strrchr(s, *sep);
1449114402Sru      if (b1 && (!b || b1 > b))
1450114402Sru	b = b1;
1451114402Sru      sep++;
1452114402Sru    }
1453114402Sru  return b ? b + 1 : s;
1454114402Sru}
1455