Imported Upstream version 1.22.4
[platform/upstream/groff.git] / src / libs / libgroff / font.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989-2018 Free Software Foundation, Inc.
3      Written by James Clark (jjc@jclark.com)
4
5 This file is part of groff.
6
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
19
20 #include "lib.h"
21
22 #include <ctype.h>
23 #include <assert.h>
24 #include <math.h>
25 #include <stdlib.h>
26 #include <wchar.h>
27 #include "errarg.h"
28 #include "error.h"
29 #include "cset.h"
30 #include "font.h"
31 #include "unicode.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   glyph *glyph1;
50   glyph *glyph2;
51   int amount;
52   font_kern_list *next;
53
54   font_kern_list(glyph *, glyph *, 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   free(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 int glyph_to_unicode(glyph *g)
150 {
151   const char *nm = glyph_to_name(g);
152   if (nm != NULL) {
153     // ASCII character?
154     if (nm[0] == 'c' && nm[1] == 'h' && nm[2] == 'a' && nm[3] == 'r'
155         && (nm[4] >= '0' && nm[4] <= '9')) {
156       int n = (nm[4] - '0');
157       if (nm[5] == '\0')
158         return n;
159       if (n > 0 && (nm[5] >= '0' && nm[5] <= '9')) {
160         n = 10*n + (nm[5] - '0');
161         if (nm[6] == '\0')
162           return n;
163         if (nm[6] >= '0' && nm[6] <= '9') {
164           n = 10*n + (nm[6] - '0');
165           if (nm[7] == '\0' && n < 128)
166             return n;
167         }
168       }
169     }
170     // Unicode character?
171     if (check_unicode_name(nm)) {
172       char *ignore;
173       return (int)strtol(nm + 1, &ignore, 16);
174     }
175     // If 'nm' is a single letter 'x', the glyph name is '\x'.
176     char buf[] = { '\\', '\0', '\0' };
177     if (nm[1] == '\0') {
178       buf[1] = nm[0];
179       nm = buf;
180     }
181     // groff glyphs that map to Unicode?
182     const char *unicode = glyph_name_to_unicode(nm);
183     if (unicode != NULL && strchr(unicode, '_') == NULL) {
184       char *ignore;
185       return (int)strtol(unicode, &ignore, 16);
186     }
187   }
188   return -1;
189 }
190
191 /* font functions */
192
193 font::font(const char *s)
194 : ligatures(0), kern_hash_table(0), space_width(0), special(0),
195   ch_index(0), nindices(0), ch(0), ch_used(0), ch_size(0), widths_cache(0)
196 {
197   name = new char[strlen(s) + 1];
198   strcpy(name, s);
199   internalname = 0;
200   slant = 0.0;
201   zoom = 0;
202   // load();                    // for testing
203 }
204
205 font::~font()
206 {
207   for (int i = 0; i < ch_used; i++)
208     if (ch[i].special_device_coding)
209       a_delete ch[i].special_device_coding;
210   a_delete ch;
211   a_delete ch_index;
212   if (kern_hash_table) {
213     for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) {
214       font_kern_list *kerns = kern_hash_table[i];
215       while (kerns) {
216         font_kern_list *tem = kerns;
217         kerns = kerns->next;
218         delete tem;
219       }
220     }
221     a_delete kern_hash_table;
222   }
223   a_delete name;
224   a_delete internalname;
225   while (widths_cache) {
226     font_widths_cache *tem = widths_cache;
227     widths_cache = widths_cache->next;
228     delete tem;
229   }
230 }
231
232 static int scale_round(int n, int x, int y)
233 {
234   assert(x >= 0 && y > 0);
235   int y2 = y/2;
236   if (x == 0)
237     return 0;
238   if (n >= 0) {
239     if (n <= (INT_MAX - y2) / x)
240       return (n * x + y2) / y;
241     return int(n * double(x) / double(y) + .5);
242   }
243   else {
244     if (-(unsigned)n <= (-(unsigned)INT_MIN - y2) / x)
245       return (n * x - y2) / y;
246     return int(n * double(x) / double(y) - .5);
247   }
248 }
249
250 static int scale_round(int n, int x, int y, int z)
251 {
252   assert(x >= 0 && y > 0 && z > 0);
253   if (x == 0)
254     return 0;
255   if (n >= 0)
256     return int((n * double(x) / double(y)) * (double(z) / 1000.0) + .5);
257   else
258     return int((n * double(x) / double(y)) * (double(z) / 1000.0) - .5);
259 }
260
261 inline int font::scale(int w, int sz)
262 {
263   if (zoom)
264     return scale_round(w, sz, unitwidth, zoom);
265   else
266     return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
267 }
268
269 int font::unit_scale(double *value, char unit)
270 {
271   // we scale everything to inch
272   double divisor = 0;
273   switch (unit) {
274   case 'i':
275     divisor = 1;
276     break;
277   case 'p':
278     divisor = 72;
279     break;
280   case 'P':
281     divisor = 6;
282     break;
283   case 'c':
284     divisor = 2.54;
285     break;
286   default:
287     assert(0);
288     break;
289   }
290   if (divisor) {
291     *value /= divisor;
292     return 1;
293   }
294   return 0;
295 }
296
297 int font::get_skew(glyph *g, int point_size, int sl)
298 {
299   int h = get_height(g, point_size);
300   return int(h * tan((slant + sl) * PI / 180.0) + .5);
301 }
302
303 int font::contains(glyph *g)
304 {
305   int idx = glyph_to_index(g);
306   assert(idx >= 0);
307   // Explicitly enumerated glyph?
308   if (idx < nindices && ch_index[idx] >= 0)
309     return 1;
310   if (is_unicode) {
311     // Unicode font
312     // ASCII or Unicode character, or groff glyph name that maps to Unicode?
313     if (glyph_to_unicode(g) >= 0)
314       return 1;
315     // Numbered character?
316     if (glyph_to_number(g) >= 0)
317       return 1;
318   }
319   return 0;
320 }
321
322 int font::is_special()
323 {
324   return special;
325 }
326
327 font_widths_cache::font_widths_cache(int ps, int ch_size,
328                                      font_widths_cache *p)
329 : next(p), point_size(ps)
330 {
331   width = new int[ch_size];
332   for (int i = 0; i < ch_size; i++)
333     width[i] = -1;
334 }
335
336 font_widths_cache::~font_widths_cache()
337 {
338   a_delete width;
339 }
340
341 int font::get_width(glyph *g, int point_size)
342 {
343   int idx = glyph_to_index(g);
344   assert(idx >= 0);
345   int real_size;
346   if (!zoom)
347     real_size = point_size;
348   else
349   {
350     if (point_size <= (INT_MAX - 500) / zoom)
351       real_size = (point_size * zoom + 500) / 1000;
352     else
353       real_size = int(point_size * double(zoom) / 1000.0 + .5);
354   }
355   if (idx < nindices && ch_index[idx] >= 0) {
356     // Explicitly enumerated glyph
357     int i = ch_index[idx];
358     if (real_size == unitwidth || font::unscaled_charwidths)
359       return ch[i].width;
360
361     if (!widths_cache)
362       widths_cache = new font_widths_cache(real_size, ch_size);
363     else if (widths_cache->point_size != real_size) {
364       font_widths_cache **p;
365       for (p = &widths_cache; *p; p = &(*p)->next)
366         if ((*p)->point_size == real_size)
367           break;
368       if (*p) {
369         font_widths_cache *tem = *p;
370         *p = (*p)->next;
371         tem->next = widths_cache;
372         widths_cache = tem;
373       }
374       else
375         widths_cache = new font_widths_cache(real_size, ch_size,
376                                              widths_cache);
377     }
378     int &w = widths_cache->width[i];
379     if (w < 0)
380       w = scale(ch[i].width, point_size);
381     return w;
382   }
383   if (is_unicode) {
384     // Unicode font
385     int width = 24; // XXX: Add a request to override this.
386     int w = wcwidth(get_code(g));
387     if (w > 1)
388       width *= w;
389     if (real_size == unitwidth || font::unscaled_charwidths)
390       return width;
391     else
392       return scale(width, point_size);
393   }
394   abort();
395 }
396
397 int font::get_height(glyph *g, int point_size)
398 {
399   int idx = glyph_to_index(g);
400   assert(idx >= 0);
401   if (idx < nindices && ch_index[idx] >= 0) {
402     // Explicitly enumerated glyph
403     return scale(ch[ch_index[idx]].height, point_size);
404   }
405   if (is_unicode) {
406     // Unicode font
407     return 0;
408   }
409   abort();
410 }
411
412 int font::get_depth(glyph *g, int point_size)
413 {
414   int idx = glyph_to_index(g);
415   assert(idx >= 0);
416   if (idx < nindices && ch_index[idx] >= 0) {
417     // Explicitly enumerated glyph
418     return scale(ch[ch_index[idx]].depth, point_size);
419   }
420   if (is_unicode) {
421     // Unicode font
422     return 0;
423   }
424   abort();
425 }
426
427 int font::get_italic_correction(glyph *g, int point_size)
428 {
429   int idx = glyph_to_index(g);
430   assert(idx >= 0);
431   if (idx < nindices && ch_index[idx] >= 0) {
432     // Explicitly enumerated glyph
433     return scale(ch[ch_index[idx]].italic_correction, point_size);
434   }
435   if (is_unicode) {
436     // Unicode font
437     return 0;
438   }
439   abort();
440 }
441
442 int font::get_left_italic_correction(glyph *g, int point_size)
443 {
444   int idx = glyph_to_index(g);
445   assert(idx >= 0);
446   if (idx < nindices && ch_index[idx] >= 0) {
447     // Explicitly enumerated glyph
448     return scale(ch[ch_index[idx]].pre_math_space, point_size);
449   }
450   if (is_unicode) {
451     // Unicode font
452     return 0;
453   }
454   abort();
455 }
456
457 int font::get_subscript_correction(glyph *g, int point_size)
458 {
459   int idx = glyph_to_index(g);
460   assert(idx >= 0);
461   if (idx < nindices && ch_index[idx] >= 0) {
462     // Explicitly enumerated glyph
463     return scale(ch[ch_index[idx]].subscript_correction, point_size);
464   }
465   if (is_unicode) {
466     // Unicode font
467     return 0;
468   }
469   abort();
470 }
471
472 void font::set_zoom(int factor)
473 {
474   assert(factor >= 0);
475   if (factor == 1000)
476     zoom = 0;
477   else
478     zoom = factor;
479 }
480
481 int font::get_zoom()
482 {
483   return zoom;
484 }
485
486 int font::get_space_width(int point_size)
487 {
488   return scale(space_width, point_size);
489 }
490
491 font_kern_list::font_kern_list(glyph *g1, glyph *g2, int n, font_kern_list *p)
492 : glyph1(g1), glyph2(g2), amount(n), next(p)
493 {
494 }
495
496 inline int font::hash_kern(glyph *g1, glyph *g2)
497 {
498   int n = ((glyph_to_index(g1) << 10) + glyph_to_index(g2))
499           % KERN_HASH_TABLE_SIZE;
500   return n < 0 ? -n : n;
501 }
502
503 void font::add_kern(glyph *g1, glyph *g2, int amount)
504 {
505   if (!kern_hash_table) {
506     kern_hash_table = new font_kern_list *[int(KERN_HASH_TABLE_SIZE)];
507     for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++)
508       kern_hash_table[i] = 0;
509   }
510   font_kern_list **p = kern_hash_table + hash_kern(g1, g2);
511   *p = new font_kern_list(g1, g2, amount, *p);
512 }
513
514 int font::get_kern(glyph *g1, glyph *g2, int point_size)
515 {
516   if (kern_hash_table) {
517     for (font_kern_list *p = kern_hash_table[hash_kern(g1, g2)]; p;
518          p = p->next)
519       if (g1 == p->glyph1 && g2 == p->glyph2)
520         return scale(p->amount, point_size);
521   }
522   return 0;
523 }
524
525 int font::has_ligature(int mask)
526 {
527   return mask & ligatures;
528 }
529
530 int font::get_character_type(glyph *g)
531 {
532   int idx = glyph_to_index(g);
533   assert(idx >= 0);
534   if (idx < nindices && ch_index[idx] >= 0) {
535     // Explicitly enumerated glyph
536     return ch[ch_index[idx]].type;
537   }
538   if (is_unicode) {
539     // Unicode font
540     return 0;
541   }
542   abort();
543 }
544
545 int font::get_code(glyph *g)
546 {
547   int idx = glyph_to_index(g);
548   assert(idx >= 0);
549   if (idx < nindices && ch_index[idx] >= 0) {
550     // Explicitly enumerated glyph
551     return ch[ch_index[idx]].code;
552   }
553   if (is_unicode) {
554     // Unicode font
555     // ASCII or Unicode character, or groff glyph name that maps to Unicode?
556     int uni = glyph_to_unicode(g);
557     if (uni >= 0)
558       return uni;
559     // Numbered character?
560     int n = glyph_to_number(g);
561     if (n >= 0)
562       return n;
563   }
564   // The caller must check 'contains(g)' before calling get_code(g).
565   abort();
566 }
567
568 const char *font::get_name()
569 {
570   return name;
571 }
572
573 const char *font::get_internal_name()
574 {
575   return internalname;
576 }
577
578 const char *font::get_special_device_encoding(glyph *g)
579 {
580   int idx = glyph_to_index(g);
581   assert(idx >= 0);
582   if (idx < nindices && ch_index[idx] >= 0) {
583     // Explicitly enumerated glyph
584     return ch[ch_index[idx]].special_device_coding;
585   }
586   if (is_unicode) {
587     // Unicode font
588     return NULL;
589   }
590   abort();
591 }
592
593 const char *font::get_image_generator()
594 {
595   return image_generator;
596 }
597
598 void font::alloc_ch_index(int idx)
599 {
600   if (nindices == 0) {
601     nindices = 128;
602     if (idx >= nindices)
603       nindices = idx + 10;
604     ch_index = new int[nindices];
605     for (int i = 0; i < nindices; i++)
606       ch_index[i] = -1;
607   }
608   else {
609     int old_nindices = nindices;
610     nindices *= 2;
611     if (idx >= nindices)
612       nindices = idx + 10;
613     int *old_ch_index = ch_index;
614     ch_index = new int[nindices];
615     memcpy(ch_index, old_ch_index, sizeof(int)*old_nindices);
616     for (int i = old_nindices; i < nindices; i++)
617       ch_index[i] = -1;
618     a_delete old_ch_index;
619   }
620 }
621
622 void font::extend_ch()
623 {
624   if (ch == 0)
625     ch = new font_char_metric[ch_size = 16];
626   else {
627     int old_ch_size = ch_size;
628     ch_size *= 2;
629     font_char_metric *old_ch = ch;
630     ch = new font_char_metric[ch_size];
631     memcpy(ch, old_ch, old_ch_size*sizeof(font_char_metric));
632     a_delete old_ch;
633   }
634 }
635
636 void font::compact()
637 {
638   int i;
639   for (i = nindices - 1; i >= 0; i--)
640     if (ch_index[i] >= 0)
641       break;
642   i++;
643   if (i < nindices) {
644     int *old_ch_index = ch_index;
645     ch_index = new int[i];
646     memcpy(ch_index, old_ch_index, i*sizeof(int));
647     a_delete old_ch_index;
648     nindices = i;
649   }
650   if (ch_used < ch_size) {
651     font_char_metric *old_ch = ch;
652     ch = new font_char_metric[ch_used];
653     memcpy(ch, old_ch, ch_used*sizeof(font_char_metric));
654     a_delete old_ch;
655     ch_size = ch_used;
656   }
657 }
658
659 void font::add_entry(glyph *g, const font_char_metric &metric)
660 {
661   int idx = glyph_to_index(g);
662   assert(idx >= 0);
663   if (idx >= nindices)
664     alloc_ch_index(idx);
665   assert(idx < nindices);
666   if (ch_used + 1 >= ch_size)
667     extend_ch();
668   assert(ch_used + 1 < ch_size);
669   ch_index[idx] = ch_used;
670   ch[ch_used++] = metric;
671 }
672
673 void font::copy_entry(glyph *new_glyph, glyph *old_glyph)
674 {
675   int new_index = glyph_to_index(new_glyph);
676   int old_index = glyph_to_index(old_glyph);
677   assert(new_index >= 0 && old_index >= 0 && old_index < nindices);
678   if (new_index >= nindices)
679     alloc_ch_index(new_index);
680   ch_index[new_index] = ch_index[old_index];
681 }
682
683 font *font::load_font(const char *s, int *not_found, int head_only)
684 {
685   font *f = new font(s);
686   if (!f->load(not_found, head_only)) {
687     delete f;
688     return 0;
689   }
690   return f;
691 }
692
693 static char *trim_arg(char *p)
694 {
695   if (!p)
696     return 0;
697   while (csspace(*p))
698     p++;
699   char *q = strchr(p, '\0');
700   while (q > p && csspace(q[-1]))
701     q--;
702   *q = '\0';
703   return p;
704 }
705
706 int font::scan_papersize(const char *p,
707                          const char **size, double *length, double *width)
708 {
709   double l, w;
710   char lu[2], wu[2];
711   const char *pp = p;
712   int test_file = 1;
713   char line[255];
714 again:
715   if (csdigit(*pp)) {
716     if (sscanf(pp, "%lf%1[ipPc],%lf%1[ipPc]", &l, lu, &w, wu) == 4
717         && l > 0 && w > 0
718         && unit_scale(&l, lu[0]) && unit_scale(&w, wu[0])) {
719       if (length)
720         *length = l;
721       if (width)
722         *width = w;
723       if (size)
724         *size = "custom";
725       return 1;
726     }
727   }
728   else {
729     int i;
730     for (i = 0; i < NUM_PAPERSIZES; i++)
731       if (strcasecmp(papersizes[i].name, pp) == 0) {
732         if (length)
733           *length = papersizes[i].length;
734         if (width)
735           *width = papersizes[i].width;
736         if (size)
737           *size = papersizes[i].name;
738         return 1;
739       }
740     if (test_file) {
741       FILE *f = fopen(p, "r");
742       if (f) {
743         fgets(line, 254, f);
744         fclose(f);
745         test_file = 0;
746         char *linep = strchr(line, '\0');
747         // skip final newline, if any
748         if (*(--linep) == '\n')
749           *linep = '\0';
750         pp = line;
751         goto again;
752       }
753     }
754   }
755   return 0;
756 }
757
758 // If the font can't be found, then if not_found is non-NULL, it will be set
759 // to 1 otherwise a message will be printed.
760
761 int font::load(int *not_found, int head_only)
762 {
763   if (strcmp(name, "DESC") == 0) {
764     if (not_found)
765       *not_found = 1;
766     else
767       error("'DESC' is not a valid font file name");
768     return 0;
769   }
770   char *path;
771   FILE *fp;
772   if ((fp = open_file(name, &path)) == NULL) {
773     if (not_found)
774       *not_found = 1;
775     else
776       error("can't find font file '%1'", name);
777     return 0;
778   }
779   text_file t(fp, path);
780   t.skip_comments = 1;
781   t.silent = head_only;
782   char *p;
783   for (;;) {
784     if (!t.next()) {
785       p = 0;
786       break;
787     }
788     p = strtok(t.buf, WS);
789     if (strcmp(p, "name") == 0) {
790     }
791     else if (strcmp(p, "spacewidth") == 0) {
792       p = strtok(0, WS);
793       int n;
794       if (p == 0 || sscanf(p, "%d", &n) != 1 || n <= 0) {
795         t.error("bad argument for 'spacewidth' command");
796         return 0;
797       }
798       space_width = n;
799     }
800     else if (strcmp(p, "slant") == 0) {
801       p = strtok(0, WS);
802       double n;
803       if (p == 0 || sscanf(p, "%lf", &n) != 1 || n >= 90.0 || n <= -90.0) {
804         t.error("bad argument for 'slant' command", p);
805         return 0;
806       }
807       slant = n;
808     }
809     else if (strcmp(p, "ligatures") == 0) {
810       for (;;) {
811         p = strtok(0, WS);
812         if (p == 0 || strcmp(p, "0") == 0)
813           break;
814         if (strcmp(p, "ff") == 0)
815           ligatures |= LIG_ff;
816         else if (strcmp(p, "fi") == 0)
817           ligatures |= LIG_fi;
818         else if (strcmp(p, "fl") == 0)
819           ligatures |= LIG_fl;
820         else if (strcmp(p, "ffi") == 0)
821           ligatures |= LIG_ffi;
822         else if (strcmp(p, "ffl") == 0)
823           ligatures |= LIG_ffl;
824         else {
825           t.error("unrecognised ligature '%1'", p);
826           return 0;
827         }
828       }
829     }
830     else if (strcmp(p, "internalname") == 0) {
831       p = strtok(0, WS);
832       if (!p) {
833         t.error("'internalname' command requires argument");
834         return 0;
835       }
836       internalname = new char[strlen(p) + 1];
837       strcpy(internalname, p);
838     }
839     else if (strcmp(p, "special") == 0) {
840       special = 1;
841     }
842     else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) {
843       char *command = p;
844       p = strtok(0, "\n");
845       handle_unknown_font_command(command, trim_arg(p), t.path, t.lineno);
846     }
847     else
848       break;
849   }
850   int had_charset = 0;
851   if (p == 0) {
852     if (!is_unicode) {
853       t.error("missing charset command");
854       return 0;
855     }
856   } else {
857     char *command = p;
858     t.skip_comments = 0;
859     while (command) {
860       if (strcmp(command, "kernpairs") == 0) {
861         if (head_only)
862           return 1;
863         for (;;) {
864           if (!t.next()) {
865             command = 0;
866             break;
867           }
868           char *c1 = strtok(t.buf, WS);
869           if (c1 == 0)
870             continue;
871           char *c2 = strtok(0, WS);
872           if (c2 == 0) {
873             command = c1;
874             break;
875           }
876           p = strtok(0, WS);
877           if (p == 0) {
878             t.error("missing kern amount");
879             return 0;
880           }
881           int n;
882           if (sscanf(p, "%d", &n) != 1) {
883             t.error("bad kern amount '%1'", p);
884             return 0;
885           }
886           glyph *g1 = name_to_glyph(c1);
887           glyph *g2 = name_to_glyph(c2);
888           add_kern(g1, g2, n);
889         }
890       }
891       else if (strcmp(command, "charset") == 0) {
892         if (head_only)
893           return 1;
894         had_charset = 1;
895         glyph *last_glyph = NULL;
896         for (;;) {
897           if (!t.next()) {
898             command = 0;
899             break;
900           }
901           char *nm = strtok(t.buf, WS);
902           if (nm == 0)
903             continue;                   // I dont think this should happen
904           p = strtok(0, WS);
905           if (p == 0) {
906             command = nm;
907             break;
908           }
909           if (p[0] == '"') {
910             if (last_glyph == NULL) {
911               t.error("first charset entry is duplicate");
912               return 0;
913             }
914             if (strcmp(nm, "---") == 0) {
915               t.error("unnamed character cannot be duplicate");
916               return 0;
917             }
918             glyph *g = name_to_glyph(nm);
919             copy_entry(g, last_glyph);
920           }
921           else {
922             font_char_metric metric;
923             metric.height = 0;
924             metric.depth = 0;
925             metric.pre_math_space = 0;
926             metric.italic_correction = 0;
927             metric.subscript_correction = 0;
928             int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
929                                 &metric.width, &metric.height, &metric.depth,
930                                 &metric.italic_correction,
931                                 &metric.pre_math_space,
932                                 &metric.subscript_correction);
933             if (nparms < 1) {
934               t.error("bad width for '%1'", nm);
935               return 0;
936             }
937             p = strtok(0, WS);
938             if (p == 0) {
939               t.error("missing character type for '%1'", nm);
940               return 0;
941             }
942             int type;
943             if (sscanf(p, "%d", &type) != 1) {
944               t.error("bad character type for '%1'", nm);
945               return 0;
946             }
947             if (type < 0 || type > 255) {
948               t.error("character type '%1' out of range", type);
949               return 0;
950             }
951             metric.type = type;
952             p = strtok(0, WS);
953             if (p == 0) {
954               t.error("missing code for '%1'", nm);
955               return 0;
956             }
957             char *ptr;
958             metric.code = (int)strtol(p, &ptr, 0);
959             if (metric.code == 0 && ptr == p) {
960               t.error("bad code '%1' for character '%2'", p, nm);
961               return 0;
962             }
963             if (is_unicode) {
964               int w = wcwidth(metric.code);
965               if (w > 1)
966                 metric.width *= w;
967             }
968             p = strtok(0, WS);
969             if ((p == NULL) || (strcmp(p, "--") == 0)) {
970               metric.special_device_coding = NULL;
971             }
972             else {
973               char *nam = new char[strlen(p) + 1];
974               strcpy(nam, p);
975               metric.special_device_coding = nam;
976             }
977             if (strcmp(nm, "---") == 0) {
978               last_glyph = number_to_glyph(metric.code);
979               add_entry(last_glyph, metric);
980             }
981             else {
982               last_glyph = name_to_glyph(nm);
983               add_entry(last_glyph, metric);
984               copy_entry(number_to_glyph(metric.code), last_glyph);
985             }
986           }
987         }
988         if (last_glyph == NULL) {
989           t.error("I didn't seem to find any characters");
990           return 0;
991         }
992       }
993       else {
994         t.error("unrecognised command '%1' "
995                 "after 'kernpairs' or 'charset' command",
996                   command);
997         return 0;
998       }
999     }
1000     compact();
1001   }
1002   if (!is_unicode && !had_charset) {
1003     t.error("missing 'charset' command");
1004     return 0;
1005   }
1006   if (space_width == 0) {
1007     if (zoom)
1008       space_width = scale_round(unitwidth, res, 72 * 3 * sizescale, zoom);
1009     else
1010       space_width = scale_round(unitwidth, res, 72 * 3 * sizescale);
1011   }
1012   return 1;
1013 }
1014
1015 static struct {
1016   const char *command;
1017   int *ptr;
1018 } table[] = {
1019   { "res", &font::res },
1020   { "hor", &font::hor },
1021   { "vert", &font::vert },
1022   { "unitwidth", &font::unitwidth },
1023   { "paperwidth", &font::paperwidth },
1024   { "paperlength", &font::paperlength },
1025   { "spare1", &font::biggestfont },
1026   { "biggestfont", &font::biggestfont },
1027   { "spare2", &font::spare2 },
1028   { "sizescale", &font::sizescale },
1029   };
1030
1031 int font::load_desc()
1032 {
1033   int nfonts = 0;
1034   FILE *fp;
1035   char *path;
1036   if ((fp = open_file("DESC", &path)) == 0) {
1037     error("can't find 'DESC' file");
1038     return 0;
1039   }
1040   text_file t(fp, path);
1041   t.skip_comments = 1;
1042   res = 0;
1043   while (t.next()) {
1044     char *p = strtok(t.buf, WS);
1045     int found = 0;
1046     unsigned int idx;
1047     for (idx = 0; !found && idx < sizeof(table)/sizeof(table[0]); idx++)
1048       if (strcmp(table[idx].command, p) == 0)
1049         found = 1;
1050     if (found) {
1051       char *q = strtok(0, WS);
1052       if (!q) {
1053         t.error("missing value for command '%1'", p);
1054         return 0;
1055       }
1056       //int *ptr = &(this->*(table[idx-1].ptr));
1057       int *ptr = table[idx-1].ptr;
1058       if (sscanf(q, "%d", ptr) != 1) {
1059         t.error("bad number '%1'", q);
1060         return 0;
1061       }
1062     }
1063     else if (strcmp("family", p) == 0) {
1064       p = strtok(0, WS);
1065       if (!p) {
1066         t.error("family command requires an argument");
1067         return 0;
1068       }
1069       char *tem = new char[strlen(p)+1];
1070       strcpy(tem, p);
1071       family = tem;
1072     }
1073     else if (strcmp("fonts", p) == 0) {
1074       p = strtok(0, WS);
1075       if (!p || sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
1076         t.error("bad number of fonts '%1'", p);
1077         return 0;
1078       }
1079       font_name_table = (const char **)new char *[nfonts+1]; 
1080       for (int i = 0; i < nfonts; i++) {
1081         p = strtok(0, WS);
1082         while (p == 0) {
1083           if (!t.next()) {
1084             t.error("end of file while reading list of fonts");
1085             return 0;
1086           }
1087           p = strtok(t.buf, WS);
1088         }
1089         char *temp = new char[strlen(p)+1];
1090         strcpy(temp, p);
1091         font_name_table[i] = temp;
1092       }
1093       p = strtok(0, WS);
1094       if (p != 0) {
1095         t.error("font count does not match number of fonts");
1096         return 0;
1097       }
1098       font_name_table[nfonts] = 0;
1099     }
1100     else if (strcmp("papersize", p) == 0) {
1101       p = strtok(0, WS);
1102       if (!p) {
1103         t.error("papersize command requires an argument");
1104         return 0;
1105       }
1106       int found_paper = 0;
1107       while (p) {
1108         double unscaled_paperwidth, unscaled_paperlength;
1109         if (scan_papersize(p, &papersize, &unscaled_paperlength,
1110                            &unscaled_paperwidth)) {
1111           paperwidth = int(unscaled_paperwidth * res + 0.5);
1112           paperlength = int(unscaled_paperlength * res + 0.5);
1113           found_paper = 1;
1114           break;
1115         }
1116         p = strtok(0, WS);
1117       }
1118       if (!found_paper) {
1119         t.error("bad paper size");
1120         return 0;
1121       }
1122     }
1123     else if (strcmp("unscaled_charwidths", p) == 0)
1124       unscaled_charwidths = 1;
1125     else if (strcmp("pass_filenames", p) == 0)
1126       pass_filenames = 1;
1127     else if (strcmp("sizes", p) == 0) {
1128       int n = 16;
1129       sizes = new int[n];
1130       int i = 0;
1131       for (;;) {
1132         p = strtok(0, WS);
1133         while (p == 0) {
1134           if (!t.next()) {
1135             t.error("list of sizes must be terminated by '0'");
1136             return 0;
1137           }
1138           p = strtok(t.buf, WS);
1139         }
1140         int lower, upper;
1141         switch (sscanf(p, "%d-%d", &lower, &upper)) {
1142         case 1:
1143           upper = lower;
1144           // fall through
1145         case 2:
1146           if (lower <= upper && lower >= 0)
1147             break;
1148           // fall through
1149         default:
1150           t.error("bad size range '%1'", p);
1151           return 0;
1152         }
1153         if (i + 2 > n) {
1154           int *old_sizes = sizes;
1155           sizes = new int[n*2];
1156           memcpy(sizes, old_sizes, n*sizeof(int));
1157           n *= 2;
1158           a_delete old_sizes;
1159         }
1160         sizes[i++] = lower;
1161         if (lower == 0)
1162           break;
1163         sizes[i++] = upper;
1164       }
1165       if (i == 1) {
1166         t.error("must have some sizes");
1167         return 0;
1168       }
1169     }
1170     else if (strcmp("styles", p) == 0) {
1171       int style_table_size = 5;
1172       style_table = (const char **)new char *[style_table_size];
1173       int j;
1174       for (j = 0; j < style_table_size; j++)
1175         style_table[j] = 0;
1176       int i = 0;
1177       for (;;) {
1178         p = strtok(0, WS);
1179         if (p == 0)
1180           break;
1181         // leave room for terminating 0
1182         if (i + 1 >= style_table_size) {
1183           const char **old_style_table = style_table;
1184           style_table_size *= 2;
1185           style_table = (const char **)new char*[style_table_size];
1186           for (j = 0; j < i; j++)
1187             style_table[j] = old_style_table[j];
1188           for (; j < style_table_size; j++)
1189             style_table[j] = 0;
1190           a_delete old_style_table;
1191         }
1192         char *tem = new char[strlen(p) + 1];
1193         strcpy(tem, p);
1194         style_table[i++] = tem;
1195       }
1196     }
1197     else if (strcmp("tcommand", p) == 0)
1198       tcommand = 1;
1199     else if (strcmp("use_charnames_in_special", p) == 0)
1200       use_charnames_in_special = 1;
1201     else if (strcmp("unicode", p) == 0)
1202       is_unicode = 1;
1203     else if (strcmp("image_generator", p) == 0) {
1204       p = strtok(0, WS);
1205       if (!p) {
1206         t.error("image_generator command requires an argument");
1207         return 0;
1208       }
1209       image_generator = strsave(p);
1210     }
1211     else if (strcmp("charset", p) == 0)
1212       break;
1213     else if (unknown_desc_command_handler) {
1214       char *command = p;
1215       p = strtok(0, "\n");
1216       (*unknown_desc_command_handler)(command, trim_arg(p), t.path, t.lineno);
1217     }
1218   }
1219   if (res == 0) {
1220     t.error("missing 'res' command");
1221     return 0;
1222   }
1223   if (unitwidth == 0) {
1224     t.error("missing 'unitwidth' command");
1225     return 0;
1226   }
1227   if (font_name_table == 0) {
1228     t.error("missing 'fonts' command");
1229     return 0;
1230   }
1231   if (sizes == 0) {
1232     t.error("missing 'sizes' command");
1233     return 0;
1234   }
1235   if (sizescale < 1) {
1236     t.error("bad 'sizescale' value");
1237     return 0;
1238   }
1239   if (hor < 1) {
1240     t.error("bad 'hor' value");
1241     return 0;
1242   }
1243   if (vert < 1) {
1244     t.error("bad 'vert' value");
1245     return 0;
1246   }
1247   return 1;
1248 }      
1249
1250 void font::handle_unknown_font_command(const char *, const char *,
1251                                        const char *, int)
1252 {
1253 }
1254
1255 FONT_COMMAND_HANDLER
1256 font::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func)
1257 {
1258   FONT_COMMAND_HANDLER prev = unknown_desc_command_handler;
1259   unknown_desc_command_handler = func;
1260   return prev;
1261 }