2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
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
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
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. */
34 const char *const WS = " \t\n\r";
36 struct font_char_metric {
43 int italic_correction;
44 int subscript_correction;
45 char *special_device_coding;
48 struct font_kern_list {
54 font_kern_list(int, int, int, font_kern_list * = 0);
57 struct font_widths_cache {
58 font_widths_cache *next;
62 font_widths_cache(int, int, font_widths_cache * = 0);
75 text_file(FILE *fp, char *p);
78 void error(const char *format,
79 const errarg &arg1 = empty_errarg,
80 const errarg &arg2 = empty_errarg,
81 const errarg &arg3 = empty_errarg);
84 text_file::text_file(FILE *p, char *s)
85 : fp(p), path(s), lineno(0), size(0), skip_comments(1), buf(0)
89 text_file::~text_file()
111 if (invalid_input_char(c))
112 error("invalid input character code `%1'", int(c));
116 buf = new char[size*2];
117 memcpy(buf, old_buf, size);
131 while (csspace(*ptr))
133 if (*ptr != 0 && (!skip_comments || *ptr != '#'))
139 void text_file::error(const char *format,
144 error_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
150 font::font(const char *s)
151 : ligatures(0), kern_hash_table(0), space_width(0), ch_index(0), nindices(0),
152 ch(0), ch_used(0), ch_size(0), special(0), widths_cache(0)
154 name = new char[strlen(s) + 1];
158 // load(); // for testing
163 for (int i = 0; i < ch_used; i++)
164 if (ch[i].special_device_coding)
165 a_delete ch[i].special_device_coding;
168 if (kern_hash_table) {
169 for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) {
170 font_kern_list *kerns = kern_hash_table[i];
172 font_kern_list *tem = kerns;
177 a_delete kern_hash_table;
180 a_delete internalname;
181 while (widths_cache) {
182 font_widths_cache *tem = widths_cache;
183 widths_cache = widths_cache->next;
188 static int scale_round(int n, int x, int y)
190 assert(x >= 0 && y > 0);
195 if (n <= (INT_MAX - y2)/x)
197 return int(n*double(x)/double(y) + .5);
200 if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
202 return int(n*double(x)/double(y) - .5);
206 inline int font::scale(int w, int sz)
208 return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
211 int font::unit_scale(double *value, char unit)
213 // we scale everything to inch
239 int font::get_skew(int c, int point_size, int sl)
241 int h = get_height(c, point_size);
242 return int(h*tan((slant+sl)*PI/180.0) + .5);
245 int font::contains(int c)
247 return c >= 0 && c < nindices && ch_index[c] >= 0;
250 int font::is_special()
255 font_widths_cache::font_widths_cache(int ps, int ch_size,
256 font_widths_cache *p)
257 : next(p), point_size(ps)
259 width = new int[ch_size];
260 for (int i = 0; i < ch_size; i++)
264 font_widths_cache::~font_widths_cache()
269 int font::get_width(int c, int point_size)
271 assert(c >= 0 && c < nindices);
275 if (point_size == unitwidth)
279 widths_cache = new font_widths_cache(point_size, ch_size);
280 else if (widths_cache->point_size != point_size) {
281 font_widths_cache **p;
282 for (p = &widths_cache; *p; p = &(*p)->next)
283 if ((*p)->point_size == point_size)
286 font_widths_cache *tem = *p;
288 tem->next = widths_cache;
292 widths_cache = new font_widths_cache(point_size, ch_size, widths_cache);
294 int &w = widths_cache->width[i];
296 w = scale(ch[i].width, point_size);
300 int font::get_height(int c, int point_size)
302 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
303 return scale(ch[ch_index[c]].height, point_size);
306 int font::get_depth(int c, int point_size)
308 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
309 return scale(ch[ch_index[c]].depth, point_size);
312 int font::get_italic_correction(int c, int point_size)
314 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
315 return scale(ch[ch_index[c]].italic_correction, point_size);
318 int font::get_left_italic_correction(int c, int point_size)
320 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
321 return scale(ch[ch_index[c]].pre_math_space, point_size);
324 int font::get_subscript_correction(int c, int point_size)
326 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
327 return scale(ch[ch_index[c]].subscript_correction, point_size);
330 int font::get_space_width(int point_size)
332 return scale(space_width, point_size);
335 font_kern_list::font_kern_list(int c1, int c2, int n, font_kern_list *p)
336 : i1(c1), i2(c2), amount(n), next(p)
340 inline int font::hash_kern(int i1, int i2)
342 int n = ((i1 << 10) + i2) % KERN_HASH_TABLE_SIZE;
343 return n < 0 ? -n : n;
346 void font::add_kern(int i1, int i2, int amount)
348 if (!kern_hash_table) {
349 kern_hash_table = new font_kern_list *[KERN_HASH_TABLE_SIZE];
350 for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++)
351 kern_hash_table[i] = 0;
353 font_kern_list **p = kern_hash_table + hash_kern(i1, i2);
354 *p = new font_kern_list(i1, i2, amount, *p);
357 int font::get_kern(int i1, int i2, int point_size)
359 if (kern_hash_table) {
360 for (font_kern_list *p = kern_hash_table[hash_kern(i1, i2)]; p; p = p->next)
361 if (i1 == p->i1 && i2 == p->i2)
362 return scale(p->amount, point_size);
367 int font::has_ligature(int mask)
369 return mask & ligatures;
372 int font::get_character_type(int c)
374 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
375 return ch[ch_index[c]].type;
378 int font::get_code(int c)
380 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
381 return ch[ch_index[c]].code;
384 const char *font::get_name()
389 const char *font::get_internal_name()
394 const char *font::get_special_device_encoding(int c)
396 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
397 return( ch[ch_index[c]].special_device_coding );
400 void font::alloc_ch_index(int index)
404 if (index >= nindices)
405 nindices = index + 10;
406 ch_index = new short[nindices];
407 for (int i = 0; i < nindices; i++)
411 int old_nindices = nindices;
413 if (index >= nindices)
414 nindices = index + 10;
415 short *old_ch_index = ch_index;
416 ch_index = new short[nindices];
417 memcpy(ch_index, old_ch_index, sizeof(short)*old_nindices);
418 for (int i = old_nindices; i < nindices; i++)
420 a_delete old_ch_index;
424 void font::extend_ch()
427 ch = new font_char_metric[ch_size = 16];
429 int old_ch_size = ch_size;
431 font_char_metric *old_ch = ch;
432 ch = new font_char_metric[ch_size];
433 memcpy(ch, old_ch, old_ch_size*sizeof(font_char_metric));
441 for (i = nindices - 1; i >= 0; i--)
442 if (ch_index[i] >= 0)
446 short *old_ch_index = ch_index;
447 ch_index = new short[i];
448 memcpy(ch_index, old_ch_index, i*sizeof(short));
449 a_delete old_ch_index;
452 if (ch_used < ch_size) {
453 font_char_metric *old_ch = ch;
454 ch = new font_char_metric[ch_used];
455 memcpy(ch, old_ch, ch_used*sizeof(font_char_metric));
461 void font::add_entry(int index, const font_char_metric &metric)
464 if (index >= nindices)
465 alloc_ch_index(index);
466 assert(index < nindices);
467 if (ch_used + 1 >= ch_size)
469 assert(ch_used + 1 < ch_size);
470 ch_index[index] = ch_used;
471 ch[ch_used++] = metric;
474 void font::copy_entry(int new_index, int old_index)
476 assert(new_index >= 0 && old_index >= 0 && old_index < nindices);
477 if (new_index >= nindices)
478 alloc_ch_index(new_index);
479 ch_index[new_index] = ch_index[old_index];
482 font *font::load_font(const char *s, int *not_found)
484 font *f = new font(s);
485 if (!f->load(not_found)) {
492 static char *trim_arg(char *p)
498 char *q = strchr(p, '\0');
499 while (q > p && csspace(q[-1]))
505 int font::scan_papersize(const char *p,
506 const char **size, double *length, double *width)
515 if (sscanf(pp, "%lf%1[ipPc],%lf%1[ipPc]", &l, lu, &w, wu) == 4
517 && unit_scale(&l, lu[0]) && unit_scale(&w, wu[0])) {
529 for (i = 0; i < NUM_PAPERSIZES; i++)
530 if (strcasecmp(papersizes[i].name, pp) == 0) {
532 *length = papersizes[i].length;
534 *width = papersizes[i].width;
536 *size = papersizes[i].name;
540 FILE *f = fopen(p, "r");
545 char *linep = strchr(line, '\0');
546 // skip final newline, if any
547 if (*(--linep) == '\n')
557 // If the font can't be found, then if not_found is non-NULL, it will be set
558 // to 1 otherwise a message will be printed.
560 int font::load(int *not_found)
564 if ((fp = open_file(name, &path)) == NULL) {
568 error("can't find font file `%1'", name);
571 text_file t(fp, path);
576 t.error("missing charset command");
579 p = strtok(t.buf, WS);
580 if (strcmp(p, "name") == 0) {
582 else if (strcmp(p, "spacewidth") == 0) {
585 if (p == 0 || sscanf(p, "%d", &n) != 1 || n <= 0) {
586 t.error("bad argument for spacewidth command");
591 else if (strcmp(p, "slant") == 0) {
594 if (p == 0 || sscanf(p, "%lf", &n) != 1 || n >= 90.0 || n <= -90.0) {
595 t.error("bad argument for slant command", p);
600 else if (strcmp(p, "ligatures") == 0) {
603 if (p == 0 || strcmp(p, "0") == 0)
605 if (strcmp(p, "ff") == 0)
607 else if (strcmp(p, "fi") == 0)
609 else if (strcmp(p, "fl") == 0)
611 else if (strcmp(p, "ffi") == 0)
612 ligatures |= LIG_ffi;
613 else if (strcmp(p, "ffl") == 0)
614 ligatures |= LIG_ffl;
616 t.error("unrecognised ligature `%1'", p);
621 else if (strcmp(p, "internalname") == 0) {
624 t.error("`internalname command requires argument");
627 internalname = new char[strlen(p) + 1];
628 strcpy(internalname, p);
630 else if (strcmp(p, "special") == 0) {
633 else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) {
636 handle_unknown_font_command(command, trim_arg(p), t.path, t.lineno);
645 if (strcmp(command, "kernpairs") == 0) {
651 char *c1 = strtok(t.buf, WS);
654 char *c2 = strtok(0, WS);
661 t.error("missing kern amount");
665 if (sscanf(p, "%d", &n) != 1) {
666 t.error("bad kern amount `%1'", p);
669 int i1 = name_to_index(c1);
671 t.error("invalid character `%1'", c1);
674 int i2 = name_to_index(c2);
676 t.error("invalid character `%1'", c2);
682 else if (strcmp(command, "charset") == 0) {
690 char *nm = strtok(t.buf, WS);
692 continue; // I dont think this should happen
699 if (last_index == -1) {
700 t.error("first charset entry is duplicate");
703 if (strcmp(nm, "---") == 0) {
704 t.error("unnamed character cannot be duplicate");
707 int index = name_to_index(nm);
709 t.error("invalid character `%1'", nm);
712 copy_entry(index, last_index);
715 font_char_metric metric;
718 metric.pre_math_space = 0;
719 metric.italic_correction = 0;
720 metric.subscript_correction = 0;
721 int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
722 &metric.width, &metric.height, &metric.depth,
723 &metric.italic_correction,
724 &metric.pre_math_space,
725 &metric.subscript_correction);
727 t.error("bad width for `%1'", nm);
732 t.error("missing character type for `%1'", nm);
736 if (sscanf(p, "%d", &type) != 1) {
737 t.error("bad character type for `%1'", nm);
740 if (type < 0 || type > 255) {
741 t.error("character code `%1' out of range", type);
747 t.error("missing code for `%1'", nm);
751 metric.code = (int)strtol(p, &ptr, 0);
752 if (metric.code == 0 && ptr == p) {
753 t.error("bad code `%1' for character `%2'", p, nm);
757 if ((p == NULL) || (strcmp(p, "--") == 0)) {
758 metric.special_device_coding = NULL;
761 char *name = new char[strlen(p) + 1];
763 metric.special_device_coding = name;
765 if (strcmp(nm, "---") == 0) {
766 last_index = number_to_index(metric.code);
767 add_entry(last_index, metric);
770 last_index = name_to_index(nm);
771 if (last_index < 0) {
772 t.error("invalid character `%1'", nm);
775 add_entry(last_index, metric);
776 copy_entry(number_to_index(metric.code), last_index);
780 if (last_index == -1) {
781 t.error("I didn't seem to find any characters");
786 t.error("unrecognised command `%1' after `kernpairs' or `charset' command", command);
791 t.error("missing charset command");
794 if (space_width == 0)
795 space_width = scale_round(unitwidth, res, 72*3*sizescale);
804 { "res", &font::res },
805 { "hor", &font::hor },
806 { "vert", &font::vert },
807 { "unitwidth", &font::unitwidth },
808 { "paperwidth", &font::paperwidth },
809 { "paperlength", &font::paperlength },
810 { "spare1", &font::biggestfont },
811 { "biggestfont", &font::biggestfont },
812 { "spare2", &font::spare2 },
813 { "sizescale", &font::sizescale }
816 int font::load_desc()
821 if ((fp = open_file("DESC", &path)) == 0) {
822 error("can't find `DESC' file");
825 text_file t(fp, path);
829 char *p = strtok(t.buf, WS);
832 for (idx = 0; !found && idx < sizeof(table)/sizeof(table[0]); idx++)
833 if (strcmp(table[idx].command, p) == 0)
836 char *q = strtok(0, WS);
838 t.error("missing value for command `%1'", p);
841 //int *ptr = &(this->*(table[idx-1].ptr));
842 int *ptr = table[idx-1].ptr;
843 if (sscanf(q, "%d", ptr) != 1) {
844 t.error("bad number `%1'", q);
848 else if (strcmp("family", p) == 0) {
851 t.error("family command requires an argument");
854 char *tem = new char[strlen(p)+1];
858 else if (strcmp("fonts", p) == 0) {
860 if (!p || sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
861 t.error("bad number of fonts `%1'", p);
864 font_name_table = (const char **)new char *[nfonts+1];
865 for (int i = 0; i < nfonts; i++) {
869 t.error("end of file while reading list of fonts");
872 p = strtok(t.buf, WS);
874 char *temp = new char[strlen(p)+1];
876 font_name_table[i] = temp;
880 t.error("font count does not match number of fonts");
883 font_name_table[nfonts] = 0;
885 else if (strcmp("papersize", p) == 0) {
888 t.error("papersize command requires an argument");
893 double unscaled_paperwidth, unscaled_paperlength;
894 if (scan_papersize(p, &papersize, &unscaled_paperlength,
895 &unscaled_paperwidth)) {
896 paperwidth = int(unscaled_paperwidth * res + 0.5);
897 paperlength = int(unscaled_paperlength * res + 0.5);
904 t.error("bad paper size");
908 else if (strcmp("pass_filenames", p) == 0)
910 else if (strcmp("sizes", p) == 0) {
918 t.error("list of sizes must be terminated by `0'");
921 p = strtok(t.buf, WS);
924 switch (sscanf(p, "%d-%d", &lower, &upper)) {
929 if (lower <= upper && lower >= 0)
933 t.error("bad size range `%1'", p);
937 int *old_sizes = sizes;
938 sizes = new int[n*2];
939 memcpy(sizes, old_sizes, n*sizeof(int));
949 t.error("must have some sizes");
953 else if (strcmp("styles", p) == 0) {
954 int style_table_size = 5;
955 style_table = (const char **)new char *[style_table_size];
957 for (j = 0; j < style_table_size; j++)
964 // leave room for terminating 0
965 if (i + 1 >= style_table_size) {
966 const char **old_style_table = style_table;
967 style_table_size *= 2;
968 style_table = (const char **)new char*[style_table_size];
969 for (j = 0; j < i; j++)
970 style_table[j] = old_style_table[j];
971 for (; j < style_table_size; j++)
973 a_delete old_style_table;
975 char *tem = new char[strlen(p) + 1];
977 style_table[i++] = tem;
980 else if (strcmp("tcommand", p) == 0)
982 else if (strcmp("use_charnames_in_special", p) == 0)
983 use_charnames_in_special = 1;
984 else if (strcmp("charset", p) == 0)
986 else if (unknown_desc_command_handler) {
989 (*unknown_desc_command_handler)(command, trim_arg(p), t.path, t.lineno);
993 t.error("missing `res' command");
996 if (unitwidth == 0) {
997 t.error("missing `unitwidth' command");
1000 if (font_name_table == 0) {
1001 t.error("missing `fonts' command");
1005 t.error("missing `sizes' command");
1008 if (sizescale < 1) {
1009 t.error("bad `sizescale' value");
1013 t.error("bad `hor' value");
1017 t.error("bad `vert' value");
1023 void font::handle_unknown_font_command(const char *, const char *,
1028 FONT_COMMAND_HANDLER
1029 font::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func)
1031 FONT_COMMAND_HANDLER prev = unknown_desc_command_handler;
1032 unknown_desc_command_handler = func;