]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/groff/src/libs/libgroff/font.cpp
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / groff / src / libs / libgroff / font.cpp
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
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, 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
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   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
85 text_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
90 text_file::~text_file()
91 {
92   a_delete buf;
93   a_delete path;
94   if (fp)
95     fclose(fp);
96 }
97
98 int 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
140 void 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
152 font::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
163 font::~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
190 static 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
208 inline int font::scale(int w, int sz)
209 {
210   return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
211 }
212
213 int 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
241 int 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
247 int font::contains(int c)
248 {
249   return c >= 0 && c < nindices && ch_index[c] >= 0;
250 }
251
252 int font::is_special()
253 {
254   return special;
255 }
256
257 font_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
266 font_widths_cache::~font_widths_cache()
267 {
268   a_delete width;
269 }
270
271 int 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
302 int 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
308 int 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
314 int 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
320 int 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
326 int 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
332 int font::get_space_width(int point_size)
333 {
334   return scale(space_width, point_size);
335 }
336
337 font_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
342 inline 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
348 void 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
359 int 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
369 int font::has_ligature(int mask)
370 {
371   return mask & ligatures;
372 }
373
374 int 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
380 int 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
386 const char *font::get_name()
387 {
388   return name;
389 }
390
391 const char *font::get_internal_name()
392 {
393   return internalname;
394 }
395
396 const 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
402 const char *font::get_image_generator()
403 {
404   return image_generator;
405 }
406
407 void 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
431 void 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
445 void 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
468 void 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
481 void 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
489 font *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
499 static 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
512 int 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];
520 again:
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
567 int 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
810 static 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
826 int 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
1043 void font::handle_unknown_font_command(const char *, const char *,
1044                                        const char *, int)
1045 {
1046 }
1047
1048 FONT_COMMAND_HANDLER
1049 font::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 }