2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
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, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, 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);
76 text_file(FILE *fp, char *p);
79 void error(const char *format,
80 const errarg &arg1 = empty_errarg,
81 const errarg &arg2 = empty_errarg,
82 const errarg &arg3 = empty_errarg);
85 text_file::text_file(FILE *p, char *s)
86 : fp(p), path(s), lineno(0), size(0), skip_comments(1), silent(0), buf(0)
90 text_file::~text_file()
112 if (invalid_input_char(c))
113 error("invalid input character code `%1'", int(c));
117 buf = new char[size*2];
118 memcpy(buf, old_buf, size);
132 while (csspace(*ptr))
134 if (*ptr != 0 && (!skip_comments || *ptr != '#'))
140 void text_file::error(const char *format,
146 error_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
152 font::font(const char *s)
153 : ligatures(0), kern_hash_table(0), space_width(0), ch_index(0), nindices(0),
154 ch(0), ch_used(0), ch_size(0), special(0), widths_cache(0)
156 name = new char[strlen(s) + 1];
160 // load(); // for testing
165 for (int i = 0; i < ch_used; i++)
166 if (ch[i].special_device_coding)
167 a_delete ch[i].special_device_coding;
170 if (kern_hash_table) {
171 for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) {
172 font_kern_list *kerns = kern_hash_table[i];
174 font_kern_list *tem = kerns;
179 a_delete kern_hash_table;
182 a_delete internalname;
183 while (widths_cache) {
184 font_widths_cache *tem = widths_cache;
185 widths_cache = widths_cache->next;
190 static int scale_round(int n, int x, int y)
192 assert(x >= 0 && y > 0);
197 if (n <= (INT_MAX - y2)/x)
199 return int(n*double(x)/double(y) + .5);
202 if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
204 return int(n*double(x)/double(y) - .5);
208 inline int font::scale(int w, int sz)
210 return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
213 int font::unit_scale(double *value, char unit)
215 // we scale everything to inch
241 int font::get_skew(int c, int point_size, int sl)
243 int h = get_height(c, point_size);
244 return int(h*tan((slant+sl)*PI/180.0) + .5);
247 int font::contains(int c)
249 return c >= 0 && c < nindices && ch_index[c] >= 0;
252 int font::is_special()
257 font_widths_cache::font_widths_cache(int ps, int ch_size,
258 font_widths_cache *p)
259 : next(p), point_size(ps)
261 width = new int[ch_size];
262 for (int i = 0; i < ch_size; i++)
266 font_widths_cache::~font_widths_cache()
271 int font::get_width(int c, int point_size)
273 assert(c >= 0 && c < nindices);
277 if (point_size == unitwidth || font::unscaled_charwidths)
281 widths_cache = new font_widths_cache(point_size, ch_size);
282 else if (widths_cache->point_size != point_size) {
283 font_widths_cache **p;
284 for (p = &widths_cache; *p; p = &(*p)->next)
285 if ((*p)->point_size == point_size)
288 font_widths_cache *tem = *p;
290 tem->next = widths_cache;
294 widths_cache = new font_widths_cache(point_size, ch_size, widths_cache);
296 int &w = widths_cache->width[i];
298 w = scale(ch[i].width, point_size);
302 int font::get_height(int c, int point_size)
304 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
305 return scale(ch[ch_index[c]].height, point_size);
308 int font::get_depth(int c, int point_size)
310 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
311 return scale(ch[ch_index[c]].depth, point_size);
314 int font::get_italic_correction(int c, int point_size)
316 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
317 return scale(ch[ch_index[c]].italic_correction, point_size);
320 int font::get_left_italic_correction(int c, int point_size)
322 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
323 return scale(ch[ch_index[c]].pre_math_space, point_size);
326 int font::get_subscript_correction(int c, int point_size)
328 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
329 return scale(ch[ch_index[c]].subscript_correction, point_size);
332 int font::get_space_width(int point_size)
334 return scale(space_width, point_size);
337 font_kern_list::font_kern_list(int c1, int c2, int n, font_kern_list *p)
338 : i1(c1), i2(c2), amount(n), next(p)
342 inline int font::hash_kern(int i1, int i2)
344 int n = ((i1 << 10) + i2) % KERN_HASH_TABLE_SIZE;
345 return n < 0 ? -n : n;
348 void font::add_kern(int i1, int i2, int amount)
350 if (!kern_hash_table) {
351 kern_hash_table = new font_kern_list *[int(KERN_HASH_TABLE_SIZE)];
352 for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++)
353 kern_hash_table[i] = 0;
355 font_kern_list **p = kern_hash_table + hash_kern(i1, i2);
356 *p = new font_kern_list(i1, i2, amount, *p);
359 int font::get_kern(int i1, int i2, int point_size)
361 if (kern_hash_table) {
362 for (font_kern_list *p = kern_hash_table[hash_kern(i1, i2)]; p; p = p->next)
363 if (i1 == p->i1 && i2 == p->i2)
364 return scale(p->amount, point_size);
369 int font::has_ligature(int mask)
371 return mask & ligatures;
374 int font::get_character_type(int c)
376 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
377 return ch[ch_index[c]].type;
380 int font::get_code(int c)
382 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
383 return ch[ch_index[c]].code;
386 const char *font::get_name()
391 const char *font::get_internal_name()
396 const char *font::get_special_device_encoding(int c)
398 assert(c >= 0 && c < nindices && ch_index[c] >= 0);
399 return ch[ch_index[c]].special_device_coding;
402 const char *font::get_image_generator()
404 return image_generator;
407 void font::alloc_ch_index(int idx)
413 ch_index = new int[nindices];
414 for (int i = 0; i < nindices; i++)
418 int old_nindices = nindices;
422 int *old_ch_index = ch_index;
423 ch_index = new int[nindices];
424 memcpy(ch_index, old_ch_index, sizeof(int)*old_nindices);
425 for (int i = old_nindices; i < nindices; i++)
427 a_delete old_ch_index;
431 void font::extend_ch()
434 ch = new font_char_metric[ch_size = 16];
436 int old_ch_size = ch_size;
438 font_char_metric *old_ch = ch;
439 ch = new font_char_metric[ch_size];
440 memcpy(ch, old_ch, old_ch_size*sizeof(font_char_metric));
448 for (i = nindices - 1; i >= 0; i--)
449 if (ch_index[i] >= 0)
453 int *old_ch_index = ch_index;
454 ch_index = new int[i];
455 memcpy(ch_index, old_ch_index, i*sizeof(int));
456 a_delete old_ch_index;
459 if (ch_used < ch_size) {
460 font_char_metric *old_ch = ch;
461 ch = new font_char_metric[ch_used];
462 memcpy(ch, old_ch, ch_used*sizeof(font_char_metric));
468 void font::add_entry(int idx, const font_char_metric &metric)
473 assert(idx < nindices);
474 if (ch_used + 1 >= ch_size)
476 assert(ch_used + 1 < ch_size);
477 ch_index[idx] = ch_used;
478 ch[ch_used++] = metric;
481 void font::copy_entry(int new_index, int old_index)
483 assert(new_index >= 0 && old_index >= 0 && old_index < nindices);
484 if (new_index >= nindices)
485 alloc_ch_index(new_index);
486 ch_index[new_index] = ch_index[old_index];
489 font *font::load_font(const char *s, int *not_found, int head_only)
491 font *f = new font(s);
492 if (!f->load(not_found, head_only)) {
499 static char *trim_arg(char *p)
505 char *q = strchr(p, '\0');
506 while (q > p && csspace(q[-1]))
512 int font::scan_papersize(const char *p,
513 const char **size, double *length, double *width)
522 if (sscanf(pp, "%lf%1[ipPc],%lf%1[ipPc]", &l, lu, &w, wu) == 4
524 && unit_scale(&l, lu[0]) && unit_scale(&w, wu[0])) {
536 for (i = 0; i < NUM_PAPERSIZES; i++)
537 if (strcasecmp(papersizes[i].name, pp) == 0) {
539 *length = papersizes[i].length;
541 *width = papersizes[i].width;
543 *size = papersizes[i].name;
547 FILE *f = fopen(p, "r");
552 char *linep = strchr(line, '\0');
553 // skip final newline, if any
554 if (*(--linep) == '\n')
564 // If the font can't be found, then if not_found is non-NULL, it will be set
565 // to 1 otherwise a message will be printed.
567 int font::load(int *not_found, int head_only)
571 if ((fp = open_file(name, &path)) == NULL) {
575 error("can't find font file `%1'", name);
578 text_file t(fp, path);
580 t.silent = head_only;
584 t.error("missing charset command");
587 p = strtok(t.buf, WS);
588 if (strcmp(p, "name") == 0) {
590 else if (strcmp(p, "spacewidth") == 0) {
593 if (p == 0 || sscanf(p, "%d", &n) != 1 || n <= 0) {
594 t.error("bad argument for spacewidth command");
599 else if (strcmp(p, "slant") == 0) {
602 if (p == 0 || sscanf(p, "%lf", &n) != 1 || n >= 90.0 || n <= -90.0) {
603 t.error("bad argument for slant command", p);
608 else if (strcmp(p, "ligatures") == 0) {
611 if (p == 0 || strcmp(p, "0") == 0)
613 if (strcmp(p, "ff") == 0)
615 else if (strcmp(p, "fi") == 0)
617 else if (strcmp(p, "fl") == 0)
619 else if (strcmp(p, "ffi") == 0)
620 ligatures |= LIG_ffi;
621 else if (strcmp(p, "ffl") == 0)
622 ligatures |= LIG_ffl;
624 t.error("unrecognised ligature `%1'", p);
629 else if (strcmp(p, "internalname") == 0) {
632 t.error("`internalname command requires argument");
635 internalname = new char[strlen(p) + 1];
636 strcpy(internalname, p);
638 else if (strcmp(p, "special") == 0) {
641 else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) {
644 handle_unknown_font_command(command, trim_arg(p), t.path, t.lineno);
655 if (strcmp(command, "kernpairs") == 0) {
661 char *c1 = strtok(t.buf, WS);
664 char *c2 = strtok(0, WS);
671 t.error("missing kern amount");
675 if (sscanf(p, "%d", &n) != 1) {
676 t.error("bad kern amount `%1'", p);
679 int i1 = name_to_index(c1);
681 t.error("invalid character `%1'", c1);
684 int i2 = name_to_index(c2);
686 t.error("invalid character `%1'", c2);
692 else if (strcmp(command, "charset") == 0) {
700 char *nm = strtok(t.buf, WS);
702 continue; // I dont think this should happen
709 if (last_index == -1) {
710 t.error("first charset entry is duplicate");
713 if (strcmp(nm, "---") == 0) {
714 t.error("unnamed character cannot be duplicate");
717 int idx = name_to_index(nm);
719 t.error("invalid character `%1'", nm);
722 copy_entry(idx, last_index);
725 font_char_metric metric;
728 metric.pre_math_space = 0;
729 metric.italic_correction = 0;
730 metric.subscript_correction = 0;
731 int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
732 &metric.width, &metric.height, &metric.depth,
733 &metric.italic_correction,
734 &metric.pre_math_space,
735 &metric.subscript_correction);
737 t.error("bad width for `%1'", nm);
742 t.error("missing character type for `%1'", nm);
746 if (sscanf(p, "%d", &type) != 1) {
747 t.error("bad character type for `%1'", nm);
750 if (type < 0 || type > 255) {
751 t.error("character type `%1' out of range", type);
757 t.error("missing code for `%1'", nm);
761 metric.code = (int)strtol(p, &ptr, 0);
762 if (metric.code == 0 && ptr == p) {
763 t.error("bad code `%1' for character `%2'", p, nm);
767 if ((p == NULL) || (strcmp(p, "--") == 0)) {
768 metric.special_device_coding = NULL;
771 char *nam = new char[strlen(p) + 1];
773 metric.special_device_coding = nam;
775 if (strcmp(nm, "---") == 0) {
776 last_index = number_to_index(metric.code);
777 add_entry(last_index, metric);
780 last_index = name_to_index(nm);
781 if (last_index < 0) {
782 t.error("invalid character `%1'", nm);
785 add_entry(last_index, metric);
786 copy_entry(number_to_index(metric.code), last_index);
790 if (last_index == -1) {
791 t.error("I didn't seem to find any characters");
796 t.error("unrecognised command `%1' after `kernpairs' or `charset' command", command);
801 t.error("missing charset command");
804 if (space_width == 0)
805 space_width = scale_round(unitwidth, res, 72*3*sizescale);
814 { "res", &font::res },
815 { "hor", &font::hor },
816 { "vert", &font::vert },
817 { "unitwidth", &font::unitwidth },
818 { "paperwidth", &font::paperwidth },
819 { "paperlength", &font::paperlength },
820 { "spare1", &font::biggestfont },
821 { "biggestfont", &font::biggestfont },
822 { "spare2", &font::spare2 },
823 { "sizescale", &font::sizescale },
826 int font::load_desc()
831 if ((fp = open_file("DESC", &path)) == 0) {
832 error("can't find `DESC' file");
835 text_file t(fp, path);
839 char *p = strtok(t.buf, WS);
842 for (idx = 0; !found && idx < sizeof(table)/sizeof(table[0]); idx++)
843 if (strcmp(table[idx].command, p) == 0)
846 char *q = strtok(0, WS);
848 t.error("missing value for command `%1'", p);
851 //int *ptr = &(this->*(table[idx-1].ptr));
852 int *ptr = table[idx-1].ptr;
853 if (sscanf(q, "%d", ptr) != 1) {
854 t.error("bad number `%1'", q);
858 else if (strcmp("family", p) == 0) {
861 t.error("family command requires an argument");
864 char *tem = new char[strlen(p)+1];
868 else if (strcmp("fonts", p) == 0) {
870 if (!p || sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
871 t.error("bad number of fonts `%1'", p);
874 font_name_table = (const char **)new char *[nfonts+1];
875 for (int i = 0; i < nfonts; i++) {
879 t.error("end of file while reading list of fonts");
882 p = strtok(t.buf, WS);
884 char *temp = new char[strlen(p)+1];
886 font_name_table[i] = temp;
890 t.error("font count does not match number of fonts");
893 font_name_table[nfonts] = 0;
895 else if (strcmp("papersize", p) == 0) {
898 t.error("papersize command requires an argument");
903 double unscaled_paperwidth, unscaled_paperlength;
904 if (scan_papersize(p, &papersize, &unscaled_paperlength,
905 &unscaled_paperwidth)) {
906 paperwidth = int(unscaled_paperwidth * res + 0.5);
907 paperlength = int(unscaled_paperlength * res + 0.5);
914 t.error("bad paper size");
918 else if (strcmp("unscaled_charwidths", p) == 0)
919 unscaled_charwidths = 1;
920 else if (strcmp("pass_filenames", p) == 0)
922 else if (strcmp("sizes", p) == 0) {
930 t.error("list of sizes must be terminated by `0'");
933 p = strtok(t.buf, WS);
936 switch (sscanf(p, "%d-%d", &lower, &upper)) {
941 if (lower <= upper && lower >= 0)
945 t.error("bad size range `%1'", p);
949 int *old_sizes = sizes;
950 sizes = new int[n*2];
951 memcpy(sizes, old_sizes, n*sizeof(int));
961 t.error("must have some sizes");
965 else if (strcmp("styles", p) == 0) {
966 int style_table_size = 5;
967 style_table = (const char **)new char *[style_table_size];
969 for (j = 0; j < style_table_size; j++)
976 // leave room for terminating 0
977 if (i + 1 >= style_table_size) {
978 const char **old_style_table = style_table;
979 style_table_size *= 2;
980 style_table = (const char **)new char*[style_table_size];
981 for (j = 0; j < i; j++)
982 style_table[j] = old_style_table[j];
983 for (; j < style_table_size; j++)
985 a_delete old_style_table;
987 char *tem = new char[strlen(p) + 1];
989 style_table[i++] = tem;
992 else if (strcmp("tcommand", p) == 0)
994 else if (strcmp("use_charnames_in_special", p) == 0)
995 use_charnames_in_special = 1;
996 else if (strcmp("image_generator", p) == 0) {
999 t.error("image_generator command requires an argument");
1002 image_generator = strsave(p);
1004 else if (strcmp("charset", p) == 0)
1006 else if (unknown_desc_command_handler) {
1008 p = strtok(0, "\n");
1009 (*unknown_desc_command_handler)(command, trim_arg(p), t.path, t.lineno);
1013 t.error("missing `res' command");
1016 if (unitwidth == 0) {
1017 t.error("missing `unitwidth' command");
1020 if (font_name_table == 0) {
1021 t.error("missing `fonts' command");
1025 t.error("missing `sizes' command");
1028 if (sizescale < 1) {
1029 t.error("bad `sizescale' value");
1033 t.error("bad `hor' value");
1037 t.error("bad `vert' value");
1043 void font::handle_unknown_font_command(const char *, const char *,
1048 FONT_COMMAND_HANDLER
1049 font::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func)
1051 FONT_COMMAND_HANDLER prev = unknown_desc_command_handler;
1052 unknown_desc_command_handler = func;