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