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