file_case.{h,cpp}: wether->whether; 2017
[s-roff.git] / src / lib-roff / font.cpp
blob99bf69f6b492205edb71f0bc46e4ab620b8a48a7
1 /*@
2 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * Copyright (C) 1989 - 1992, 2000 - 2006, 2008
5 * Free Software Foundation, Inc.
6 * Written by James Clark (jjc@jclark.com)
8 * This 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.
13 * This 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.
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.
23 #include "config.h"
24 #include "lib.h"
26 #include <assert.h>
27 #include <ctype.h>
28 #include <math.h>
29 #include <stdlib.h>
31 #include "cset.h"
32 #include "errarg.h"
33 #include "error.h"
34 #include "file_case.h"
35 #include "font.h"
36 #include "paper.h"
37 #include "searchpath.h"
38 #include "unicode.h"
40 const char * const WS = " \t\n\r"; // FIXME lib.h?
42 struct font_char_metric {
43 char type;
44 int code;
45 int width;
46 int height;
47 int depth;
48 int pre_math_space;
49 int italic_correction;
50 int subscript_correction;
51 char *special_device_coding;
54 class font_kern_list
56 public:
57 glyph *glyph1;
58 glyph *glyph2;
59 int amount;
60 font_kern_list *next;
62 font_kern_list(glyph *, glyph *, int, font_kern_list * = 0);
65 class font_widths_cache
67 public:
68 font_widths_cache *next;
69 int point_size;
70 int *width;
72 font_widths_cache(int, int, font_widths_cache * = 0);
73 ~font_widths_cache();
76 class text_file
78 public:
79 file_case *fcp;
80 int lineno;
81 int size;
82 int skip_comments;
83 int silent;
84 char *buf;
85 text_file(file_case *fcp);
86 ~text_file();
87 int next();
88 void error(const char *format,
89 const errarg &arg1 = empty_errarg,
90 const errarg &arg2 = empty_errarg,
91 const errarg &arg3 = empty_errarg);
94 text_file::text_file(file_case *fcp)
95 : fcp(fcp), lineno(0), size(0), skip_comments(1), silent(0), buf(0)
99 text_file::~text_file()
101 a_delete buf;
102 if (fcp != NULL)
103 delete fcp;
106 int text_file::next()
108 if (fcp == NULL)
109 return 0;
110 if (buf == 0) {
111 buf = new char[128];
112 size = 128;
114 for (;;) {
115 int i = 0;
116 for (;;) {
117 int c = fcp->get_c();
118 if (c == EOF)
119 break;
120 if (invalid_input_char(c))
121 error("invalid input character code `%1'", int(c));
122 else {
123 if (i + 1 >= size) {
124 char *old_buf = buf;
125 buf = new char[size*2];
126 memcpy(buf, old_buf, size);
127 a_delete old_buf;
128 size *= 2;
130 buf[i++] = c;
131 if (c == '\n')
132 break;
135 if (i == 0)
136 break;
137 buf[i] = '\0';
138 lineno++;
139 char *ptr = buf;
140 while (csspace(*ptr))
141 ptr++;
142 if (*ptr != 0 && (!skip_comments || *ptr != '#'))
143 return 1;
145 return 0;
148 void text_file::error(const char *format,
149 const errarg &arg1,
150 const errarg &arg2,
151 const errarg &arg3)
153 if (!silent)
154 error_with_file_and_line(fcp->path(), lineno, format, arg1, arg2, arg3);
157 font::font(const char *s)
158 : ligatures(0), kern_hash_table(0), space_width(0), special(0),
159 ch_index(0), nindices(0), ch(0), ch_used(0), ch_size(0), widths_cache(0)
161 name = new char[strlen(s) + 1];
162 strcpy(name, s);
163 internalname = 0;
164 slant = 0.0;
165 zoom = 0;
166 // load(); // for testing
169 font::~font()
171 for (int i = 0; i < ch_used; i++)
172 if (ch[i].special_device_coding)
173 a_delete ch[i].special_device_coding;
174 a_delete ch;
175 a_delete ch_index;
176 if (kern_hash_table) {
177 for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) {
178 font_kern_list *kerns = kern_hash_table[i];
179 while (kerns) {
180 font_kern_list *tem = kerns;
181 kerns = kerns->next;
182 delete tem;
185 a_delete kern_hash_table;
187 a_delete name;
188 a_delete internalname;
189 while (widths_cache) {
190 font_widths_cache *tem = widths_cache;
191 widths_cache = widths_cache->next;
192 delete tem;
196 static int scale_round(int n, int x, int y)
198 assert(x >= 0 && y > 0);
199 int y2 = y/2;
200 if (x == 0)
201 return 0;
202 if (n >= 0) {
203 if (n <= (INT_MAX - y2) / x)
204 return (n * x + y2) / y;
205 return int(n * double(x) / double(y) + .5);
207 else {
208 if (-(unsigned)n <= (-(unsigned)INT_MIN - y2) / x)
209 return (n * x - y2) / y;
210 return int(n * double(x) / double(y) - .5);
214 static int scale_round(int n, int x, int y, int z)
216 assert(x >= 0 && y > 0 && z > 0);
217 if (x == 0)
218 return 0;
219 if (n >= 0)
220 return int((n * double(x) / double(y)) * (double(z) / 1000.0) + .5);
221 else
222 return int((n * double(x) / double(y)) * (double(z) / 1000.0) - .5);
225 inline int font::scale(int w, int sz)
227 if (zoom)
228 return scale_round(w, sz, unitwidth, zoom);
229 else
230 return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
233 int font::unit_scale(double *value, char unit)
235 // we scale everything to inch
236 double divisor = 0;
237 switch (unit) {
238 case 'i':
239 divisor = 1;
240 break;
241 case 'p':
242 divisor = 72;
243 break;
244 case 'P':
245 divisor = 6;
246 break;
247 case 'c':
248 divisor = 2.54;
249 break;
250 default:
251 assert(0);
252 break;
254 if (divisor) {
255 *value /= divisor;
256 return 1;
258 return 0;
261 int font::get_skew(glyph *g, int point_size, int sl)
263 int h = get_height(g, point_size);
264 return int(h * tan((slant + sl) * PI / 180.0) + .5);
267 int font::contains(glyph *g)
269 int idx = glyph_to_index(g);
270 assert(idx >= 0);
271 // Explicitly enumerated glyph?
272 if (idx < nindices && ch_index[idx] >= 0)
273 return 1;
274 if (is_unicode) {
275 // Unicode font
276 const char *nm = glyph_to_name(g);
277 if (nm != NULL) {
278 // ASCII character?
279 if (nm[0] == 'c' && nm[1] == 'h' && nm[2] == 'a' && nm[3] == 'r'
280 && (nm[4] >= '0' && nm[4] <= '9')) {
281 int n = (nm[4] - '0');
282 if (nm[5] == '\0')
283 return 1;
284 if (n > 0 && (nm[5] >= '0' && nm[5] <= '9')) {
285 n = 10*n + (nm[5] - '0');
286 if (nm[6] == '\0')
287 return 1;
288 if (nm[6] >= '0' && nm[6] <= '9') {
289 n = 10*n + (nm[6] - '0');
290 if (nm[7] == '\0' && n < 128)
291 return 1;
295 // Unicode character?
296 if (check_unicode_name(nm))
297 return 1;
298 // If `nm' is a single letter `x', the glyph name is `\x'.
299 char buf[] = { '\\', '\0', '\0' };
300 if (nm[1] == '\0') {
301 buf[1] = nm[0];
302 nm = buf;
304 // groff glyph name that maps to Unicode?
305 const char *unicode = glyph_name_to_unicode(nm);
306 if (unicode != NULL && strchr(unicode, '_') == NULL)
307 return 1;
309 // Numbered character?
310 int n = glyph_to_number(g);
311 if (n >= 0)
312 return 1;
314 return 0;
317 int font::is_special()
319 return special;
322 font_widths_cache::font_widths_cache(int ps, int ch_size,
323 font_widths_cache *p)
324 : next(p), point_size(ps)
326 width = new int[ch_size];
327 for (int i = 0; i < ch_size; i++)
328 width[i] = -1;
331 font_widths_cache::~font_widths_cache()
333 a_delete width;
336 int font::get_width(glyph *g, int point_size)
338 int idx = glyph_to_index(g);
339 assert(idx >= 0);
340 int real_size;
341 if (!zoom)
342 real_size = point_size;
343 else
345 if (point_size <= (INT_MAX - 500) / zoom)
346 real_size = (point_size * zoom + 500) / 1000;
347 else
348 real_size = int(point_size * double(zoom) / 1000.0 + .5);
350 if (idx < nindices && ch_index[idx] >= 0) {
351 // Explicitly enumerated glyph
352 int i = ch_index[idx];
353 if (real_size == unitwidth || font::unscaled_charwidths)
354 return ch[i].width;
356 if (!widths_cache)
357 widths_cache = new font_widths_cache(real_size, ch_size);
358 else if (widths_cache->point_size != real_size) {
359 font_widths_cache **p;
360 for (p = &widths_cache; *p; p = &(*p)->next)
361 if ((*p)->point_size == real_size)
362 break;
363 if (*p) {
364 font_widths_cache *tem = *p;
365 *p = (*p)->next;
366 tem->next = widths_cache;
367 widths_cache = tem;
369 else
370 widths_cache = new font_widths_cache(real_size, ch_size,
371 widths_cache);
373 int &w = widths_cache->width[i];
374 if (w < 0)
375 w = scale(ch[i].width, point_size);
376 return w;
378 if (is_unicode) {
379 // Unicode font
380 int width = 24; // value found in the original font files
381 // XXX: this must be eventually moved back to the
382 // font description file!
383 if (real_size == unitwidth || font::unscaled_charwidths)
384 return width;
385 else
386 return scale(width, point_size);
388 abort();
391 int font::get_height(glyph *g, int point_size)
393 int idx = glyph_to_index(g);
394 assert(idx >= 0);
395 if (idx < nindices && ch_index[idx] >= 0) {
396 // Explicitly enumerated glyph
397 return scale(ch[ch_index[idx]].height, point_size);
399 if (is_unicode) {
400 // Unicode font
401 return 0; // value found in the original font files
402 // XXX: this must be eventually moved back to the
403 // font description file!
405 abort();
408 int font::get_depth(glyph *g, int point_size)
410 int idx = glyph_to_index(g);
411 assert(idx >= 0);
412 if (idx < nindices && ch_index[idx] >= 0) {
413 // Explicitly enumerated glyph
414 return scale(ch[ch_index[idx]].depth, point_size);
416 if (is_unicode) {
417 // Unicode font
418 return 0; // value found in the original font files
419 // XXX: this must be eventually moved back to the
420 // font description file!
422 abort();
425 int font::get_italic_correction(glyph *g, int point_size)
427 int idx = glyph_to_index(g);
428 assert(idx >= 0);
429 if (idx < nindices && ch_index[idx] >= 0) {
430 // Explicitly enumerated glyph
431 return scale(ch[ch_index[idx]].italic_correction, point_size);
433 if (is_unicode) {
434 // Unicode font
435 return 0; // value found in the original font files
436 // XXX: this must be eventually moved back to the
437 // font description file!
439 abort();
442 int font::get_left_italic_correction(glyph *g, int point_size)
444 int idx = glyph_to_index(g);
445 assert(idx >= 0);
446 if (idx < nindices && ch_index[idx] >= 0) {
447 // Explicitly enumerated glyph
448 return scale(ch[ch_index[idx]].pre_math_space, point_size);
450 if (is_unicode) {
451 // Unicode font
452 return 0; // value found in the original font files
453 // XXX: this must be eventually moved back to the
454 // font description file!
456 abort();
459 int font::get_subscript_correction(glyph *g, int point_size)
461 int idx = glyph_to_index(g);
462 assert(idx >= 0);
463 if (idx < nindices && ch_index[idx] >= 0) {
464 // Explicitly enumerated glyph
465 return scale(ch[ch_index[idx]].subscript_correction, point_size);
467 if (is_unicode) {
468 // Unicode font
469 return 0; // value found in the original font files
470 // XXX: this must be eventually moved back to the
471 // font description file!
473 abort();
476 void font::set_zoom(int factor)
478 assert(factor >= 0);
479 if (factor == 1000)
480 zoom = 0;
481 else
482 zoom = factor;
485 int font::get_zoom()
487 return zoom;
490 int font::get_space_width(int point_size)
492 return scale(space_width, point_size);
495 font_kern_list::font_kern_list(glyph *g1, glyph *g2, int n, font_kern_list *p)
496 : glyph1(g1), glyph2(g2), amount(n), next(p)
500 inline int font::hash_kern(glyph *g1, glyph *g2)
502 int n = ((glyph_to_index(g1) << 10) + glyph_to_index(g2))
503 % KERN_HASH_TABLE_SIZE;
504 return n < 0 ? -n : n;
507 void font::add_kern(glyph *g1, glyph *g2, int amount)
509 if (!kern_hash_table) {
510 kern_hash_table = new font_kern_list *[int(KERN_HASH_TABLE_SIZE)];
511 for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++)
512 kern_hash_table[i] = 0;
514 font_kern_list **p = kern_hash_table + hash_kern(g1, g2);
515 *p = new font_kern_list(g1, g2, amount, *p);
518 int font::get_kern(glyph *g1, glyph *g2, int point_size)
520 if (kern_hash_table) {
521 for (font_kern_list *p = kern_hash_table[hash_kern(g1, g2)]; p;
522 p = p->next)
523 if (g1 == p->glyph1 && g2 == p->glyph2)
524 return scale(p->amount, point_size);
526 return 0;
529 int font::has_ligature(int mask)
531 return mask & ligatures;
534 int font::get_character_type(glyph *g)
536 int idx = glyph_to_index(g);
537 assert(idx >= 0);
538 if (idx < nindices && ch_index[idx] >= 0) {
539 // Explicitly enumerated glyph
540 return ch[ch_index[idx]].type;
542 if (is_unicode) {
543 // Unicode font
544 return 0; // value found in the original font files
545 // XXX: this must be eventually moved back to the
546 // font description file!
548 abort();
551 int font::get_code(glyph *g)
553 int idx = glyph_to_index(g);
554 assert(idx >= 0);
555 if (idx < nindices && ch_index[idx] >= 0) {
556 // Explicitly enumerated glyph
557 return ch[ch_index[idx]].code;
559 if (is_unicode) {
560 // Unicode font
561 const char *nm = glyph_to_name(g);
562 if (nm != NULL) {
563 // ASCII character?
564 if (nm[0] == 'c' && nm[1] == 'h' && nm[2] == 'a' && nm[3] == 'r'
565 && (nm[4] >= '0' && nm[4] <= '9')) {
566 int n = (nm[4] - '0');
567 if (nm[5] == '\0')
568 return n;
569 if (n > 0 && (nm[5] >= '0' && nm[5] <= '9')) {
570 n = 10*n + (nm[5] - '0');
571 if (nm[6] == '\0')
572 return n;
573 if (nm[6] >= '0' && nm[6] <= '9') {
574 n = 10*n + (nm[6] - '0');
575 if (nm[7] == '\0' && n < 128)
576 return n;
580 // Unicode character?
581 if (check_unicode_name(nm)) {
582 char *ignore;
583 return (int)strtol(nm + 1, &ignore, 16);
585 // If `nm' is a single letter `x', the glyph name is `\x'.
586 char buf[] = { '\\', '\0', '\0' };
587 if (nm[1] == '\0') {
588 buf[1] = nm[0];
589 nm = buf;
591 // groff glyphs that map to Unicode?
592 const char *unicode = glyph_name_to_unicode(nm);
593 if (unicode != NULL && strchr(unicode, '_') == NULL) {
594 char *ignore;
595 return (int)strtol(unicode, &ignore, 16);
598 // Numbered character?
599 int n = glyph_to_number(g);
600 if (n >= 0)
601 return n;
603 // The caller must check `contains(g)' before calling get_code(g).
604 abort();
607 const char *font::get_name()
609 return name;
612 const char *font::get_internal_name()
614 return internalname;
617 const char *font::get_special_device_encoding(glyph *g)
619 int idx = glyph_to_index(g);
620 assert(idx >= 0);
621 if (idx < nindices && ch_index[idx] >= 0) {
622 // Explicitly enumerated glyph
623 return ch[ch_index[idx]].special_device_coding;
625 if (is_unicode) {
626 // Unicode font
627 return NULL;
629 abort();
632 const char *font::get_image_generator()
634 return image_generator;
637 void font::alloc_ch_index(int idx)
639 if (nindices == 0) {
640 nindices = 128;
641 if (idx >= nindices)
642 nindices = idx + 10;
643 ch_index = new int[nindices];
644 for (int i = 0; i < nindices; i++)
645 ch_index[i] = -1;
647 else {
648 int old_nindices = nindices;
649 nindices *= 2;
650 if (idx >= nindices)
651 nindices = idx + 10;
652 int *old_ch_index = ch_index;
653 ch_index = new int[nindices];
654 memcpy(ch_index, old_ch_index, sizeof(int)*old_nindices);
655 for (int i = old_nindices; i < nindices; i++)
656 ch_index[i] = -1;
657 a_delete old_ch_index;
661 void font::extend_ch()
663 if (ch == 0)
664 ch = new font_char_metric[ch_size = 16];
665 else {
666 int old_ch_size = ch_size;
667 ch_size *= 2;
668 font_char_metric *old_ch = ch;
669 ch = new font_char_metric[ch_size];
670 memcpy(ch, old_ch, old_ch_size*sizeof(font_char_metric));
671 a_delete old_ch;
675 void font::compact()
677 int i;
678 for (i = nindices - 1; i >= 0; i--)
679 if (ch_index[i] >= 0)
680 break;
681 i++;
682 if (i < nindices) {
683 int *old_ch_index = ch_index;
684 ch_index = new int[i];
685 memcpy(ch_index, old_ch_index, i*sizeof(int));
686 a_delete old_ch_index;
687 nindices = i;
689 if (ch_used < ch_size) {
690 font_char_metric *old_ch = ch;
691 ch = new font_char_metric[ch_used];
692 memcpy(ch, old_ch, ch_used*sizeof(font_char_metric));
693 a_delete old_ch;
694 ch_size = ch_used;
698 void font::add_entry(glyph *g, const font_char_metric &metric)
700 int idx = glyph_to_index(g);
701 assert(idx >= 0);
702 if (idx >= nindices)
703 alloc_ch_index(idx);
704 assert(idx < nindices);
705 if (ch_used + 1 >= ch_size)
706 extend_ch();
707 assert(ch_used + 1 < ch_size);
708 ch_index[idx] = ch_used;
709 ch[ch_used++] = metric;
712 void font::copy_entry(glyph *new_glyph, glyph *old_glyph)
714 int new_index = glyph_to_index(new_glyph);
715 int old_index = glyph_to_index(old_glyph);
716 assert(new_index >= 0 && old_index >= 0 && old_index < nindices);
717 if (new_index >= nindices)
718 alloc_ch_index(new_index);
719 ch_index[new_index] = ch_index[old_index];
722 font *font::load_font(const char *s, int *not_found, int head_only)
724 font *f = new font(s);
725 if (!f->load(not_found, head_only)) {
726 delete f;
727 return 0;
729 return f;
732 static char *trim_arg(char *p)
734 if (!p)
735 return 0;
736 while (csspace(*p))
737 p++;
738 char *q = strchr(p, '\0');
739 while (q > p && csspace(q[-1]))
740 q--;
741 *q = '\0';
742 return p;
745 int font::scan_papersize(const char *p,
746 const char **size, double *length, double *width)
748 double l, w;
749 char lu[2], wu[2];
750 const char *pp = p;
751 int test_file = 1;
752 char line[255];
753 again:
754 if (csdigit(*pp)) {
755 if (sscanf(pp, "%lf%1[ipPc],%lf%1[ipPc]", &l, lu, &w, wu) == 4
756 && l > 0 && w > 0
757 && unit_scale(&l, lu[0]) && unit_scale(&w, wu[0])) {
758 if (length)
759 *length = l;
760 if (width)
761 *width = w;
762 if (size)
763 *size = "custom";
764 return 1;
767 else {
768 int i;
769 for (i = 0; i < NUM_PAPERSIZES; i++)
770 if (strcasecmp(papersizes[i].name, pp) == 0) {
771 if (length)
772 *length = papersizes[i].length;
773 if (width)
774 *width = papersizes[i].width;
775 if (size)
776 *size = papersizes[i].name;
777 return 1;
779 if (test_file) {
780 FILE *f = fopen(p, "r");
781 if (f) {
782 fgets(line, 254, f);
783 fclose(f);
784 test_file = 0;
785 char *linep = strchr(line, '\0');
786 // skip final newline, if any
787 if (*(--linep) == '\n')
788 *linep = '\0';
789 pp = line;
790 goto again;
794 return 0;
797 // If the font can't be found, then if not_found is non-NULL, it will be set
798 // to 1 otherwise a message will be printed.
800 int font::load(int *not_found, int head_only)
802 if (strcmp(name, "DESC") == 0) {
803 if (not_found)
804 *not_found = 1;
805 else
806 error("`DESC' is not a valid font file name");
807 return 0;
809 file_case *fcp;
810 if ((fcp = open_file(name)) == NULL) {
811 if (not_found)
812 *not_found = 1;
813 else
814 error("can't find font file `%1'", name);
815 return 0;
817 text_file t(fcp);
818 t.skip_comments = 1;
819 t.silent = head_only;
820 char *p;
821 for (;;) {
822 if (!t.next()) {
823 p = 0;
824 break;
826 p = strtok(t.buf, WS);
827 if (strcmp(p, "name") == 0) {
829 else if (strcmp(p, "spacewidth") == 0) {
830 p = strtok(0, WS);
831 int n;
832 if (p == 0 || sscanf(p, "%d", &n) != 1 || n <= 0) {
833 t.error("bad argument for `spacewidth' command");
834 return 0;
836 space_width = n;
838 else if (strcmp(p, "slant") == 0) {
839 p = strtok(0, WS);
840 double n;
841 if (p == 0 || sscanf(p, "%lf", &n) != 1 || n >= 90.0 || n <= -90.0) {
842 t.error("bad argument for `slant' command", p);
843 return 0;
845 slant = n;
847 else if (strcmp(p, "ligatures") == 0) {
848 for (;;) {
849 p = strtok(0, WS);
850 if (p == 0 || strcmp(p, "0") == 0)
851 break;
852 if (strcmp(p, "ff") == 0)
853 ligatures |= LIG_ff;
854 else if (strcmp(p, "fi") == 0)
855 ligatures |= LIG_fi;
856 else if (strcmp(p, "fl") == 0)
857 ligatures |= LIG_fl;
858 else if (strcmp(p, "ffi") == 0)
859 ligatures |= LIG_ffi;
860 else if (strcmp(p, "ffl") == 0)
861 ligatures |= LIG_ffl;
862 else {
863 t.error("unrecognised ligature `%1'", p);
864 return 0;
868 else if (strcmp(p, "internalname") == 0) {
869 p = strtok(0, WS);
870 if (!p) {
871 t.error("`internalname' command requires argument");
872 return 0;
874 internalname = new char[strlen(p) + 1];
875 strcpy(internalname, p);
877 else if (strcmp(p, "special") == 0) {
878 special = 1;
880 else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) {
881 char *command = p;
882 p = strtok(0, "\n");
883 handle_unknown_font_command(command, trim_arg(p), t.fcp->path(),
884 t.lineno);
886 else
887 break;
889 int had_charset = 0;
890 if (p == 0) {
891 if (!is_unicode) {
892 t.error("missing charset command");
893 return 0;
895 } else {
896 char *command = p;
897 t.skip_comments = 0;
898 while (command) {
899 if (strcmp(command, "kernpairs") == 0) {
900 if (head_only)
901 return 1;
902 for (;;) {
903 if (!t.next()) {
904 command = 0;
905 break;
907 char *c1 = strtok(t.buf, WS);
908 if (c1 == 0)
909 continue;
910 char *c2 = strtok(0, WS);
911 if (c2 == 0) {
912 command = c1;
913 break;
915 p = strtok(0, WS);
916 if (p == 0) {
917 t.error("missing kern amount");
918 return 0;
920 int n;
921 if (sscanf(p, "%d", &n) != 1) {
922 t.error("bad kern amount `%1'", p);
923 return 0;
925 glyph *g1 = name_to_glyph(c1);
926 glyph *g2 = name_to_glyph(c2);
927 add_kern(g1, g2, n);
930 else if (strcmp(command, "charset") == 0) {
931 if (head_only)
932 return 1;
933 had_charset = 1;
934 glyph *last_glyph = NULL;
935 for (;;) {
936 if (!t.next()) {
937 command = 0;
938 break;
940 char *nm = strtok(t.buf, WS);
941 if (nm == 0)
942 continue; // I dont think this should happen
943 p = strtok(0, WS);
944 if (p == 0) {
945 command = nm;
946 break;
948 if (p[0] == '"') {
949 if (last_glyph == NULL) {
950 t.error("first charset entry is duplicate");
951 return 0;
953 if (strcmp(nm, "---") == 0) {
954 t.error("unnamed character cannot be duplicate");
955 return 0;
957 glyph *g = name_to_glyph(nm);
958 copy_entry(g, last_glyph);
960 else {
961 font_char_metric metric;
962 metric.height = 0;
963 metric.depth = 0;
964 metric.pre_math_space = 0;
965 metric.italic_correction = 0;
966 metric.subscript_correction = 0;
967 int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
968 &metric.width, &metric.height, &metric.depth,
969 &metric.italic_correction,
970 &metric.pre_math_space,
971 &metric.subscript_correction);
972 if (nparms < 1) {
973 t.error("bad width for `%1'", nm);
974 return 0;
976 p = strtok(0, WS);
977 if (p == 0) {
978 t.error("missing character type for `%1'", nm);
979 return 0;
981 int type;
982 if (sscanf(p, "%d", &type) != 1) {
983 t.error("bad character type for `%1'", nm);
984 return 0;
986 if (type < 0 || type > 255) {
987 t.error("character type `%1' out of range", type);
988 return 0;
990 metric.type = type;
991 p = strtok(0, WS);
992 if (p == 0) {
993 t.error("missing code for `%1'", nm);
994 return 0;
996 char *ptr;
997 metric.code = (int)strtol(p, &ptr, 0);
998 if (metric.code == 0 && ptr == p) {
999 t.error("bad code `%1' for character `%2'", p, nm);
1000 return 0;
1002 p = strtok(0, WS);
1003 if ((p == NULL) || (strcmp(p, "--") == 0)) {
1004 metric.special_device_coding = NULL;
1006 else {
1007 char *nam = new char[strlen(p) + 1];
1008 strcpy(nam, p);
1009 metric.special_device_coding = nam;
1011 if (strcmp(nm, "---") == 0) {
1012 last_glyph = number_to_glyph(metric.code);
1013 add_entry(last_glyph, metric);
1015 else {
1016 last_glyph = name_to_glyph(nm);
1017 add_entry(last_glyph, metric);
1018 copy_entry(number_to_glyph(metric.code), last_glyph);
1022 if (last_glyph == NULL) {
1023 t.error("I didn't seem to find any characters");
1024 return 0;
1027 else {
1028 t.error("unrecognised command `%1' "
1029 "after `kernpairs' or `charset' command",
1030 command);
1031 return 0;
1034 compact();
1036 if (!is_unicode && !had_charset) {
1037 t.error("missing `charset' command");
1038 return 0;
1040 if (space_width == 0) {
1041 if (zoom)
1042 space_width = scale_round(unitwidth, res, 72 * 3 * sizescale, zoom);
1043 else
1044 space_width = scale_round(unitwidth, res, 72 * 3 * sizescale);
1046 return 1;
1049 static struct {
1050 const char *command;
1051 int *ptr;
1052 } table[] = { // FIXME const?
1053 { "res", &font::res },
1054 { "hor", &font::hor },
1055 { "vert", &font::vert },
1056 { "unitwidth", &font::unitwidth },
1057 { "paperwidth", &font::paperwidth },
1058 { "paperlength", &font::paperlength },
1059 { "spare1", &font::biggestfont },
1060 { "biggestfont", &font::biggestfont },
1061 { "spare2", &font::spare2 },
1062 { "sizescale", &font::sizescale },
1065 int font::load_desc()
1067 int nfonts = 0;
1068 file_case *fcp;
1069 if ((fcp = open_file("DESC")) == NULL) {
1070 error("can't find `DESC' file");
1071 return 0;
1073 text_file t(fcp);
1074 t.skip_comments = 1;
1075 res = 0;
1076 while (t.next()) {
1077 char *p = strtok(t.buf, WS);
1078 int found = 0;
1079 unsigned int idx;
1080 for (idx = 0; !found && idx < sizeof(table)/sizeof(table[0]); idx++)
1081 if (strcmp(table[idx].command, p) == 0)
1082 found = 1;
1083 if (found) {
1084 char *q = strtok(0, WS);
1085 if (!q) {
1086 t.error("missing value for command `%1'", p);
1087 return 0;
1089 //int *ptr = &(this->*(table[idx-1].ptr));
1090 int *ptr = table[idx-1].ptr;
1091 if (sscanf(q, "%d", ptr) != 1) {
1092 t.error("bad number `%1'", q);
1093 return 0;
1096 else if (strcmp("family", p) == 0) {
1097 p = strtok(0, WS);
1098 if (!p) {
1099 t.error("family command requires an argument");
1100 return 0;
1102 char *tem = new char[strlen(p)+1];
1103 strcpy(tem, p);
1104 family = tem;
1106 else if (strcmp("fonts", p) == 0) {
1107 p = strtok(0, WS);
1108 if (!p || sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
1109 t.error("bad number of fonts `%1'", p);
1110 return 0;
1112 font_name_table = (const char **)new char *[nfonts+1];
1113 for (int i = 0; i < nfonts; i++) {
1114 p = strtok(0, WS);
1115 while (p == 0) {
1116 if (!t.next()) {
1117 t.error("end of file while reading list of fonts");
1118 return 0;
1120 p = strtok(t.buf, WS);
1122 char *temp = new char[strlen(p)+1];
1123 strcpy(temp, p);
1124 font_name_table[i] = temp;
1126 p = strtok(0, WS);
1127 if (p != 0) {
1128 t.error("font count does not match number of fonts");
1129 return 0;
1131 font_name_table[nfonts] = 0;
1133 else if (strcmp("papersize", p) == 0) {
1134 p = strtok(0, WS);
1135 if (!p) {
1136 t.error("papersize command requires an argument");
1137 return 0;
1139 int found_paper = 0;
1140 while (p) {
1141 double unscaled_paperwidth, unscaled_paperlength;
1142 if (scan_papersize(p, &papersize, &unscaled_paperlength,
1143 &unscaled_paperwidth)) {
1144 paperwidth = int(unscaled_paperwidth * res + 0.5);
1145 paperlength = int(unscaled_paperlength * res + 0.5);
1146 found_paper = 1;
1147 break;
1149 p = strtok(0, WS);
1151 if (!found_paper) {
1152 t.error("bad paper size");
1153 return 0;
1156 else if (strcmp("unscaled_charwidths", p) == 0)
1157 unscaled_charwidths = 1;
1158 else if (strcmp("pass_filenames", p) == 0)
1159 pass_filenames = 1;
1160 else if (strcmp("sizes", p) == 0) {
1161 int n = 16;
1162 sizes = new int[n];
1163 int i = 0;
1164 for (;;) {
1165 p = strtok(0, WS);
1166 while (p == 0) {
1167 if (!t.next()) {
1168 t.error("list of sizes must be terminated by `0'");
1169 return 0;
1171 p = strtok(t.buf, WS);
1173 int lower, upper;
1174 switch (sscanf(p, "%d-%d", &lower, &upper)) {
1175 case 1:
1176 upper = lower;
1177 // fall through
1178 case 2:
1179 if (lower <= upper && lower >= 0)
1180 break;
1181 // fall through
1182 default:
1183 t.error("bad size range `%1'", p);
1184 return 0;
1186 if (i + 2 > n) {
1187 int *old_sizes = sizes;
1188 sizes = new int[n*2];
1189 memcpy(sizes, old_sizes, n*sizeof(int));
1190 n *= 2;
1191 a_delete old_sizes;
1193 sizes[i++] = lower;
1194 if (lower == 0)
1195 break;
1196 sizes[i++] = upper;
1198 if (i == 1) {
1199 t.error("must have some sizes");
1200 return 0;
1203 else if (strcmp("styles", p) == 0) {
1204 int style_table_size = 5;
1205 style_table = (const char **)new char *[style_table_size];
1206 int j;
1207 for (j = 0; j < style_table_size; j++)
1208 style_table[j] = 0;
1209 int i = 0;
1210 for (;;) {
1211 p = strtok(0, WS);
1212 if (p == 0)
1213 break;
1214 // leave room for terminating 0
1215 if (i + 1 >= style_table_size) {
1216 const char **old_style_table = style_table;
1217 style_table_size *= 2;
1218 style_table = (const char **)new char*[style_table_size];
1219 for (j = 0; j < i; j++)
1220 style_table[j] = old_style_table[j];
1221 for (; j < style_table_size; j++)
1222 style_table[j] = 0;
1223 a_delete old_style_table;
1225 char *tem = new char[strlen(p) + 1];
1226 strcpy(tem, p);
1227 style_table[i++] = tem;
1230 else if (strcmp("tcommand", p) == 0)
1231 tcommand = 1;
1232 else if (strcmp("use_charnames_in_special", p) == 0)
1233 use_charnames_in_special = 1;
1234 else if (strcmp("unicode", p) == 0)
1235 is_unicode = 1;
1236 else if (strcmp("image_generator", p) == 0) {
1237 p = strtok(0, WS);
1238 if (!p) {
1239 t.error("image_generator command requires an argument");
1240 return 0;
1242 image_generator = strsave(p);
1244 else if (strcmp("charset", p) == 0)
1245 break;
1246 else if (unknown_desc_command_handler) {
1247 char *command = p;
1248 p = strtok(0, "\n");
1249 (*unknown_desc_command_handler)(command, trim_arg(p), t.fcp->path(),
1250 t.lineno);
1253 if (res == 0) {
1254 t.error("missing `res' command");
1255 return 0;
1257 if (unitwidth == 0) {
1258 t.error("missing `unitwidth' command");
1259 return 0;
1261 if (font_name_table == 0) {
1262 t.error("missing `fonts' command");
1263 return 0;
1265 if (sizes == 0) {
1266 t.error("missing `sizes' command");
1267 return 0;
1269 if (sizescale < 1) {
1270 t.error("bad `sizescale' value");
1271 return 0;
1273 if (hor < 1) {
1274 t.error("bad `hor' value");
1275 return 0;
1277 if (vert < 1) {
1278 t.error("bad `vert' value");
1279 return 0;
1281 return 1;
1284 void font::handle_unknown_font_command(const char *, const char *,
1285 const char *, int)
1289 FONT_COMMAND_HANDLER
1290 font::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func)
1292 FONT_COMMAND_HANDLER prev = unknown_desc_command_handler;
1293 unknown_desc_command_handler = func;
1294 return prev;
1297 // s-it2-mode