groff before CVS: release 1.05
[s-roff.git] / lib / font.c
blobc234327799f74ca7cc98e0ed05978fd6a757d65e
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.uucp)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 1, or (at your option) any later
10 version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file LICENSE. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 #include <stdio.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <assert.h>
25 #include <math.h>
26 #include <stdlib.h>
27 #include "errarg.h"
28 #include "error.h"
29 #include "cset.h"
30 #include "font.h"
31 #include "lib.h"
33 const char *const WS = " \t\n\r";
35 struct font_char_metric {
36 char type;
37 int code;
38 int width;
39 int height;
40 int depth;
41 int pre_math_space;
42 int italic_correction;
43 int subscript_correction;
46 struct font_kern_list {
47 int i1;
48 int i2;
49 int amount;
50 font_kern_list *next;
52 font_kern_list(int, int, int, font_kern_list * = 0);
55 struct font_widths_cache {
56 font_widths_cache *next;
57 int point_size;
58 int *width;
60 font_widths_cache(int, int, font_widths_cache *);
61 ~font_widths_cache();
64 /* text_file */
66 struct text_file {
67 FILE *fp;
68 char *path;
69 int lineno;
70 int size;
71 int skip_comments;
72 char *buf;
73 text_file(FILE *fp, char *p);
74 ~text_file();
75 int next();
76 void error(const char *format,
77 const errarg &arg1 = empty_errarg,
78 const errarg &arg2 = empty_errarg,
79 const errarg &arg3 = empty_errarg);
82 text_file::text_file(FILE *p, char *s)
83 : lineno(0), buf(0), size(0), skip_comments(1), fp(p), path(s)
87 text_file::~text_file()
89 a_delete buf;
90 a_delete path;
91 if (fp)
92 fclose(fp);
96 int text_file::next()
98 if (fp == 0)
99 return 0;
100 if (buf == 0) {
101 buf = new char [128];
102 size = 128;
104 for (;;) {
105 int i = 0;
106 for (;;) {
107 int c = getc(fp);
108 if (c == EOF)
109 break;
110 if (illegal_input_char(c))
111 error("illegal input character code `%1'", int(c));
112 else {
113 if (i + 1 >= size) {
114 char *old_buf = buf;
115 buf = new char[size*2];
116 memcpy(buf, old_buf, size);
117 a_delete old_buf;
118 size *= 2;
120 buf[i++] = c;
121 if (c == '\n')
122 break;
125 if (i == 0)
126 break;
127 buf[i] = '\0';
128 lineno++;
129 char *ptr = buf;
130 while (csspace(*ptr))
131 ptr++;
132 if (*ptr != 0 && (!skip_comments || *ptr != '#'))
133 return 1;
135 return 0;
138 void text_file::error(const char *format,
139 const errarg &arg1,
140 const errarg &arg2,
141 const errarg &arg3)
143 error_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
147 /* font functions */
149 font::font(const char *s)
150 : special(0), ligatures(0), kern_hash_table(0), space_width(0),
151 ch(0), ch_used(0), ch_size(0), ch_index(0), nindices(0), widths_cache(0)
153 name = new char[strlen(s) + 1];
154 strcpy(name, s);
155 internalname = 0;
156 slant = 0.0;
157 // load(); // for testing
160 font::~font()
162 a_delete ch;
163 a_delete ch_index;
164 if (kern_hash_table) {
165 for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) {
166 font_kern_list *kerns = kern_hash_table[i];
167 while (kerns) {
168 font_kern_list *tem = kerns;
169 kerns = kerns->next;
170 delete tem;
173 a_delete kern_hash_table;
175 a_delete name;
176 a_delete internalname;
177 while (widths_cache) {
178 font_widths_cache *tem = widths_cache;
179 widths_cache = widths_cache->next;
180 delete tem;
184 static int scale_round(int n, int x, int y)
186 assert(x >= 0 && y > 0);
187 int y2 = y/2;
188 if (x == 0)
189 return 0;
190 if (n >= 0) {
191 if (n <= (INT_MAX - y2)/x)
192 return (n*x + y2)/y;
193 return int(n*double(x)/double(y) + .5);
195 else {
196 if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
197 return (n*x - y2)/y;
198 return int(n*double(x)/double(y) - .5);
202 inline int font::scale(int w, int sz)
204 return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
207 int font::get_skew(int c, int point_size, int sl)
209 int h = get_height(c, point_size);
210 return int(h*tan((slant+sl)*M_PI/180.0) + .5);
213 int font::contains(int c)
215 return c >= 0 && c < nindices && ch_index[c] >= 0;
218 int font::is_special()
220 return special;
223 font_widths_cache::font_widths_cache(int ps, int ch_size,
224 font_widths_cache *p = 0)
225 : next(p), point_size(ps)
227 width = new int[ch_size];
228 for (int i = 0; i < ch_size; i++)
229 width[i] = -1;
232 font_widths_cache::~font_widths_cache()
234 a_delete width;
237 int font::get_width(int c, int point_size)
239 assert(c >= 0 && c < nindices);
240 int i = ch_index[c];
241 assert(i >= 0);
243 if (point_size == unitwidth)
244 return ch[i].width;
246 if (!widths_cache)
247 widths_cache = new font_widths_cache(point_size, ch_size);
248 else if (widths_cache->point_size != point_size) {
249 for (font_widths_cache **p = &widths_cache; *p; p = &(*p)->next)
250 if ((*p)->point_size == point_size)
251 break;
252 if (*p) {
253 font_widths_cache *tem = *p;
254 *p = (*p)->next;
255 tem->next = widths_cache;
256 widths_cache = tem;
258 else
259 widths_cache = new font_widths_cache(point_size, ch_size, widths_cache);
261 int &w = widths_cache->width[i];
262 if (w < 0)
263 w = scale(ch[i].width, point_size);
264 return w;
267 int font::get_height(int c, int point_size)
269 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
270 return scale(ch[ch_index[c]].height, point_size);
273 int font::get_depth(int c, int point_size)
275 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
276 return scale(ch[ch_index[c]].depth, point_size);
279 int font::get_italic_correction(int c, int point_size)
281 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
282 return scale(ch[ch_index[c]].italic_correction, point_size);
285 int font::get_left_italic_correction(int c, int point_size)
287 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
288 return scale(ch[ch_index[c]].pre_math_space, point_size);
291 int font::get_subscript_correction(int c, int point_size)
293 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
294 return scale(ch[ch_index[c]].subscript_correction, point_size);
297 int font::get_space_width(int point_size)
299 return scale(space_width, point_size);
302 font_kern_list::font_kern_list(int c1, int c2, int n, font_kern_list *p)
303 : i1(c1), i2(c2), amount(n), next(p)
307 inline int font::hash_kern(int i1, int i2)
309 int n = ((i1 << 10) + i2) % KERN_HASH_TABLE_SIZE;
310 return n < 0 ? -n : n;
313 void font::add_kern(int i1, int i2, int amount)
315 if (!kern_hash_table) {
316 kern_hash_table = new font_kern_list *[KERN_HASH_TABLE_SIZE];
317 for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++)
318 kern_hash_table[i] = 0;
320 font_kern_list **p = kern_hash_table + hash_kern(i1, i2);
321 *p = new font_kern_list(i1, i2, amount, *p);
324 int font::get_kern(int i1, int i2, int point_size)
326 if (kern_hash_table) {
327 for (font_kern_list *p = kern_hash_table[hash_kern(i1, i2)]; p; p = p->next)
328 if (i1 == p->i1 && i2 == p->i2)
329 return scale(p->amount, point_size);
331 return 0;
334 int font::has_ligature(int mask)
336 return mask & ligatures;
339 int font::get_character_type(int c)
341 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
342 return ch[ch_index[c]].type;
345 int font::get_code(int c)
347 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
348 return ch[ch_index[c]].code;
351 const char *font::get_name()
353 return name;
356 const char *font::get_internal_name()
358 return internalname;
361 void font::alloc_ch_index(int index)
363 if (nindices == 0) {
364 nindices = 128;
365 if (index >= nindices)
366 nindices = index + 10;
367 ch_index = new short[nindices];
368 for (int i = 0; i < nindices; i++)
369 ch_index[i] = -1;
371 else {
372 int old_nindices = nindices;
373 nindices *= 2;
374 if (index >= nindices)
375 nindices = index + 10;
376 short *old_ch_index = ch_index;
377 ch_index = new short[nindices];
378 memcpy(ch_index, old_ch_index, sizeof(short)*old_nindices);
379 for (int i = old_nindices; i < nindices; i++)
380 ch_index[i] = -1;
381 a_delete old_ch_index;
385 void font::extend_ch()
387 if (ch == 0)
388 ch = new font_char_metric[ch_size = 16];
389 else {
390 int old_ch_size = ch_size;
391 ch_size *= 2;
392 font_char_metric *old_ch = ch;
393 ch = new font_char_metric[ch_size];
394 memcpy(ch, old_ch, old_ch_size*sizeof(font_char_metric));
395 a_delete old_ch;
399 void font::compact()
401 for (int i = nindices - 1; i >= 0; i--)
402 if (ch_index[i] >= 0)
403 break;
404 i++;
405 if (i < nindices) {
406 short *old_ch_index = ch_index;
407 ch_index = new short[i];
408 memcpy(ch_index, old_ch_index, i*sizeof(short));
409 a_delete old_ch_index;
410 nindices = i;
412 if (ch_used < ch_size) {
413 font_char_metric *old_ch = ch;
414 ch = new font_char_metric[ch_used];
415 memcpy(ch, old_ch, ch_used*sizeof(font_char_metric));
416 a_delete old_ch;
417 ch_size = ch_used;
421 void font::add_entry(int index, const font_char_metric &metric)
423 assert(index >= 0);
424 if (index >= nindices)
425 alloc_ch_index(index);
426 assert(index < nindices);
427 if (ch_used + 1 >= ch_size)
428 extend_ch();
429 assert(ch_used + 1 < ch_size);
430 ch_index[index] = ch_used;
431 ch[ch_used++] = metric;
434 void font::copy_entry(int new_index, int old_index)
436 assert(new_index >= 0 && old_index >= 0 && old_index < nindices);
437 if (new_index >= nindices)
438 alloc_ch_index(new_index);
439 ch_index[new_index] = ch_index[old_index];
442 font *font::load_font(const char *s)
444 font *f = new font(s);
445 if (!f->load()) {
446 delete f;
447 return 0;
449 return f;
452 int font::load()
454 char *path;
455 FILE *fp;
456 if ((fp = open_file(name, &path)) == NULL) {
457 error("can't find font file `%1'", name);
458 return 0;
460 text_file t(fp, path);
461 t.skip_comments = 1;
462 char *p;
463 for (;;) {
464 if (!t.next()) {
465 t.error("missing charset command");
466 return 0;
468 p = strtok(t.buf, WS);
469 if (strcmp(p, "name") == 0) {
471 else if (strcmp(p, "spacewidth") == 0) {
472 p = strtok(0, WS);
473 int n;
474 if (p == 0 || sscanf(p, "%d", &n) != 1 || n <= 0) {
475 t.error("bad argument for spacewidth command");
476 return 0;
478 space_width = n;
480 else if (strcmp(p, "slant") == 0) {
481 p = strtok(0, WS);
482 double n;
483 if (p == 0 || sscanf(p, "%lf", &n) != 1 || n >= 90.0 || n <= -90.0) {
484 t.error("bad argument for slant command", p);
485 return 0;
487 slant = n;
489 else if (strcmp(p, "ligatures") == 0) {
490 for (;;) {
491 p = strtok(0, WS);
492 if (p == 0 || strcmp(p, "0") == 0)
493 break;
494 if (strcmp(p, "ff") == 0)
495 ligatures |= LIG_ff;
496 else if (strcmp(p, "fi") == 0)
497 ligatures |= LIG_fi;
498 else if (strcmp(p, "fl") == 0)
499 ligatures |= LIG_fl;
500 else if (strcmp(p, "ffi") == 0)
501 ligatures |= LIG_ffi;
502 else if (strcmp(p, "ffl") == 0)
503 ligatures |= LIG_ffl;
504 else {
505 t.error("unrecognised ligature `%1'", p);
506 return 0;
510 else if (strcmp(p, "internalname") == 0) {
511 p = strtok(0, WS);
512 if (!p) {
513 t.error("`internalname command requires argument");
514 return 0;
516 internalname = new char[strlen(p) + 1];
517 strcpy(internalname, p);
519 else if (strcmp(p, "special") == 0) {
520 special = 1;
522 else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) {
523 int nargv = 5;
524 const char **argv = new char *[nargv];
525 int argc = 0;
526 do {
527 if (argc < nargv) {
528 const char **old_argv = argv;
529 int old_nargv = nargv;
530 nargv *= 2;
531 argv = new char *[nargv];
532 memcpy(argv, old_argv, sizeof(char*)*old_nargv);
533 a_delete old_argv;
535 argv[argc++] = p;
536 p = strtok(0, WS);
537 } while (p != 0);
538 handle_unknown_font_command(argc, argv);
539 a_delete argv;
541 else
542 break;
544 char *command = p;
545 int had_charset = 0;
546 t.skip_comments = 0;
547 while (command) {
548 if (strcmp(command, "kernpairs") == 0) {
549 for (;;) {
550 if (!t.next()) {
551 command = 0;
552 break;
554 char *c1 = strtok(t.buf, WS);
555 if (c1 == 0)
556 continue;
557 char *c2 = strtok(0, WS);
558 if (c2 == 0) {
559 command = c1;
560 break;
562 p = strtok(0, WS);
563 if (p == 0) {
564 t.error("missing kern amount");
565 return 0;
567 int n;
568 if (sscanf(p, "%d", &n) != 1) {
569 t.error("bad kern amount `%1'", p);
570 return 0;
572 int i1 = name_to_index(c1);
573 if (i1 < 0) {
574 t.error("illegal character `%1'", c1);
575 return 0;
577 int i2 = name_to_index(c2);
578 if (i2 < 0) {
579 t.error("illegal character `%1'", c2);
580 return 0;
582 add_kern(i1, i2, n);
585 else if (strcmp(command, "charset") == 0) {
586 had_charset = 1;
587 int last_index = -1;
588 for (;;) {
589 if (!t.next()) {
590 command = 0;
591 break;
593 char *nm = strtok(t.buf, WS);
594 if (nm == 0)
595 continue; // I dont think this should happen
596 p = strtok(0, WS);
597 if (p == 0) {
598 command = nm;
599 break;
601 if (p[0] == '"') {
602 if (last_index == -1) {
603 t.error("first charset entry is duplicate");
604 return 0;
606 if (strcmp(nm, "---") == 0) {
607 t.error("unnamed character cannot be duplicate");
608 return 0;
610 int index = name_to_index(nm);
611 if (index < 0) {
612 t.error("illegal character `%1'", nm);
613 return 0;
615 copy_entry(index, last_index);
617 else {
618 font_char_metric metric;
619 metric.height = 0;
620 metric.depth = 0;
621 metric.pre_math_space = 0;
622 metric.italic_correction = 0;
623 metric.subscript_correction = 0;
624 int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
625 &metric.width, &metric.height, &metric.depth,
626 &metric.italic_correction,
627 &metric.pre_math_space,
628 &metric.subscript_correction);
629 if (nparms < 1) {
630 t.error("bad width for `%1'", nm);
631 return 0;
633 p = strtok(0, WS);
634 if (p == 0) {
635 t.error("missing character type for `%1'", nm);
636 return 0;
638 int type;
639 if (sscanf(p, "%d", &type) != 1) {
640 t.error("bad character type for `%1'", nm);
641 return 0;
643 if (type < 0 || type > 255) {
644 t.error("character code `%1' out of range", type);
645 return 0;
647 metric.type = type;
648 p = strtok(0, WS);
649 if (p == 0) {
650 t.error("missing code for `%1'", nm);
651 return 0;
653 char *ptr;
654 metric.code = (int)strtol(p, &ptr, 0);
655 if (metric.code == 0 && ptr == p) {
656 t.error("bad code `%1' for character `%2'", p, nm);
657 return 0;
659 if (strcmp(nm, "---") == 0) {
660 last_index = number_to_index(metric.code);
661 add_entry(last_index, metric);
663 else {
664 last_index = name_to_index(nm);
665 if (last_index < 0) {
666 t.error("illegal character `%1'", nm);
667 return 0;
669 add_entry(last_index, metric);
670 copy_entry(number_to_index(metric.code), last_index);
674 if (last_index == -1) {
675 t.error("I didn't seem to find any characters");
676 return 0;
679 else {
680 t.error("unrecognised command `%1' after `kernpairs' or `charset' command", command);
681 return 0;
684 if (!had_charset) {
685 t.error("missing charset command");
686 return 0;
688 if (space_width == 0)
689 space_width = scale_round(unitwidth, res, 72*3*sizescale);
690 compact();
691 return 1;
694 static struct {
695 const char *command;
696 int *ptr;
697 } table[] = {
698 "res", &font::res,
699 "hor", &font::hor,
700 "vert", &font::vert,
701 "unitwidth", &font::unitwidth,
702 "paperwidth", &font::paperwidth,
703 "paperlength", &font::paperlength,
704 "spare1", &font::biggestfont,
705 "biggestfont", &font::biggestfont,
706 "spare2", &font::spare2,
707 "sizescale", &font::sizescale
711 int font::load_desc()
713 int nfonts = 0;
714 FILE *fp;
715 char *path;
716 if ((fp = open_file("DESC", &path)) == 0) {
717 error("can't open `DESC'");
718 return 0;
720 text_file t(fp, path);
721 t.skip_comments = 1;
722 res = 0;
723 while (t.next()) {
724 char *p = strtok(t.buf, WS);
725 int found = 0;
726 for (int i = 0; !found && i < sizeof(table)/sizeof(table[0]); i++)
727 if (strcmp(table[i].command, p) == 0)
728 found = 1;
729 if (found) {
730 char *q = strtok(0, WS);
731 if (!q) {
732 t.error("missing value for command `%1'", p);
733 return 0;
735 //int *ptr = &(this->*(table[i-1].ptr));
736 int *ptr = table[i-1].ptr;
737 if (sscanf(q, "%d", ptr) != 1) {
738 t.error("bad number `%1'", q);
739 return 0;
742 else if (strcmp("tcommand", p) == 0) {
743 tcommand = 1;
745 else if (strcmp("family", p) == 0) {
746 p = strtok(0, WS);
747 if (!p) {
748 t.error("family command requires an argument");
749 return 0;
751 char *tem = new char[strlen(p)+1];
752 strcpy(tem, p);
753 family = tem;
755 else if (strcmp("fonts", p) == 0) {
756 p = strtok(0, WS);
757 if (!p || sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
758 t.error("bad number of fonts `%1'", p);
759 return 0;
761 font_name_table = new char *[nfonts+1];
762 for (int i = 0; i < nfonts; i++) {
763 p = strtok(0, WS);
764 while (p == 0) {
765 if (!t.next()) {
766 t.error("end of file while reading list of fonts");
767 return 0;
769 p = strtok(t.buf, WS);
771 char *temp = new char[strlen(p)+1];
772 strcpy(temp, p);
773 font_name_table[i] = temp;
775 p = strtok(0, WS);
776 if (p != 0) {
777 t.error("font count does not match number of fonts");
778 return 0;
780 font_name_table[nfonts] = 0;
782 else if (strcmp("sizes", p) == 0) {
783 int n = 16;
784 sizes = new int[n];
785 int i = 0;
786 for (;;) {
787 p = strtok(0, WS);
788 while (p == 0) {
789 if (!t.next()) {
790 t.error("list of sizes must be terminated by `0'");
791 return 0;
793 p = strtok(t.buf, WS);
795 int lower, upper;
796 switch (sscanf(p, "%d-%d", &lower, &upper)) {
797 case 1:
798 upper = lower;
799 // fall through
800 case 2:
801 if (lower <= upper && lower >= 0)
802 break;
803 // fall through
804 default:
805 t.error("bad size range `%1'", p);
806 return 0;
808 if (i + 2 > n) {
809 int *old_sizes = sizes;
810 sizes = new int[n*2];
811 memcpy(sizes, old_sizes, n*sizeof(int));
812 n *= 2;
813 a_delete old_sizes;
815 sizes[i++] = lower;
816 if (lower == 0)
817 break;
818 sizes[i++] = upper;
820 if (i == 1) {
821 t.error("must have some sizes");
822 return 0;
825 else if (strcmp("styles", p) == 0) {
826 int style_table_size = 5;
827 style_table = new char *[style_table_size];
828 for (int j = 0; j < style_table_size; j++)
829 style_table[j] = 0;
830 int i = 0;
831 for (;;) {
832 p = strtok(0, WS);
833 if (p == 0)
834 break;
835 // leave room for terminating 0
836 if (i + 1 >= style_table_size) {
837 const char **old_style_table = style_table;
838 style_table_size *= 2;
839 style_table = new char*[style_table_size];
840 for (j = 0; j < i; j++)
841 style_table[j] = old_style_table[j];
842 for (; j < style_table_size; j++)
843 style_table[j] = 0;
844 a_delete old_style_table;
846 char *tem = new char[strlen(p) + 1];
847 strcpy(tem, p);
848 style_table[i++] = tem;
851 else if (strcmp("charset", p) == 0)
852 break;
854 if (res == 0) {
855 t.error("missing `res' command");
856 return 0;
858 if (unitwidth == 0) {
859 t.error("missing `unitwidth' command");
860 return 0;
862 if (font_name_table == 0) {
863 t.error("missing `fonts' commmand");
864 return 0;
866 if (sizes == 0) {
867 t.error("missing `sizes' command");
868 return 0;
870 if (sizescale < 1) {
871 t.error("bad `sizescale' value");
872 return 0;
874 if (hor < 1) {
875 t.error("bad `hor' value");
876 return 0;
878 if (vert < 1) {
879 t.error("bad `vert' value");
880 return 0;
882 return 1;
885 void font::handle_unknown_font_command(int, const char **)