Home | History | Annotate | Line # | Download | only in libgroff
      1 /*	$NetBSD: font.cpp,v 1.1.1.1 2016/01/13 18:41:48 christos Exp $	*/
      2 
      3 // -*- C++ -*-
      4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
      5    Free Software Foundation, Inc.
      6      Written by James Clark (jjc (at) jclark.com)
      7 
      8 This file is part of groff.
      9 
     10 groff is free software; you can redistribute it and/or modify it under
     11 the terms of the GNU General Public License as published by the Free
     12 Software Foundation; either version 2, or (at your option) any later
     13 version.
     14 
     15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
     16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     17 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     18 for more details.
     19 
     20 You should have received a copy of the GNU General Public License along
     21 with groff; see the file COPYING.  If not, write to the Free Software
     22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
     23 
     24 #include "lib.h"
     25 
     26 #include <ctype.h>
     27 #include <assert.h>
     28 #include <math.h>
     29 #include <stdlib.h>
     30 #include "errarg.h"
     31 #include "error.h"
     32 #include "cset.h"
     33 #include "font.h"
     34 #include "paper.h"
     35 
     36 const char *const WS = " \t\n\r";
     37 
     38 struct font_char_metric {
     39   char type;
     40   int code;
     41   int width;
     42   int height;
     43   int depth;
     44   int pre_math_space;
     45   int italic_correction;
     46   int subscript_correction;
     47   char *special_device_coding;
     48 };
     49 
     50 struct font_kern_list {
     51   int i1;
     52   int i2;
     53   int amount;
     54   font_kern_list *next;
     55 
     56   font_kern_list(int, int, int, font_kern_list * = 0);
     57 };
     58 
     59 struct font_widths_cache {
     60   font_widths_cache *next;
     61   int point_size;
     62   int *width;
     63 
     64   font_widths_cache(int, int, font_widths_cache * = 0);
     65   ~font_widths_cache();
     66 };
     67 
     68 /* text_file */
     69 
     70 struct text_file {
     71   FILE *fp;
     72   char *path;
     73   int lineno;
     74   int size;
     75   int skip_comments;
     76   int silent;
     77   char *buf;
     78   text_file(FILE *fp, char *p);
     79   ~text_file();
     80   int next();
     81   void error(const char *format,
     82 	     const errarg &arg1 = empty_errarg,
     83 	     const errarg &arg2 = empty_errarg,
     84 	     const errarg &arg3 = empty_errarg);
     85 };
     86 
     87 text_file::text_file(FILE *p, char *s)
     88 : fp(p), path(s), lineno(0), size(0), skip_comments(1), silent(0), buf(0)
     89 {
     90 }
     91 
     92 text_file::~text_file()
     93 {
     94   a_delete buf;
     95   a_delete path;
     96   if (fp)
     97     fclose(fp);
     98 }
     99 
    100 int text_file::next()
    101 {
    102   if (fp == 0)
    103     return 0;
    104   if (buf == 0) {
    105     buf = new char[128];
    106     size = 128;
    107   }
    108   for (;;) {
    109     int i = 0;
    110     for (;;) {
    111       int c = getc(fp);
    112       if (c == EOF)
    113 	break;
    114       if (invalid_input_char(c))
    115 	error("invalid input character code `%1'", int(c));
    116       else {
    117 	if (i + 1 >= size) {
    118 	  char *old_buf = buf;
    119 	  buf = new char[size*2];
    120 	  memcpy(buf, old_buf, size);
    121 	  a_delete old_buf;
    122 	  size *= 2;
    123 	}
    124 	buf[i++] = c;
    125 	if (c == '\n')
    126 	  break;
    127       }
    128     }
    129     if (i == 0)
    130       break;
    131     buf[i] = '\0';
    132     lineno++;
    133     char *ptr = buf;
    134     while (csspace(*ptr))
    135       ptr++;
    136     if (*ptr != 0 && (!skip_comments || *ptr != '#'))
    137       return 1;
    138   }
    139   return 0;
    140 }
    141 
    142 void text_file::error(const char *format,
    143 		      const errarg &arg1,
    144 		      const errarg &arg2,
    145 		      const errarg &arg3)
    146 {
    147   if (!silent)
    148     error_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
    149 }
    150 
    151 
    152 /* font functions */
    153 
    154 font::font(const char *s)
    155 : ligatures(0), kern_hash_table(0), space_width(0), ch_index(0), nindices(0),
    156   ch(0), ch_used(0), ch_size(0), special(0), widths_cache(0)
    157 {
    158   name = new char[strlen(s) + 1];
    159   strcpy(name, s);
    160   internalname = 0;
    161   slant = 0.0;
    162   // load();			// for testing
    163 }
    164 
    165 font::~font()
    166 {
    167   for (int i = 0; i < ch_used; i++)
    168     if (ch[i].special_device_coding)
    169       a_delete ch[i].special_device_coding;
    170   a_delete ch;
    171   a_delete ch_index;
    172   if (kern_hash_table) {
    173     for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) {
    174       font_kern_list *kerns = kern_hash_table[i];
    175       while (kerns) {
    176 	font_kern_list *tem = kerns;
    177 	kerns = kerns->next;
    178 	delete tem;
    179       }
    180     }
    181     a_delete kern_hash_table;
    182   }
    183   a_delete name;
    184   a_delete internalname;
    185   while (widths_cache) {
    186     font_widths_cache *tem = widths_cache;
    187     widths_cache = widths_cache->next;
    188     delete tem;
    189   }
    190 }
    191 
    192 static int scale_round(int n, int x, int y)
    193 {
    194   assert(x >= 0 && y > 0);
    195   int y2 = y/2;
    196   if (x == 0)
    197     return 0;
    198   if (n >= 0) {
    199     if (n <= (INT_MAX - y2)/x)
    200       return (n*x + y2)/y;
    201     return int(n*double(x)/double(y) + .5);
    202   }
    203   else {
    204     if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
    205       return (n*x - y2)/y;
    206     return int(n*double(x)/double(y) - .5);
    207   }
    208 }
    209 
    210 inline int font::scale(int w, int sz)
    211 {
    212   return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
    213 }
    214 
    215 int font::unit_scale(double *value, char unit)
    216 {
    217   // we scale everything to inch
    218   double divisor = 0;
    219   switch (unit) {
    220   case 'i':
    221     divisor = 1;
    222     break;
    223   case 'p':
    224     divisor = 72;
    225     break;
    226   case 'P':
    227     divisor = 6;
    228     break;
    229   case 'c':
    230     divisor = 2.54;
    231     break;
    232   default:
    233     assert(0);
    234     break;
    235   }
    236   if (divisor) {
    237     *value /= divisor;
    238     return 1;
    239   }
    240   return 0;
    241 }
    242 
    243 int font::get_skew(int c, int point_size, int sl)
    244 {
    245   int h = get_height(c, point_size);
    246   return int(h*tan((slant+sl)*PI/180.0) + .5);
    247 }
    248 
    249 int font::contains(int c)
    250 {
    251   return c >= 0 && c < nindices && ch_index[c] >= 0;
    252 }
    253 
    254 int font::is_special()
    255 {
    256   return special;
    257 }
    258 
    259 font_widths_cache::font_widths_cache(int ps, int ch_size,
    260 				     font_widths_cache *p)
    261 : next(p), point_size(ps)
    262 {
    263   width = new int[ch_size];
    264   for (int i = 0; i < ch_size; i++)
    265     width[i] = -1;
    266 }
    267 
    268 font_widths_cache::~font_widths_cache()
    269 {
    270   a_delete width;
    271 }
    272 
    273 int font::get_width(int c, int point_size)
    274 {
    275   assert(c >= 0 && c < nindices);
    276   int i = ch_index[c];
    277   assert(i >= 0);
    278 
    279   if (point_size == unitwidth || font::unscaled_charwidths)
    280     return ch[i].width;
    281 
    282   if (!widths_cache)
    283     widths_cache = new font_widths_cache(point_size, ch_size);
    284   else if (widths_cache->point_size != point_size) {
    285     font_widths_cache **p;
    286     for (p = &widths_cache; *p; p = &(*p)->next)
    287       if ((*p)->point_size == point_size)
    288 	break;
    289     if (*p) {
    290       font_widths_cache *tem = *p;
    291       *p = (*p)->next;
    292       tem->next = widths_cache;
    293       widths_cache = tem;
    294     }
    295     else
    296       widths_cache = new font_widths_cache(point_size, ch_size, widths_cache);
    297   }
    298   int &w = widths_cache->width[i];
    299   if (w < 0)
    300     w = scale(ch[i].width, point_size);
    301   return w;
    302 }
    303 
    304 int font::get_height(int c, int point_size)
    305 {
    306   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
    307   return scale(ch[ch_index[c]].height, point_size);
    308 }
    309 
    310 int font::get_depth(int c, int point_size)
    311 {
    312   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
    313   return scale(ch[ch_index[c]].depth, point_size);
    314 }
    315 
    316 int font::get_italic_correction(int c, int point_size)
    317 {
    318   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
    319   return scale(ch[ch_index[c]].italic_correction, point_size);
    320 }
    321 
    322 int font::get_left_italic_correction(int c, int point_size)
    323 {
    324   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
    325   return scale(ch[ch_index[c]].pre_math_space, point_size);
    326 }
    327 
    328 int font::get_subscript_correction(int c, int point_size)
    329 {
    330   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
    331   return scale(ch[ch_index[c]].subscript_correction, point_size);
    332 }
    333 
    334 int font::get_space_width(int point_size)
    335 {
    336   return scale(space_width, point_size);
    337 }
    338 
    339 font_kern_list::font_kern_list(int c1, int c2, int n, font_kern_list *p)
    340 : i1(c1), i2(c2), amount(n), next(p)
    341 {
    342 }
    343 
    344 inline int font::hash_kern(int i1, int i2)
    345 {
    346   int n = ((i1 << 10) + i2) % KERN_HASH_TABLE_SIZE;
    347   return n < 0 ? -n : n;
    348 }
    349 
    350 void font::add_kern(int i1, int i2, int amount)
    351 {
    352   if (!kern_hash_table) {
    353     kern_hash_table = new font_kern_list *[int(KERN_HASH_TABLE_SIZE)];
    354     for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++)
    355       kern_hash_table[i] = 0;
    356   }
    357   font_kern_list **p = kern_hash_table + hash_kern(i1, i2);
    358   *p = new font_kern_list(i1, i2, amount, *p);
    359 }
    360 
    361 int font::get_kern(int i1, int i2, int point_size)
    362 {
    363   if (kern_hash_table) {
    364     for (font_kern_list *p = kern_hash_table[hash_kern(i1, i2)]; p; p = p->next)
    365       if (i1 == p->i1 && i2 == p->i2)
    366 	return scale(p->amount, point_size);
    367   }
    368   return 0;
    369 }
    370 
    371 int font::has_ligature(int mask)
    372 {
    373   return mask & ligatures;
    374 }
    375 
    376 int font::get_character_type(int c)
    377 {
    378   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
    379   return ch[ch_index[c]].type;
    380 }
    381 
    382 int font::get_code(int c)
    383 {
    384   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
    385   return ch[ch_index[c]].code;
    386 }
    387 
    388 const char *font::get_name()
    389 {
    390   return name;
    391 }
    392 
    393 const char *font::get_internal_name()
    394 {
    395   return internalname;
    396 }
    397 
    398 const char *font::get_special_device_encoding(int c)
    399 {
    400   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
    401   return ch[ch_index[c]].special_device_coding;
    402 }
    403 
    404 const char *font::get_image_generator()
    405 {
    406   return image_generator;
    407 }
    408 
    409 void font::alloc_ch_index(int idx)
    410 {
    411   if (nindices == 0) {
    412     nindices = 128;
    413     if (idx >= nindices)
    414       nindices = idx + 10;
    415     ch_index = new int[nindices];
    416     for (int i = 0; i < nindices; i++)
    417       ch_index[i] = -1;
    418   }
    419   else {
    420     int old_nindices = nindices;
    421     nindices *= 2;
    422     if (idx >= nindices)
    423       nindices = idx + 10;
    424     int *old_ch_index = ch_index;
    425     ch_index = new int[nindices];
    426     memcpy(ch_index, old_ch_index, sizeof(int)*old_nindices);
    427     for (int i = old_nindices; i < nindices; i++)
    428       ch_index[i] = -1;
    429     a_delete old_ch_index;
    430   }
    431 }
    432 
    433 void font::extend_ch()
    434 {
    435   if (ch == 0)
    436     ch = new font_char_metric[ch_size = 16];
    437   else {
    438     int old_ch_size = ch_size;
    439     ch_size *= 2;
    440     font_char_metric *old_ch = ch;
    441     ch = new font_char_metric[ch_size];
    442     memcpy(ch, old_ch, old_ch_size*sizeof(font_char_metric));
    443     a_delete old_ch;
    444   }
    445 }
    446 
    447 void font::compact()
    448 {
    449   int i;
    450   for (i = nindices - 1; i >= 0; i--)
    451     if (ch_index[i] >= 0)
    452       break;
    453   i++;
    454   if (i < nindices) {
    455     int *old_ch_index = ch_index;
    456     ch_index = new int[i];
    457     memcpy(ch_index, old_ch_index, i*sizeof(int));
    458     a_delete old_ch_index;
    459     nindices = i;
    460   }
    461   if (ch_used < ch_size) {
    462     font_char_metric *old_ch = ch;
    463     ch = new font_char_metric[ch_used];
    464     memcpy(ch, old_ch, ch_used*sizeof(font_char_metric));
    465     a_delete old_ch;
    466     ch_size = ch_used;
    467   }
    468 }
    469 
    470 void font::add_entry(int idx, const font_char_metric &metric)
    471 {
    472   assert(idx >= 0);
    473   if (idx >= nindices)
    474     alloc_ch_index(idx);
    475   assert(idx < nindices);
    476   if (ch_used + 1 >= ch_size)
    477     extend_ch();
    478   assert(ch_used + 1 < ch_size);
    479   ch_index[idx] = ch_used;
    480   ch[ch_used++] = metric;
    481 }
    482 
    483 void font::copy_entry(int new_index, int old_index)
    484 {
    485   assert(new_index >= 0 && old_index >= 0 && old_index < nindices);
    486   if (new_index >= nindices)
    487     alloc_ch_index(new_index);
    488   ch_index[new_index] = ch_index[old_index];
    489 }
    490 
    491 font *font::load_font(const char *s, int *not_found, int head_only)
    492 {
    493   font *f = new font(s);
    494   if (!f->load(not_found, head_only)) {
    495     delete f;
    496     return 0;
    497   }
    498   return f;
    499 }
    500 
    501 static char *trim_arg(char *p)
    502 {
    503   if (!p)
    504     return 0;
    505   while (csspace(*p))
    506     p++;
    507   char *q = strchr(p, '\0');
    508   while (q > p && csspace(q[-1]))
    509     q--;
    510   *q = '\0';
    511   return p;
    512 }
    513 
    514 int font::scan_papersize(const char *p,
    515 			 const char **size, double *length, double *width)
    516 {
    517   double l, w;
    518   char lu[2], wu[2];
    519   const char *pp = p;
    520   int test_file = 1;
    521   char line[255];
    522 again:
    523   if (csdigit(*pp)) {
    524     if (sscanf(pp, "%lf%1[ipPc],%lf%1[ipPc]", &l, lu, &w, wu) == 4
    525 	&& l > 0 && w > 0
    526 	&& unit_scale(&l, lu[0]) && unit_scale(&w, wu[0])) {
    527       if (length)
    528 	*length = l;
    529       if (width)
    530 	*width = w;
    531       if (size)
    532 	*size = "custom";
    533       return 1;
    534     }
    535   }
    536   else {
    537     int i;
    538     for (i = 0; i < NUM_PAPERSIZES; i++)
    539       if (strcasecmp(papersizes[i].name, pp) == 0) {
    540 	if (length)
    541 	  *length = papersizes[i].length;
    542 	if (width)
    543 	  *width = papersizes[i].width;
    544 	if (size)
    545 	  *size = papersizes[i].name;
    546 	return 1;
    547       }
    548     if (test_file) {
    549       FILE *f = fopen(p, "r");
    550       if (f) {
    551 	fgets(line, 254, f);
    552 	fclose(f);
    553 	test_file = 0;
    554 	char *linep = strchr(line, '\0');
    555 	// skip final newline, if any
    556 	if (*(--linep) == '\n')
    557 	  *linep = '\0';
    558 	pp = line;
    559 	goto again;
    560       }
    561     }
    562   }
    563   return 0;
    564 }
    565 
    566 // If the font can't be found, then if not_found is non-NULL, it will be set
    567 // to 1 otherwise a message will be printed.
    568 
    569 int font::load(int *not_found, int head_only)
    570 {
    571   char *path;
    572   FILE *fp;
    573   if ((fp = open_file(name, &path)) == NULL) {
    574     if (not_found)
    575       *not_found = 1;
    576     else
    577       error("can't find font file `%1'", name);
    578     return 0;
    579   }
    580   text_file t(fp, path);
    581   t.skip_comments = 1;
    582   t.silent = head_only;
    583   char *p;
    584   for (;;) {
    585     if (!t.next()) {
    586       t.error("missing charset command");
    587       return 0;
    588     }
    589     p = strtok(t.buf, WS);
    590     if (strcmp(p, "name") == 0) {
    591     }
    592     else if (strcmp(p, "spacewidth") == 0) {
    593       p = strtok(0, WS);
    594       int n;
    595       if (p == 0 || sscanf(p, "%d", &n) != 1 || n <= 0) {
    596 	t.error("bad argument for spacewidth command");
    597 	return 0;
    598       }
    599       space_width = n;
    600     }
    601     else if (strcmp(p, "slant") == 0) {
    602       p = strtok(0, WS);
    603       double n;
    604       if (p == 0 || sscanf(p, "%lf", &n) != 1 || n >= 90.0 || n <= -90.0) {
    605 	t.error("bad argument for slant command", p);
    606 	return 0;
    607       }
    608       slant = n;
    609     }
    610     else if (strcmp(p, "ligatures") == 0) {
    611       for (;;) {
    612 	p = strtok(0, WS);
    613 	if (p == 0 || strcmp(p, "0") == 0)
    614 	  break;
    615 	if (strcmp(p, "ff") == 0)
    616 	  ligatures |= LIG_ff;
    617 	else if (strcmp(p, "fi") == 0)
    618 	  ligatures |= LIG_fi;
    619 	else if (strcmp(p, "fl") == 0)
    620 	  ligatures |= LIG_fl;
    621 	else if (strcmp(p, "ffi") == 0)
    622 	  ligatures |= LIG_ffi;
    623 	else if (strcmp(p, "ffl") == 0)
    624 	  ligatures |= LIG_ffl;
    625 	else {
    626 	  t.error("unrecognised ligature `%1'", p);
    627 	  return 0;
    628 	}
    629       }
    630     }
    631     else if (strcmp(p, "internalname") == 0) {
    632       p = strtok(0, WS);
    633       if (!p) {
    634 	t.error("`internalname command requires argument");
    635 	return 0;
    636       }
    637       internalname = new char[strlen(p) + 1];
    638       strcpy(internalname, p);
    639     }
    640     else if (strcmp(p, "special") == 0) {
    641       special = 1;
    642     }
    643     else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) {
    644       char *command = p;
    645       p = strtok(0, "\n");
    646       handle_unknown_font_command(command, trim_arg(p), t.path, t.lineno);
    647     }
    648     else
    649       break;
    650   }
    651   if (head_only)
    652     return 1;
    653   char *command = p;
    654   int had_charset = 0;
    655   t.skip_comments = 0;
    656   while (command) {
    657     if (strcmp(command, "kernpairs") == 0) {
    658       for (;;) {
    659 	if (!t.next()) {
    660 	  command = 0;
    661 	  break;
    662 	}
    663 	char *c1 = strtok(t.buf, WS);
    664 	if (c1 == 0)
    665 	  continue;
    666 	char *c2 = strtok(0, WS);
    667 	if (c2 == 0) {
    668 	  command = c1;
    669 	  break;
    670 	}
    671 	p = strtok(0, WS);
    672 	if (p == 0) {
    673 	  t.error("missing kern amount");
    674 	  return 0;
    675 	}
    676 	int n;
    677 	if (sscanf(p, "%d", &n) != 1) {
    678 	  t.error("bad kern amount `%1'", p);
    679 	  return 0;
    680 	}
    681 	int i1 = name_to_index(c1);
    682 	if (i1 < 0) {
    683 	  t.error("invalid character `%1'", c1);
    684 	  return 0;
    685 	}
    686 	int i2 = name_to_index(c2);
    687 	if (i2 < 0) {
    688 	  t.error("invalid character `%1'", c2);
    689 	  return 0;
    690 	}
    691 	add_kern(i1, i2, n);
    692       }
    693     }
    694     else if (strcmp(command, "charset") == 0) {
    695       had_charset = 1;
    696       int last_index = -1;
    697       for (;;) {
    698 	if (!t.next()) {
    699 	  command = 0;
    700 	  break;
    701 	}
    702 	char *nm = strtok(t.buf, WS);
    703 	if (nm == 0)
    704 	  continue;			// I dont think this should happen
    705 	p = strtok(0, WS);
    706 	if (p == 0) {
    707 	  command = nm;
    708 	  break;
    709 	}
    710 	if (p[0] == '"') {
    711 	  if (last_index == -1) {
    712 	    t.error("first charset entry is duplicate");
    713 	    return 0;
    714 	  }
    715 	  if (strcmp(nm, "---") == 0) {
    716 	    t.error("unnamed character cannot be duplicate");
    717 	    return 0;
    718 	  }
    719 	  int idx = name_to_index(nm);
    720 	  if (idx < 0) {
    721 	    t.error("invalid character `%1'", nm);
    722 	    return 0;
    723 	  }
    724 	  copy_entry(idx, last_index);
    725 	}
    726 	else {
    727 	  font_char_metric metric;
    728 	  metric.height = 0;
    729 	  metric.depth = 0;
    730 	  metric.pre_math_space = 0;
    731 	  metric.italic_correction = 0;
    732 	  metric.subscript_correction = 0;
    733 	  int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
    734 			      &metric.width, &metric.height, &metric.depth,
    735 			      &metric.italic_correction,
    736 			      &metric.pre_math_space,
    737 			      &metric.subscript_correction);
    738 	  if (nparms < 1) {
    739 	    t.error("bad width for `%1'", nm);
    740 	    return 0;
    741 	  }
    742 	  p = strtok(0, WS);
    743 	  if (p == 0) {
    744 	    t.error("missing character type for `%1'", nm);
    745 	    return 0;
    746 	  }
    747 	  int type;
    748 	  if (sscanf(p, "%d", &type) != 1) {
    749 	    t.error("bad character type for `%1'", nm);
    750 	    return 0;
    751 	  }
    752 	  if (type < 0 || type > 255) {
    753 	    t.error("character type `%1' out of range", type);
    754 	    return 0;
    755 	  }
    756 	  metric.type = type;
    757 	  p = strtok(0, WS);
    758 	  if (p == 0) {
    759 	    t.error("missing code for `%1'", nm);
    760 	    return 0;
    761 	  }
    762 	  char *ptr;
    763 	  metric.code = (int)strtol(p, &ptr, 0);
    764 	  if (metric.code == 0 && ptr == p) {
    765 	    t.error("bad code `%1' for character `%2'", p, nm);
    766 	    return 0;
    767 	  }
    768 	  p = strtok(0, WS);
    769 	  if ((p == NULL) || (strcmp(p, "--") == 0)) {
    770 	    metric.special_device_coding = NULL;
    771 	  }
    772 	  else {
    773 	    char *nam = new char[strlen(p) + 1];
    774 	    strcpy(nam, p);
    775 	    metric.special_device_coding = nam;
    776 	  }
    777 	  if (strcmp(nm, "---") == 0) {
    778 	    last_index = number_to_index(metric.code);
    779 	    add_entry(last_index, metric);
    780 	  }
    781 	  else {
    782 	    last_index = name_to_index(nm);
    783 	    if (last_index < 0) {
    784 	      t.error("invalid character `%1'", nm);
    785 	      return 0;
    786 	    }
    787 	    add_entry(last_index, metric);
    788 	    copy_entry(number_to_index(metric.code), last_index);
    789 	  }
    790 	}
    791       }
    792       if (last_index == -1) {
    793 	t.error("I didn't seem to find any characters");
    794 	return 0;
    795       }
    796     }
    797     else {
    798       t.error("unrecognised command `%1' after `kernpairs' or `charset' command", command);
    799       return 0;
    800     }
    801   }
    802   if (!had_charset) {
    803     t.error("missing charset command");
    804     return 0;
    805   }
    806   if (space_width == 0)
    807     space_width = scale_round(unitwidth, res, 72*3*sizescale);
    808   compact();
    809   return 1;
    810 }
    811 
    812 static struct {
    813   const char *command;
    814   int *ptr;
    815 } table[] = {
    816   { "res", &font::res },
    817   { "hor", &font::hor },
    818   { "vert", &font::vert },
    819   { "unitwidth", &font::unitwidth },
    820   { "paperwidth", &font::paperwidth },
    821   { "paperlength", &font::paperlength },
    822   { "spare1", &font::biggestfont },
    823   { "biggestfont", &font::biggestfont },
    824   { "spare2", &font::spare2 },
    825   { "sizescale", &font::sizescale },
    826   };
    827 
    828 int font::load_desc()
    829 {
    830   int nfonts = 0;
    831   FILE *fp;
    832   char *path;
    833   if ((fp = open_file("DESC", &path)) == 0) {
    834     error("can't find `DESC' file");
    835     return 0;
    836   }
    837   text_file t(fp, path);
    838   t.skip_comments = 1;
    839   res = 0;
    840   while (t.next()) {
    841     char *p = strtok(t.buf, WS);
    842     int found = 0;
    843     unsigned int idx;
    844     for (idx = 0; !found && idx < sizeof(table)/sizeof(table[0]); idx++)
    845       if (strcmp(table[idx].command, p) == 0)
    846 	found = 1;
    847     if (found) {
    848       char *q = strtok(0, WS);
    849       if (!q) {
    850 	t.error("missing value for command `%1'", p);
    851 	return 0;
    852       }
    853       //int *ptr = &(this->*(table[idx-1].ptr));
    854       int *ptr = table[idx-1].ptr;
    855       if (sscanf(q, "%d", ptr) != 1) {
    856 	t.error("bad number `%1'", q);
    857 	return 0;
    858       }
    859     }
    860     else if (strcmp("family", p) == 0) {
    861       p = strtok(0, WS);
    862       if (!p) {
    863 	t.error("family command requires an argument");
    864 	return 0;
    865       }
    866       char *tem = new char[strlen(p)+1];
    867       strcpy(tem, p);
    868       family = tem;
    869     }
    870     else if (strcmp("fonts", p) == 0) {
    871       p = strtok(0, WS);
    872       if (!p || sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
    873 	t.error("bad number of fonts `%1'", p);
    874 	return 0;
    875       }
    876       font_name_table = (const char **)new char *[nfonts+1];
    877       for (int i = 0; i < nfonts; i++) {
    878 	p = strtok(0, WS);
    879 	while (p == 0) {
    880 	  if (!t.next()) {
    881 	    t.error("end of file while reading list of fonts");
    882 	    return 0;
    883 	  }
    884 	  p = strtok(t.buf, WS);
    885 	}
    886 	char *temp = new char[strlen(p)+1];
    887 	strcpy(temp, p);
    888 	font_name_table[i] = temp;
    889       }
    890       p = strtok(0, WS);
    891       if (p != 0) {
    892 	t.error("font count does not match number of fonts");
    893 	return 0;
    894       }
    895       font_name_table[nfonts] = 0;
    896     }
    897     else if (strcmp("papersize", p) == 0) {
    898       p = strtok(0, WS);
    899       if (!p) {
    900 	t.error("papersize command requires an argument");
    901 	return 0;
    902       }
    903       int found_paper = 0;
    904       while (p) {
    905 	double unscaled_paperwidth, unscaled_paperlength;
    906 	if (scan_papersize(p, &papersize, &unscaled_paperlength,
    907 			   &unscaled_paperwidth)) {
    908 	  paperwidth = int(unscaled_paperwidth * res + 0.5);
    909 	  paperlength = int(unscaled_paperlength * res + 0.5);
    910 	  found_paper = 1;
    911 	  break;
    912 	}
    913 	p = strtok(0, WS);
    914       }
    915       if (!found_paper) {
    916 	t.error("bad paper size");
    917 	return 0;
    918       }
    919     }
    920     else if (strcmp("unscaled_charwidths", p) == 0)
    921       unscaled_charwidths = 1;
    922     else if (strcmp("pass_filenames", p) == 0)
    923       pass_filenames = 1;
    924     else if (strcmp("sizes", p) == 0) {
    925       int n = 16;
    926       sizes = new int[n];
    927       int i = 0;
    928       for (;;) {
    929 	p = strtok(0, WS);
    930 	while (p == 0) {
    931 	  if (!t.next()) {
    932 	    t.error("list of sizes must be terminated by `0'");
    933 	    return 0;
    934 	  }
    935 	  p = strtok(t.buf, WS);
    936 	}
    937 	int lower, upper;
    938 	switch (sscanf(p, "%d-%d", &lower, &upper)) {
    939 	case 1:
    940 	  upper = lower;
    941 	  // fall through
    942 	case 2:
    943 	  if (lower <= upper && lower >= 0)
    944 	    break;
    945 	  // fall through
    946 	default:
    947 	  t.error("bad size range `%1'", p);
    948 	  return 0;
    949 	}
    950 	if (i + 2 > n) {
    951 	  int *old_sizes = sizes;
    952 	  sizes = new int[n*2];
    953 	  memcpy(sizes, old_sizes, n*sizeof(int));
    954 	  n *= 2;
    955 	  a_delete old_sizes;
    956 	}
    957 	sizes[i++] = lower;
    958 	if (lower == 0)
    959 	  break;
    960 	sizes[i++] = upper;
    961       }
    962       if (i == 1) {
    963 	t.error("must have some sizes");
    964 	return 0;
    965       }
    966     }
    967     else if (strcmp("styles", p) == 0) {
    968       int style_table_size = 5;
    969       style_table = (const char **)new char *[style_table_size];
    970       int j;
    971       for (j = 0; j < style_table_size; j++)
    972 	style_table[j] = 0;
    973       int i = 0;
    974       for (;;) {
    975 	p = strtok(0, WS);
    976 	if (p == 0)
    977 	  break;
    978 	// leave room for terminating 0
    979 	if (i + 1 >= style_table_size) {
    980 	  const char **old_style_table = style_table;
    981 	  style_table_size *= 2;
    982 	  style_table = (const char **)new char*[style_table_size];
    983 	  for (j = 0; j < i; j++)
    984 	    style_table[j] = old_style_table[j];
    985 	  for (; j < style_table_size; j++)
    986 	    style_table[j] = 0;
    987 	  a_delete old_style_table;
    988 	}
    989 	char *tem = new char[strlen(p) + 1];
    990 	strcpy(tem, p);
    991 	style_table[i++] = tem;
    992       }
    993     }
    994     else if (strcmp("tcommand", p) == 0)
    995       tcommand = 1;
    996     else if (strcmp("use_charnames_in_special", p) == 0)
    997       use_charnames_in_special = 1;
    998     else if (strcmp("image_generator", p) == 0) {
    999       p = strtok(0, WS);
   1000       if (!p) {
   1001 	t.error("image_generator command requires an argument");
   1002 	return 0;
   1003       }
   1004       image_generator = strsave(p);
   1005     }
   1006     else if (strcmp("charset", p) == 0)
   1007       break;
   1008     else if (unknown_desc_command_handler) {
   1009       char *command = p;
   1010       p = strtok(0, "\n");
   1011       (*unknown_desc_command_handler)(command, trim_arg(p), t.path, t.lineno);
   1012     }
   1013   }
   1014   if (res == 0) {
   1015     t.error("missing `res' command");
   1016     return 0;
   1017   }
   1018   if (unitwidth == 0) {
   1019     t.error("missing `unitwidth' command");
   1020     return 0;
   1021   }
   1022   if (font_name_table == 0) {
   1023     t.error("missing `fonts' command");
   1024     return 0;
   1025   }
   1026   if (sizes == 0) {
   1027     t.error("missing `sizes' command");
   1028     return 0;
   1029   }
   1030   if (sizescale < 1) {
   1031     t.error("bad `sizescale' value");
   1032     return 0;
   1033   }
   1034   if (hor < 1) {
   1035     t.error("bad `hor' value");
   1036     return 0;
   1037   }
   1038   if (vert < 1) {
   1039     t.error("bad `vert' value");
   1040     return 0;
   1041   }
   1042   return 1;
   1043 }
   1044 
   1045 void font::handle_unknown_font_command(const char *, const char *,
   1046 				       const char *, int)
   1047 {
   1048 }
   1049 
   1050 FONT_COMMAND_HANDLER
   1051 font::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func)
   1052 {
   1053   FONT_COMMAND_HANDLER prev = unknown_desc_command_handler;
   1054   unknown_desc_command_handler = func;
   1055   return prev;
   1056 }
   1057