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