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}
1055