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