PageRenderTime 115ms CodeModel.GetById 2ms app.highlight 104ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/groff/src/libs/libgroff/font.cpp

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