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