xref: /netbsd-src/external/gpl2/groff/dist/src/libs/libgroff/font.cpp (revision 89a07cf815a29524268025a1139fac4c5190f765)
1 /*	$NetBSD: font.cpp,v 1.1.1.1 2016/01/13 18:41:48 christos Exp $	*/
2 
3 // -*- C++ -*-
4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
5    Free Software Foundation, Inc.
6      Written by James Clark (jjc@jclark.com)
7 
8 This file is part of groff.
9 
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
13 version.
14 
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18 for more details.
19 
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING.  If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23 
24 #include "lib.h"
25 
26 #include <ctype.h>
27 #include <assert.h>
28 #include <math.h>
29 #include <stdlib.h>
30 #include "errarg.h"
31 #include "error.h"
32 #include "cset.h"
33 #include "font.h"
34 #include "paper.h"
35 
36 const char *const WS = " \t\n\r";
37 
38 struct font_char_metric {
39   char type;
40   int code;
41   int width;
42   int height;
43   int depth;
44   int pre_math_space;
45   int italic_correction;
46   int subscript_correction;
47   char *special_device_coding;
48 };
49 
50 struct font_kern_list {
51   int i1;
52   int i2;
53   int amount;
54   font_kern_list *next;
55 
56   font_kern_list(int, int, int, font_kern_list * = 0);
57 };
58 
59 struct font_widths_cache {
60   font_widths_cache *next;
61   int point_size;
62   int *width;
63 
64   font_widths_cache(int, int, font_widths_cache * = 0);
65   ~font_widths_cache();
66 };
67 
68 /* text_file */
69 
70 struct text_file {
71   FILE *fp;
72   char *path;
73   int lineno;
74   int size;
75   int skip_comments;
76   int silent;
77   char *buf;
78   text_file(FILE *fp, char *p);
79   ~text_file();
80   int next();
81   void error(const char *format,
82 	     const errarg &arg1 = empty_errarg,
83 	     const errarg &arg2 = empty_errarg,
84 	     const errarg &arg3 = empty_errarg);
85 };
86 
text_file(FILE * p,char * s)87 text_file::text_file(FILE *p, char *s)
88 : fp(p), path(s), lineno(0), size(0), skip_comments(1), silent(0), buf(0)
89 {
90 }
91 
~text_file()92 text_file::~text_file()
93 {
94   a_delete buf;
95   a_delete path;
96   if (fp)
97     fclose(fp);
98 }
99 
next()100 int text_file::next()
101 {
102   if (fp == 0)
103     return 0;
104   if (buf == 0) {
105     buf = new char[128];
106     size = 128;
107   }
108   for (;;) {
109     int i = 0;
110     for (;;) {
111       int c = getc(fp);
112       if (c == EOF)
113 	break;
114       if (invalid_input_char(c))
115 	error("invalid input character code `%1'", int(c));
116       else {
117 	if (i + 1 >= size) {
118 	  char *old_buf = buf;
119 	  buf = new char[size*2];
120 	  memcpy(buf, old_buf, size);
121 	  a_delete old_buf;
122 	  size *= 2;
123 	}
124 	buf[i++] = c;
125 	if (c == '\n')
126 	  break;
127       }
128     }
129     if (i == 0)
130       break;
131     buf[i] = '\0';
132     lineno++;
133     char *ptr = buf;
134     while (csspace(*ptr))
135       ptr++;
136     if (*ptr != 0 && (!skip_comments || *ptr != '#'))
137       return 1;
138   }
139   return 0;
140 }
141 
error(const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)142 void text_file::error(const char *format,
143 		      const errarg &arg1,
144 		      const errarg &arg2,
145 		      const errarg &arg3)
146 {
147   if (!silent)
148     error_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
149 }
150 
151 
152 /* font functions */
153 
font(const char * s)154 font::font(const char *s)
155 : ligatures(0), kern_hash_table(0), space_width(0), ch_index(0), nindices(0),
156   ch(0), ch_used(0), ch_size(0), special(0), widths_cache(0)
157 {
158   name = new char[strlen(s) + 1];
159   strcpy(name, s);
160   internalname = 0;
161   slant = 0.0;
162   // load();			// for testing
163 }
164 
~font()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 
scale_round(int n,int x,int y)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 
scale(int w,int sz)210 inline int font::scale(int w, int sz)
211 {
212   return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
213 }
214 
unit_scale(double * value,char unit)215 int font::unit_scale(double *value, char unit)
216 {
217   // we scale everything to inch
218   double divisor = 0;
219   switch (unit) {
220   case 'i':
221     divisor = 1;
222     break;
223   case 'p':
224     divisor = 72;
225     break;
226   case 'P':
227     divisor = 6;
228     break;
229   case 'c':
230     divisor = 2.54;
231     break;
232   default:
233     assert(0);
234     break;
235   }
236   if (divisor) {
237     *value /= divisor;
238     return 1;
239   }
240   return 0;
241 }
242 
get_skew(int c,int point_size,int sl)243 int font::get_skew(int c, int point_size, int sl)
244 {
245   int h = get_height(c, point_size);
246   return int(h*tan((slant+sl)*PI/180.0) + .5);
247 }
248 
contains(int c)249 int font::contains(int c)
250 {
251   return c >= 0 && c < nindices && ch_index[c] >= 0;
252 }
253 
is_special()254 int font::is_special()
255 {
256   return special;
257 }
258 
font_widths_cache(int ps,int ch_size,font_widths_cache * p)259 font_widths_cache::font_widths_cache(int ps, int ch_size,
260 				     font_widths_cache *p)
261 : next(p), point_size(ps)
262 {
263   width = new int[ch_size];
264   for (int i = 0; i < ch_size; i++)
265     width[i] = -1;
266 }
267 
~font_widths_cache()268 font_widths_cache::~font_widths_cache()
269 {
270   a_delete width;
271 }
272 
get_width(int c,int point_size)273 int font::get_width(int c, int point_size)
274 {
275   assert(c >= 0 && c < nindices);
276   int i = ch_index[c];
277   assert(i >= 0);
278 
279   if (point_size == unitwidth || font::unscaled_charwidths)
280     return ch[i].width;
281 
282   if (!widths_cache)
283     widths_cache = new font_widths_cache(point_size, ch_size);
284   else if (widths_cache->point_size != point_size) {
285     font_widths_cache **p;
286     for (p = &widths_cache; *p; p = &(*p)->next)
287       if ((*p)->point_size == point_size)
288 	break;
289     if (*p) {
290       font_widths_cache *tem = *p;
291       *p = (*p)->next;
292       tem->next = widths_cache;
293       widths_cache = tem;
294     }
295     else
296       widths_cache = new font_widths_cache(point_size, ch_size, widths_cache);
297   }
298   int &w = widths_cache->width[i];
299   if (w < 0)
300     w = scale(ch[i].width, point_size);
301   return w;
302 }
303 
get_height(int c,int point_size)304 int font::get_height(int c, int point_size)
305 {
306   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
307   return scale(ch[ch_index[c]].height, point_size);
308 }
309 
get_depth(int c,int point_size)310 int font::get_depth(int c, int point_size)
311 {
312   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
313   return scale(ch[ch_index[c]].depth, point_size);
314 }
315 
get_italic_correction(int c,int point_size)316 int font::get_italic_correction(int c, int point_size)
317 {
318   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
319   return scale(ch[ch_index[c]].italic_correction, point_size);
320 }
321 
get_left_italic_correction(int c,int point_size)322 int font::get_left_italic_correction(int c, int point_size)
323 {
324   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
325   return scale(ch[ch_index[c]].pre_math_space, point_size);
326 }
327 
get_subscript_correction(int c,int point_size)328 int font::get_subscript_correction(int c, int point_size)
329 {
330   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
331   return scale(ch[ch_index[c]].subscript_correction, point_size);
332 }
333 
get_space_width(int point_size)334 int font::get_space_width(int point_size)
335 {
336   return scale(space_width, point_size);
337 }
338 
font_kern_list(int c1,int c2,int n,font_kern_list * p)339 font_kern_list::font_kern_list(int c1, int c2, int n, font_kern_list *p)
340 : i1(c1), i2(c2), amount(n), next(p)
341 {
342 }
343 
hash_kern(int i1,int i2)344 inline int font::hash_kern(int i1, int i2)
345 {
346   int n = ((i1 << 10) + i2) % KERN_HASH_TABLE_SIZE;
347   return n < 0 ? -n : n;
348 }
349 
add_kern(int i1,int i2,int amount)350 void font::add_kern(int i1, int i2, int amount)
351 {
352   if (!kern_hash_table) {
353     kern_hash_table = new font_kern_list *[int(KERN_HASH_TABLE_SIZE)];
354     for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++)
355       kern_hash_table[i] = 0;
356   }
357   font_kern_list **p = kern_hash_table + hash_kern(i1, i2);
358   *p = new font_kern_list(i1, i2, amount, *p);
359 }
360 
get_kern(int i1,int i2,int point_size)361 int font::get_kern(int i1, int i2, int point_size)
362 {
363   if (kern_hash_table) {
364     for (font_kern_list *p = kern_hash_table[hash_kern(i1, i2)]; p; p = p->next)
365       if (i1 == p->i1 && i2 == p->i2)
366 	return scale(p->amount, point_size);
367   }
368   return 0;
369 }
370 
has_ligature(int mask)371 int font::has_ligature(int mask)
372 {
373   return mask & ligatures;
374 }
375 
get_character_type(int c)376 int font::get_character_type(int c)
377 {
378   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
379   return ch[ch_index[c]].type;
380 }
381 
get_code(int c)382 int font::get_code(int c)
383 {
384   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
385   return ch[ch_index[c]].code;
386 }
387 
get_name()388 const char *font::get_name()
389 {
390   return name;
391 }
392 
get_internal_name()393 const char *font::get_internal_name()
394 {
395   return internalname;
396 }
397 
get_special_device_encoding(int c)398 const char *font::get_special_device_encoding(int c)
399 {
400   assert(c >= 0 && c < nindices && ch_index[c] >= 0);
401   return ch[ch_index[c]].special_device_coding;
402 }
403 
get_image_generator()404 const char *font::get_image_generator()
405 {
406   return image_generator;
407 }
408 
alloc_ch_index(int idx)409 void font::alloc_ch_index(int idx)
410 {
411   if (nindices == 0) {
412     nindices = 128;
413     if (idx >= nindices)
414       nindices = idx + 10;
415     ch_index = new int[nindices];
416     for (int i = 0; i < nindices; i++)
417       ch_index[i] = -1;
418   }
419   else {
420     int old_nindices = nindices;
421     nindices *= 2;
422     if (idx >= nindices)
423       nindices = idx + 10;
424     int *old_ch_index = ch_index;
425     ch_index = new int[nindices];
426     memcpy(ch_index, old_ch_index, sizeof(int)*old_nindices);
427     for (int i = old_nindices; i < nindices; i++)
428       ch_index[i] = -1;
429     a_delete old_ch_index;
430   }
431 }
432 
extend_ch()433 void font::extend_ch()
434 {
435   if (ch == 0)
436     ch = new font_char_metric[ch_size = 16];
437   else {
438     int old_ch_size = ch_size;
439     ch_size *= 2;
440     font_char_metric *old_ch = ch;
441     ch = new font_char_metric[ch_size];
442     memcpy(ch, old_ch, old_ch_size*sizeof(font_char_metric));
443     a_delete old_ch;
444   }
445 }
446 
compact()447 void font::compact()
448 {
449   int i;
450   for (i = nindices - 1; i >= 0; i--)
451     if (ch_index[i] >= 0)
452       break;
453   i++;
454   if (i < nindices) {
455     int *old_ch_index = ch_index;
456     ch_index = new int[i];
457     memcpy(ch_index, old_ch_index, i*sizeof(int));
458     a_delete old_ch_index;
459     nindices = i;
460   }
461   if (ch_used < ch_size) {
462     font_char_metric *old_ch = ch;
463     ch = new font_char_metric[ch_used];
464     memcpy(ch, old_ch, ch_used*sizeof(font_char_metric));
465     a_delete old_ch;
466     ch_size = ch_used;
467   }
468 }
469 
add_entry(int idx,const font_char_metric & metric)470 void font::add_entry(int idx, const font_char_metric &metric)
471 {
472   assert(idx >= 0);
473   if (idx >= nindices)
474     alloc_ch_index(idx);
475   assert(idx < nindices);
476   if (ch_used + 1 >= ch_size)
477     extend_ch();
478   assert(ch_used + 1 < ch_size);
479   ch_index[idx] = ch_used;
480   ch[ch_used++] = metric;
481 }
482 
copy_entry(int new_index,int old_index)483 void font::copy_entry(int new_index, int old_index)
484 {
485   assert(new_index >= 0 && old_index >= 0 && old_index < nindices);
486   if (new_index >= nindices)
487     alloc_ch_index(new_index);
488   ch_index[new_index] = ch_index[old_index];
489 }
490 
load_font(const char * s,int * not_found,int head_only)491 font *font::load_font(const char *s, int *not_found, int head_only)
492 {
493   font *f = new font(s);
494   if (!f->load(not_found, head_only)) {
495     delete f;
496     return 0;
497   }
498   return f;
499 }
500 
trim_arg(char * p)501 static char *trim_arg(char *p)
502 {
503   if (!p)
504     return 0;
505   while (csspace(*p))
506     p++;
507   char *q = strchr(p, '\0');
508   while (q > p && csspace(q[-1]))
509     q--;
510   *q = '\0';
511   return p;
512 }
513 
scan_papersize(const char * p,const char ** size,double * length,double * width)514 int font::scan_papersize(const char *p,
515 			 const char **size, double *length, double *width)
516 {
517   double l, w;
518   char lu[2], wu[2];
519   const char *pp = p;
520   int test_file = 1;
521   char line[255];
522 again:
523   if (csdigit(*pp)) {
524     if (sscanf(pp, "%lf%1[ipPc],%lf%1[ipPc]", &l, lu, &w, wu) == 4
525 	&& l > 0 && w > 0
526 	&& unit_scale(&l, lu[0]) && unit_scale(&w, wu[0])) {
527       if (length)
528 	*length = l;
529       if (width)
530 	*width = w;
531       if (size)
532 	*size = "custom";
533       return 1;
534     }
535   }
536   else {
537     int i;
538     for (i = 0; i < NUM_PAPERSIZES; i++)
539       if (strcasecmp(papersizes[i].name, pp) == 0) {
540 	if (length)
541 	  *length = papersizes[i].length;
542 	if (width)
543 	  *width = papersizes[i].width;
544 	if (size)
545 	  *size = papersizes[i].name;
546 	return 1;
547       }
548     if (test_file) {
549       FILE *f = fopen(p, "r");
550       if (f) {
551 	fgets(line, 254, f);
552 	fclose(f);
553 	test_file = 0;
554 	char *linep = strchr(line, '\0');
555 	// skip final newline, if any
556 	if (*(--linep) == '\n')
557 	  *linep = '\0';
558 	pp = line;
559 	goto again;
560       }
561     }
562   }
563   return 0;
564 }
565 
566 // If the font can't be found, then if not_found is non-NULL, it will be set
567 // to 1 otherwise a message will be printed.
568 
load(int * not_found,int head_only)569 int font::load(int *not_found, int head_only)
570 {
571   char *path;
572   FILE *fp;
573   if ((fp = open_file(name, &path)) == NULL) {
574     if (not_found)
575       *not_found = 1;
576     else
577       error("can't find font file `%1'", name);
578     return 0;
579   }
580   text_file t(fp, path);
581   t.skip_comments = 1;
582   t.silent = head_only;
583   char *p;
584   for (;;) {
585     if (!t.next()) {
586       t.error("missing charset command");
587       return 0;
588     }
589     p = strtok(t.buf, WS);
590     if (strcmp(p, "name") == 0) {
591     }
592     else if (strcmp(p, "spacewidth") == 0) {
593       p = strtok(0, WS);
594       int n;
595       if (p == 0 || sscanf(p, "%d", &n) != 1 || n <= 0) {
596 	t.error("bad argument for spacewidth command");
597 	return 0;
598       }
599       space_width = n;
600     }
601     else if (strcmp(p, "slant") == 0) {
602       p = strtok(0, WS);
603       double n;
604       if (p == 0 || sscanf(p, "%lf", &n) != 1 || n >= 90.0 || n <= -90.0) {
605 	t.error("bad argument for slant command", p);
606 	return 0;
607       }
608       slant = n;
609     }
610     else if (strcmp(p, "ligatures") == 0) {
611       for (;;) {
612 	p = strtok(0, WS);
613 	if (p == 0 || strcmp(p, "0") == 0)
614 	  break;
615 	if (strcmp(p, "ff") == 0)
616 	  ligatures |= LIG_ff;
617 	else if (strcmp(p, "fi") == 0)
618 	  ligatures |= LIG_fi;
619 	else if (strcmp(p, "fl") == 0)
620 	  ligatures |= LIG_fl;
621 	else if (strcmp(p, "ffi") == 0)
622 	  ligatures |= LIG_ffi;
623 	else if (strcmp(p, "ffl") == 0)
624 	  ligatures |= LIG_ffl;
625 	else {
626 	  t.error("unrecognised ligature `%1'", p);
627 	  return 0;
628 	}
629       }
630     }
631     else if (strcmp(p, "internalname") == 0) {
632       p = strtok(0, WS);
633       if (!p) {
634 	t.error("`internalname command requires argument");
635 	return 0;
636       }
637       internalname = new char[strlen(p) + 1];
638       strcpy(internalname, p);
639     }
640     else if (strcmp(p, "special") == 0) {
641       special = 1;
642     }
643     else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) {
644       char *command = p;
645       p = strtok(0, "\n");
646       handle_unknown_font_command(command, trim_arg(p), t.path, t.lineno);
647     }
648     else
649       break;
650   }
651   if (head_only)
652     return 1;
653   char *command = p;
654   int had_charset = 0;
655   t.skip_comments = 0;
656   while (command) {
657     if (strcmp(command, "kernpairs") == 0) {
658       for (;;) {
659 	if (!t.next()) {
660 	  command = 0;
661 	  break;
662 	}
663 	char *c1 = strtok(t.buf, WS);
664 	if (c1 == 0)
665 	  continue;
666 	char *c2 = strtok(0, WS);
667 	if (c2 == 0) {
668 	  command = c1;
669 	  break;
670 	}
671 	p = strtok(0, WS);
672 	if (p == 0) {
673 	  t.error("missing kern amount");
674 	  return 0;
675 	}
676 	int n;
677 	if (sscanf(p, "%d", &n) != 1) {
678 	  t.error("bad kern amount `%1'", p);
679 	  return 0;
680 	}
681 	int i1 = name_to_index(c1);
682 	if (i1 < 0) {
683 	  t.error("invalid character `%1'", c1);
684 	  return 0;
685 	}
686 	int i2 = name_to_index(c2);
687 	if (i2 < 0) {
688 	  t.error("invalid character `%1'", c2);
689 	  return 0;
690 	}
691 	add_kern(i1, i2, n);
692       }
693     }
694     else if (strcmp(command, "charset") == 0) {
695       had_charset = 1;
696       int last_index = -1;
697       for (;;) {
698 	if (!t.next()) {
699 	  command = 0;
700 	  break;
701 	}
702 	char *nm = strtok(t.buf, WS);
703 	if (nm == 0)
704 	  continue;			// I dont think this should happen
705 	p = strtok(0, WS);
706 	if (p == 0) {
707 	  command = nm;
708 	  break;
709 	}
710 	if (p[0] == '"') {
711 	  if (last_index == -1) {
712 	    t.error("first charset entry is duplicate");
713 	    return 0;
714 	  }
715 	  if (strcmp(nm, "---") == 0) {
716 	    t.error("unnamed character cannot be duplicate");
717 	    return 0;
718 	  }
719 	  int idx = name_to_index(nm);
720 	  if (idx < 0) {
721 	    t.error("invalid character `%1'", nm);
722 	    return 0;
723 	  }
724 	  copy_entry(idx, last_index);
725 	}
726 	else {
727 	  font_char_metric metric;
728 	  metric.height = 0;
729 	  metric.depth = 0;
730 	  metric.pre_math_space = 0;
731 	  metric.italic_correction = 0;
732 	  metric.subscript_correction = 0;
733 	  int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
734 			      &metric.width, &metric.height, &metric.depth,
735 			      &metric.italic_correction,
736 			      &metric.pre_math_space,
737 			      &metric.subscript_correction);
738 	  if (nparms < 1) {
739 	    t.error("bad width for `%1'", nm);
740 	    return 0;
741 	  }
742 	  p = strtok(0, WS);
743 	  if (p == 0) {
744 	    t.error("missing character type for `%1'", nm);
745 	    return 0;
746 	  }
747 	  int type;
748 	  if (sscanf(p, "%d", &type) != 1) {
749 	    t.error("bad character type for `%1'", nm);
750 	    return 0;
751 	  }
752 	  if (type < 0 || type > 255) {
753 	    t.error("character type `%1' out of range", type);
754 	    return 0;
755 	  }
756 	  metric.type = type;
757 	  p = strtok(0, WS);
758 	  if (p == 0) {
759 	    t.error("missing code for `%1'", nm);
760 	    return 0;
761 	  }
762 	  char *ptr;
763 	  metric.code = (int)strtol(p, &ptr, 0);
764 	  if (metric.code == 0 && ptr == p) {
765 	    t.error("bad code `%1' for character `%2'", p, nm);
766 	    return 0;
767 	  }
768 	  p = strtok(0, WS);
769 	  if ((p == NULL) || (strcmp(p, "--") == 0)) {
770 	    metric.special_device_coding = NULL;
771 	  }
772 	  else {
773 	    char *nam = new char[strlen(p) + 1];
774 	    strcpy(nam, p);
775 	    metric.special_device_coding = nam;
776 	  }
777 	  if (strcmp(nm, "---") == 0) {
778 	    last_index = number_to_index(metric.code);
779 	    add_entry(last_index, metric);
780 	  }
781 	  else {
782 	    last_index = name_to_index(nm);
783 	    if (last_index < 0) {
784 	      t.error("invalid character `%1'", nm);
785 	      return 0;
786 	    }
787 	    add_entry(last_index, metric);
788 	    copy_entry(number_to_index(metric.code), last_index);
789 	  }
790 	}
791       }
792       if (last_index == -1) {
793 	t.error("I didn't seem to find any characters");
794 	return 0;
795       }
796     }
797     else {
798       t.error("unrecognised command `%1' after `kernpairs' or `charset' command", command);
799       return 0;
800     }
801   }
802   if (!had_charset) {
803     t.error("missing charset command");
804     return 0;
805   }
806   if (space_width == 0)
807     space_width = scale_round(unitwidth, res, 72*3*sizescale);
808   compact();
809   return 1;
810 }
811 
812 static struct {
813   const char *command;
814   int *ptr;
815 } table[] = {
816   { "res", &font::res },
817   { "hor", &font::hor },
818   { "vert", &font::vert },
819   { "unitwidth", &font::unitwidth },
820   { "paperwidth", &font::paperwidth },
821   { "paperlength", &font::paperlength },
822   { "spare1", &font::biggestfont },
823   { "biggestfont", &font::biggestfont },
824   { "spare2", &font::spare2 },
825   { "sizescale", &font::sizescale },
826   };
827 
load_desc()828 int font::load_desc()
829 {
830   int nfonts = 0;
831   FILE *fp;
832   char *path;
833   if ((fp = open_file("DESC", &path)) == 0) {
834     error("can't find `DESC' file");
835     return 0;
836   }
837   text_file t(fp, path);
838   t.skip_comments = 1;
839   res = 0;
840   while (t.next()) {
841     char *p = strtok(t.buf, WS);
842     int found = 0;
843     unsigned int idx;
844     for (idx = 0; !found && idx < sizeof(table)/sizeof(table[0]); idx++)
845       if (strcmp(table[idx].command, p) == 0)
846 	found = 1;
847     if (found) {
848       char *q = strtok(0, WS);
849       if (!q) {
850 	t.error("missing value for command `%1'", p);
851 	return 0;
852       }
853       //int *ptr = &(this->*(table[idx-1].ptr));
854       int *ptr = table[idx-1].ptr;
855       if (sscanf(q, "%d", ptr) != 1) {
856 	t.error("bad number `%1'", q);
857 	return 0;
858       }
859     }
860     else if (strcmp("family", p) == 0) {
861       p = strtok(0, WS);
862       if (!p) {
863 	t.error("family command requires an argument");
864 	return 0;
865       }
866       char *tem = new char[strlen(p)+1];
867       strcpy(tem, p);
868       family = tem;
869     }
870     else if (strcmp("fonts", p) == 0) {
871       p = strtok(0, WS);
872       if (!p || sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
873 	t.error("bad number of fonts `%1'", p);
874 	return 0;
875       }
876       font_name_table = (const char **)new char *[nfonts+1];
877       for (int i = 0; i < nfonts; i++) {
878 	p = strtok(0, WS);
879 	while (p == 0) {
880 	  if (!t.next()) {
881 	    t.error("end of file while reading list of fonts");
882 	    return 0;
883 	  }
884 	  p = strtok(t.buf, WS);
885 	}
886 	char *temp = new char[strlen(p)+1];
887 	strcpy(temp, p);
888 	font_name_table[i] = temp;
889       }
890       p = strtok(0, WS);
891       if (p != 0) {
892 	t.error("font count does not match number of fonts");
893 	return 0;
894       }
895       font_name_table[nfonts] = 0;
896     }
897     else if (strcmp("papersize", p) == 0) {
898       p = strtok(0, WS);
899       if (!p) {
900 	t.error("papersize command requires an argument");
901 	return 0;
902       }
903       int found_paper = 0;
904       while (p) {
905 	double unscaled_paperwidth, unscaled_paperlength;
906 	if (scan_papersize(p, &papersize, &unscaled_paperlength,
907 			   &unscaled_paperwidth)) {
908 	  paperwidth = int(unscaled_paperwidth * res + 0.5);
909 	  paperlength = int(unscaled_paperlength * res + 0.5);
910 	  found_paper = 1;
911 	  break;
912 	}
913 	p = strtok(0, WS);
914       }
915       if (!found_paper) {
916 	t.error("bad paper size");
917 	return 0;
918       }
919     }
920     else if (strcmp("unscaled_charwidths", p) == 0)
921       unscaled_charwidths = 1;
922     else if (strcmp("pass_filenames", p) == 0)
923       pass_filenames = 1;
924     else if (strcmp("sizes", p) == 0) {
925       int n = 16;
926       sizes = new int[n];
927       int i = 0;
928       for (;;) {
929 	p = strtok(0, WS);
930 	while (p == 0) {
931 	  if (!t.next()) {
932 	    t.error("list of sizes must be terminated by `0'");
933 	    return 0;
934 	  }
935 	  p = strtok(t.buf, WS);
936 	}
937 	int lower, upper;
938 	switch (sscanf(p, "%d-%d", &lower, &upper)) {
939 	case 1:
940 	  upper = lower;
941 	  // fall through
942 	case 2:
943 	  if (lower <= upper && lower >= 0)
944 	    break;
945 	  // fall through
946 	default:
947 	  t.error("bad size range `%1'", p);
948 	  return 0;
949 	}
950 	if (i + 2 > n) {
951 	  int *old_sizes = sizes;
952 	  sizes = new int[n*2];
953 	  memcpy(sizes, old_sizes, n*sizeof(int));
954 	  n *= 2;
955 	  a_delete old_sizes;
956 	}
957 	sizes[i++] = lower;
958 	if (lower == 0)
959 	  break;
960 	sizes[i++] = upper;
961       }
962       if (i == 1) {
963 	t.error("must have some sizes");
964 	return 0;
965       }
966     }
967     else if (strcmp("styles", p) == 0) {
968       int style_table_size = 5;
969       style_table = (const char **)new char *[style_table_size];
970       int j;
971       for (j = 0; j < style_table_size; j++)
972 	style_table[j] = 0;
973       int i = 0;
974       for (;;) {
975 	p = strtok(0, WS);
976 	if (p == 0)
977 	  break;
978 	// leave room for terminating 0
979 	if (i + 1 >= style_table_size) {
980 	  const char **old_style_table = style_table;
981 	  style_table_size *= 2;
982 	  style_table = (const char **)new char*[style_table_size];
983 	  for (j = 0; j < i; j++)
984 	    style_table[j] = old_style_table[j];
985 	  for (; j < style_table_size; j++)
986 	    style_table[j] = 0;
987 	  a_delete old_style_table;
988 	}
989 	char *tem = new char[strlen(p) + 1];
990 	strcpy(tem, p);
991 	style_table[i++] = tem;
992       }
993     }
994     else if (strcmp("tcommand", p) == 0)
995       tcommand = 1;
996     else if (strcmp("use_charnames_in_special", p) == 0)
997       use_charnames_in_special = 1;
998     else if (strcmp("image_generator", p) == 0) {
999       p = strtok(0, WS);
1000       if (!p) {
1001 	t.error("image_generator command requires an argument");
1002 	return 0;
1003       }
1004       image_generator = strsave(p);
1005     }
1006     else if (strcmp("charset", p) == 0)
1007       break;
1008     else if (unknown_desc_command_handler) {
1009       char *command = p;
1010       p = strtok(0, "\n");
1011       (*unknown_desc_command_handler)(command, trim_arg(p), t.path, t.lineno);
1012     }
1013   }
1014   if (res == 0) {
1015     t.error("missing `res' command");
1016     return 0;
1017   }
1018   if (unitwidth == 0) {
1019     t.error("missing `unitwidth' command");
1020     return 0;
1021   }
1022   if (font_name_table == 0) {
1023     t.error("missing `fonts' command");
1024     return 0;
1025   }
1026   if (sizes == 0) {
1027     t.error("missing `sizes' command");
1028     return 0;
1029   }
1030   if (sizescale < 1) {
1031     t.error("bad `sizescale' value");
1032     return 0;
1033   }
1034   if (hor < 1) {
1035     t.error("bad `hor' value");
1036     return 0;
1037   }
1038   if (vert < 1) {
1039     t.error("bad `vert' value");
1040     return 0;
1041   }
1042   return 1;
1043 }
1044 
handle_unknown_font_command(const char *,const char *,const char *,int)1045 void font::handle_unknown_font_command(const char *, const char *,
1046 				       const char *, int)
1047 {
1048 }
1049 
1050 FONT_COMMAND_HANDLER
set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func)1051 font::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func)
1052 {
1053   FONT_COMMAND_HANDLER prev = unknown_desc_command_handler;
1054   unknown_desc_command_handler = func;
1055   return prev;
1056 }
1057