Initial import from FreeBSD RELENG_4:
[games.git] / contrib / groff / src / libs / libgroff / font.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
3    Free Software Foundation, Inc.
4      Written by James Clark (jjc@jclark.com)
5
6 This file is part of groff.
7
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
12
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING.  If not, write to the Free Software
20 Foundation, 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
34 const char *const WS = " \t\n\r";
35
36 struct 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
48 struct 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
57 struct 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
68 struct 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
84 text_file::text_file(FILE *p, char *s) 
85 : fp(p), path(s), lineno(0), size(0), skip_comments(1), buf(0)
86 {
87 }
88
89 text_file::~text_file()
90 {
91   a_delete buf;
92   a_delete path;
93   if (fp)
94     fclose(fp);
95 }
96
97 int 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
139 void 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
150 font::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
161 font::~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
188 static 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
206 inline int font::scale(int w, int sz)
207 {
208   return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
209 }
210
211 int 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
239 int 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
245 int font::contains(int c)
246 {
247   return c >= 0 && c < nindices && ch_index[c] >= 0;
248 }
249
250 int font::is_special()
251 {
252   return special;
253 }
254
255 font_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
264 font_widths_cache::~font_widths_cache()
265 {
266   a_delete width;
267 }
268
269 int 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
300 int 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
306 int 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
312 int 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
318 int 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
324 int 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
330 int font::get_space_width(int point_size)
331 {
332   return scale(space_width, point_size);
333 }
334
335 font_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
340 inline 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
346 void font::add_kern(int i1, int i2, int amount)
347 {
348   if (!kern_hash_table) {
349     kern_hash_table = new font_kern_list *[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
357 int 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
367 int font::has_ligature(int mask)
368 {
369   return mask & ligatures;
370 }
371
372 int 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
378 int 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
384 const char *font::get_name()
385 {
386   return name;
387 }
388
389 const char *font::get_internal_name()
390 {
391   return internalname;
392 }
393
394 const 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
400 void 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
424 void 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
438 void 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
461 void 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
474 void 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
482 font *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
492 static 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
505 int 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];
513 again:
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
560 int 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 code `%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
800 static 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
816 int 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
1023 void font::handle_unknown_font_command(const char *, const char *,
1024                                        const char *, int)
1025 {
1026 }
1027
1028 FONT_COMMAND_HANDLER
1029 font::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