diff options
Diffstat (limited to 'src/libs/libgroff/font.cpp')
-rw-r--r-- | src/libs/libgroff/font.cpp | 1321 |
1 files changed, 1321 insertions, 0 deletions
diff --git a/src/libs/libgroff/font.cpp b/src/libs/libgroff/font.cpp new file mode 100644 index 0000000..c1af12c --- /dev/null +++ b/src/libs/libgroff/font.cpp @@ -0,0 +1,1321 @@ +/* Copyright (C) 1989-2021 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, or +(at your option) any later version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "lib.h" + +#include <assert.h> +#include <ctype.h> +#include <math.h> +#include <stdlib.h> +#include <wchar.h> + +#include "errarg.h" +#include "error.h" +#include "cset.h" +#include "font.h" +#include "unicode.h" +#include "paper.h" + +const char *const WS = " \t\n\r"; + +struct font_char_metric { + char type; + int code; + int width; + int height; + int depth; + int pre_math_space; + int italic_correction; + int subscript_correction; + char *special_device_coding; +}; + +struct font_kern_list { + glyph *glyph1; + glyph *glyph2; + int amount; + font_kern_list *next; + + font_kern_list(glyph *, glyph *, int, font_kern_list * = 0); +}; + +struct font_widths_cache { + font_widths_cache *next; + int point_size; + int *width; + + font_widths_cache(int, int, font_widths_cache * = 0); + ~font_widths_cache(); +}; + +/* text_file */ + +struct text_file { + FILE *fp; + char *path; + int lineno; + int linebufsize; + bool recognize_comments; + bool silent; + char *buf; + text_file(FILE *fp, char *p); + ~text_file(); + bool next_line(); + void error(const char *format, + const errarg &arg1 = empty_errarg, + const errarg &arg2 = empty_errarg, + const errarg &arg3 = empty_errarg); + void fatal(const char *format, + const errarg &arg1 = empty_errarg, + const errarg &arg2 = empty_errarg, + const errarg &arg3 = empty_errarg); +}; + +text_file::text_file(FILE *p, char *s) : fp(p), path(s), lineno(0), + linebufsize(128), recognize_comments(true), silent(false), buf(0) +{ +} + +text_file::~text_file() +{ + delete[] buf; + free(path); + if (fp) + fclose(fp); +} + +bool text_file::next_line() +{ + if (fp == 0) + return false; + if (buf == 0) + buf = new char[linebufsize]; + for (;;) { + lineno++; + int length = 0; + for (;;) { + int c = getc(fp); + if (c == EOF) + break; + if (is_invalid_input_char(c)) + error("invalid input character code %1", int(c)); + else { + if (length + 1 >= linebufsize) { + char *old_buf = buf; + buf = new char[linebufsize * 2]; + memcpy(buf, old_buf, linebufsize); + delete[] old_buf; + linebufsize *= 2; + } + buf[length++] = c; + if (c == '\n') + break; + } + } + if (length == 0) + break; + buf[length] = '\0'; + char *ptr = buf; + while (csspace(*ptr)) + ptr++; + if (*ptr != 0 && (!recognize_comments || *ptr != '#')) + return true; + } + return false; +} + +void text_file::error(const char *format, + const errarg &arg1, + const errarg &arg2, + const errarg &arg3) +{ + if (!silent) + error_with_file_and_line(path, lineno, format, arg1, arg2, arg3); +} + +void text_file::fatal(const char *format, + const errarg &arg1, + const errarg &arg2, + const errarg &arg3) +{ + if (!silent) + fatal_with_file_and_line(path, lineno, format, arg1, arg2, arg3); +} + +int glyph_to_unicode(glyph *g) +{ + const char *nm = glyph_to_name(g); + if (nm != 0) { + // ASCII character? + if (nm[0] == 'c' && nm[1] == 'h' && nm[2] == 'a' && nm[3] == 'r' + && (nm[4] >= '0' && nm[4] <= '9')) { + int n = (nm[4] - '0'); + if (nm[5] == '\0') + return n; + if (n > 0 && (nm[5] >= '0' && nm[5] <= '9')) { + n = 10*n + (nm[5] - '0'); + if (nm[6] == '\0') + return n; + if (nm[6] >= '0' && nm[6] <= '9') { + n = 10*n + (nm[6] - '0'); + if (nm[7] == '\0' && n < 128) + return n; + } + } + } + // Unicode character? + if (check_unicode_name(nm)) { + char *ignore; + return (int)strtol(nm + 1, &ignore, 16); + } + // If 'nm' is a single letter 'x', the glyph name is '\x'. + char buf[] = { '\\', '\0', '\0' }; + if (nm[1] == '\0') { + buf[1] = nm[0]; + nm = buf; + } + // groff glyphs that map to Unicode? + const char *unicode = glyph_name_to_unicode(nm); + if (unicode != 0 && strchr(unicode, '_') == 0) { + char *ignore; + return (int)strtol(unicode, &ignore, 16); + } + } + return -1; +} + +/* font functions */ + +font::font(const char *s) : ligatures(0), kern_hash_table(0), + space_width(0), special(false), internalname(0), slant(0.0), zoom(0), + ch_index(0), nindices(0), ch(0), ch_used(0), ch_size(0), + widths_cache(0) +{ + name = new char[strlen(s) + 1]; + strcpy(name, s); +} + +font::~font() +{ + for (int i = 0; i < ch_used; i++) + if (ch[i].special_device_coding) + delete[] ch[i].special_device_coding; + delete[] ch; + delete[] ch_index; + if (kern_hash_table) { + for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) { + font_kern_list *kerns = kern_hash_table[i]; + while (kerns) { + font_kern_list *tem = kerns; + kerns = kerns->next; + delete tem; + } + } + delete[] kern_hash_table; + } + delete[] name; + delete[] internalname; + while (widths_cache) { + font_widths_cache *tem = widths_cache; + widths_cache = widths_cache->next; + delete tem; + } +} + +static int scale_round(int n, int x, int y) +{ + assert(x >= 0 && y > 0); + int y2 = y/2; + if (x == 0) + return 0; + if (n >= 0) { + if (n <= (INT_MAX - y2) / x) + return (n * x + y2) / y; + return int(n * double(x) / double(y) + .5); + } + else { + if (-(unsigned)n <= (-(unsigned)INT_MIN - y2) / x) + return (n * x - y2) / y; + return int(n * double(x) / double(y) - .5); + } +} + +static int scale_round(int n, int x, int y, int z) +{ + assert(x >= 0 && y > 0 && z > 0); + if (x == 0) + return 0; + if (n >= 0) + return int((n * double(x) / double(y)) * (double(z) / 1000.0) + .5); + else + return int((n * double(x) / double(y)) * (double(z) / 1000.0) - .5); +} + +inline int font::scale(int w, int sz) +{ + if (zoom) + return scale_round(w, sz, unitwidth, zoom); + else + return sz == unitwidth ? w : scale_round(w, sz, unitwidth); +} + +// Returns whether scaling by arguments was successful. Used for paper +// size conversions. +bool font::unit_scale(double *value, char unit) +{ + // Paper sizes are handled in inches. + double divisor = 0; + switch (unit) { + case 'i': + divisor = 1; + break; + case 'p': + divisor = 72; + break; + case 'P': + divisor = 6; + break; + case 'c': + divisor = 2.54; + break; + default: + assert(0 == "unit not in [cipP]"); + break; + } + if (divisor) { + *value /= divisor; + return true; + } + return false; +} + +int font::get_skew(glyph *g, int point_size, int sl) +{ + int h = get_height(g, point_size); + return int(h * tan((slant + sl) * PI / 180.0) + .5); +} + +bool font::contains(glyph *g) +{ + int idx = glyph_to_index(g); + assert(idx >= 0); + // Explicitly enumerated glyph? + if (idx < nindices && ch_index[idx] >= 0) + return true; + if (is_unicode) { + // Unicode font + // ASCII or Unicode character, or groff glyph name that maps to Unicode? + if (glyph_to_unicode(g) >= 0) + return true; + // Numbered character? + if (glyph_to_number(g) >= 0) + return true; + } + return false; +} + +bool font::is_special() +{ + return special; +} + +font_widths_cache::font_widths_cache(int ps, int ch_size, + font_widths_cache *p) +: next(p), point_size(ps) +{ + width = new int[ch_size]; + for (int i = 0; i < ch_size; i++) + width[i] = -1; +} + +font_widths_cache::~font_widths_cache() +{ + delete[] width; +} + +int font::get_width(glyph *g, int point_size) +{ + int idx = glyph_to_index(g); + assert(idx >= 0); + int real_size; + if (zoom == 0) // 0 means "don't zoom" + real_size = point_size; + else + { + if (point_size <= (INT_MAX - 500) / zoom) + real_size = (point_size * zoom + 500) / 1000; + else + real_size = int(point_size * double(zoom) / 1000.0 + .5); + } + if (idx < nindices && ch_index[idx] >= 0) { + // Explicitly enumerated glyph + int i = ch_index[idx]; + if (real_size == unitwidth || font::use_unscaled_charwidths) + return ch[i].width; + + if (!widths_cache) + widths_cache = new font_widths_cache(real_size, ch_size); + else if (widths_cache->point_size != real_size) { + font_widths_cache **p; + for (p = &widths_cache; *p; p = &(*p)->next) + if ((*p)->point_size == real_size) + break; + if (*p) { + font_widths_cache *tem = *p; + *p = (*p)->next; + tem->next = widths_cache; + widths_cache = tem; + } + else + widths_cache = new font_widths_cache(real_size, ch_size, + widths_cache); + } + int &w = widths_cache->width[i]; + if (w < 0) + w = scale(ch[i].width, point_size); + return w; + } + if (is_unicode) { + // Unicode font + int width = 24; // XXX: Add a request to override this. + int w = wcwidth(get_code(g)); + if (w > 1) + width *= w; + if (real_size == unitwidth || font::use_unscaled_charwidths) + return width; + else + return scale(width, point_size); + } + assert(0 == "glyph is not indexed and device lacks Unicode support"); + abort(); // -Wreturn-type +} + +int font::get_height(glyph *g, int point_size) +{ + int idx = glyph_to_index(g); + assert(idx >= 0); + if (idx < nindices && ch_index[idx] >= 0) { + // Explicitly enumerated glyph + return scale(ch[ch_index[idx]].height, point_size); + } + if (is_unicode) { + // Unicode font + return 0; + } + assert(0 == "glyph is not indexed and device lacks Unicode support"); + abort(); // -Wreturn-type +} + +int font::get_depth(glyph *g, int point_size) +{ + int idx = glyph_to_index(g); + assert(idx >= 0); + if (idx < nindices && ch_index[idx] >= 0) { + // Explicitly enumerated glyph + return scale(ch[ch_index[idx]].depth, point_size); + } + if (is_unicode) { + // Unicode font + return 0; + } + assert(0 == "glyph is not indexed and device lacks Unicode support"); + abort(); // -Wreturn-type +} + +int font::get_italic_correction(glyph *g, int point_size) +{ + int idx = glyph_to_index(g); + assert(idx >= 0); + if (idx < nindices && ch_index[idx] >= 0) { + // Explicitly enumerated glyph + return scale(ch[ch_index[idx]].italic_correction, point_size); + } + if (is_unicode) { + // Unicode font + return 0; + } + assert(0 == "glyph is not indexed and device lacks Unicode support"); + abort(); // -Wreturn-type +} + +int font::get_left_italic_correction(glyph *g, int point_size) +{ + int idx = glyph_to_index(g); + assert(idx >= 0); + if (idx < nindices && ch_index[idx] >= 0) { + // Explicitly enumerated glyph + return scale(ch[ch_index[idx]].pre_math_space, point_size); + } + if (is_unicode) { + // Unicode font + return 0; + } + assert(0 == "glyph is not indexed and device lacks Unicode support"); + abort(); // -Wreturn-type +} + +int font::get_subscript_correction(glyph *g, int point_size) +{ + int idx = glyph_to_index(g); + assert(idx >= 0); + if (idx < nindices && ch_index[idx] >= 0) { + // Explicitly enumerated glyph + return scale(ch[ch_index[idx]].subscript_correction, point_size); + } + if (is_unicode) { + // Unicode font + return 0; + } + assert(0 == "glyph is not indexed and device lacks Unicode support"); + abort(); // -Wreturn-type +} + +void font::set_zoom(int factor) +{ + assert(factor >= 0); + if (factor == 1000) + zoom = 0; + else + zoom = factor; +} + +int font::get_zoom() +{ + return zoom; +} + +int font::get_space_width(int point_size) +{ + return scale(space_width, point_size); +} + +font_kern_list::font_kern_list(glyph *g1, glyph *g2, int n, font_kern_list *p) +: glyph1(g1), glyph2(g2), amount(n), next(p) +{ +} + +inline int font::hash_kern(glyph *g1, glyph *g2) +{ + int n = ((glyph_to_index(g1) << 10) + glyph_to_index(g2)) + % KERN_HASH_TABLE_SIZE; + return n < 0 ? -n : n; +} + +void font::add_kern(glyph *g1, glyph *g2, int amount) +{ + if (!kern_hash_table) { + kern_hash_table = new font_kern_list *[int(KERN_HASH_TABLE_SIZE)]; + for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) + kern_hash_table[i] = 0; + } + font_kern_list **p = kern_hash_table + hash_kern(g1, g2); + *p = new font_kern_list(g1, g2, amount, *p); +} + +int font::get_kern(glyph *g1, glyph *g2, int point_size) +{ + if (kern_hash_table) { + for (font_kern_list *p = kern_hash_table[hash_kern(g1, g2)]; p; + p = p->next) + if (g1 == p->glyph1 && g2 == p->glyph2) + return scale(p->amount, point_size); + } + return 0; +} + +bool font::has_ligature(int mask) +{ + return (bool) (mask & ligatures); +} + +int font::get_character_type(glyph *g) +{ + int idx = glyph_to_index(g); + assert(idx >= 0); + if (idx < nindices && ch_index[idx] >= 0) { + // Explicitly enumerated glyph + return ch[ch_index[idx]].type; + } + if (is_unicode) { + // Unicode font + return 0; + } + assert(0 == "glyph is not indexed and device lacks Unicode support"); + abort(); // -Wreturn-type +} + +int font::get_code(glyph *g) +{ + int idx = glyph_to_index(g); + assert(idx >= 0); + if (idx < nindices && ch_index[idx] >= 0) { + // Explicitly enumerated glyph + return ch[ch_index[idx]].code; + } + if (is_unicode) { + // Unicode font + // ASCII or Unicode character, or groff glyph name that maps to Unicode? + int uni = glyph_to_unicode(g); + if (uni >= 0) + return uni; + // Numbered character? + int n = glyph_to_number(g); + if (n >= 0) + return n; + } + // The caller must check 'contains(g)' before calling get_code(g). + assert(0 == "glyph is not indexed and device lacks Unicode support"); + abort(); // -Wreturn-type +} + +const char *font::get_name() +{ + return name; +} + +const char *font::get_internal_name() +{ + return internalname; +} + +const char *font::get_special_device_encoding(glyph *g) +{ + int idx = glyph_to_index(g); + assert(idx >= 0); + if (idx < nindices && ch_index[idx] >= 0) { + // Explicitly enumerated glyph + return ch[ch_index[idx]].special_device_coding; + } + if (is_unicode) { + // Unicode font + return 0; + } + assert(0 == "glyph is not indexed and device lacks Unicode support"); + abort(); // -Wreturn-type +} + +const char *font::get_image_generator() +{ + return image_generator; +} + +void font::alloc_ch_index(int idx) +{ + if (nindices == 0) { + nindices = 128; + if (idx >= nindices) + nindices = idx + 10; + ch_index = new int[nindices]; + for (int i = 0; i < nindices; i++) + ch_index[i] = -1; + } + else { + int old_nindices = nindices; + nindices *= 2; + if (idx >= nindices) + nindices = idx + 10; + int *old_ch_index = ch_index; + ch_index = new int[nindices]; + memcpy(ch_index, old_ch_index, sizeof(int) * old_nindices); + for (int i = old_nindices; i < nindices; i++) + ch_index[i] = -1; + delete[] old_ch_index; + } +} + +void font::extend_ch() +{ + if (ch == 0) + ch = new font_char_metric[ch_size = 16]; + else { + int old_ch_size = ch_size; + ch_size *= 2; + font_char_metric *old_ch = ch; + ch = new font_char_metric[ch_size]; + memcpy(ch, old_ch, old_ch_size * sizeof(font_char_metric)); + delete[] old_ch; + } +} + +void font::compact() +{ + int i; + for (i = nindices - 1; i >= 0; i--) + if (ch_index[i] >= 0) + break; + i++; + if (i < nindices) { + int *old_ch_index = ch_index; + ch_index = new int[i]; + memcpy(ch_index, old_ch_index, i*sizeof(int)); + delete[] old_ch_index; + nindices = i; + } + if (ch_used < ch_size) { + font_char_metric *old_ch = ch; + ch = new font_char_metric[ch_used]; + memcpy(ch, old_ch, ch_used*sizeof(font_char_metric)); + delete[] old_ch; + ch_size = ch_used; + } +} + +void font::add_entry(glyph *g, const font_char_metric &metric) +{ + int idx = glyph_to_index(g); + assert(idx >= 0); + if (idx >= nindices) + alloc_ch_index(idx); + assert(idx < nindices); + if (ch_used + 1 >= ch_size) + extend_ch(); + assert(ch_used + 1 < ch_size); + ch_index[idx] = ch_used; + ch[ch_used++] = metric; +} + +void font::copy_entry(glyph *new_glyph, glyph *old_glyph) +{ + int new_index = glyph_to_index(new_glyph); + int old_index = glyph_to_index(old_glyph); + assert(new_index >= 0 && old_index >= 0 && old_index < nindices); + if (new_index >= nindices) + alloc_ch_index(new_index); + ch_index[new_index] = ch_index[old_index]; +} + +font *font::load_font(const char *s, bool load_header_only) +{ + font *f = new font(s); + if (!f->load(load_header_only)) { + delete f; + return 0; + } + return f; +} + +static char *trim_arg(char *p) +{ + if (0 == p) + return 0; + while (csspace(*p)) + p++; + char *q = strchr(p, '\0'); + while (q > p && csspace(q[-1])) + q--; + *q = '\0'; + return p; +} + +bool font::scan_papersize(const char *p, const char **size, + double *length, double *width) +{ + double l, w; + char lu[2], wu[2]; + const char *pp = p; + bool attempt_file_open = true; + char line[255]; +again: + if (csdigit(*pp)) { + if (sscanf(pp, "%lf%1[ipPc],%lf%1[ipPc]", &l, lu, &w, wu) == 4 + && l > 0 && w > 0 + && unit_scale(&l, lu[0]) && unit_scale(&w, wu[0])) { + if (length) + *length = l; + if (width) + *width = w; + if (size) + *size = "custom"; + return true; + } + } + else { + int i; + for (i = 0; i < NUM_PAPERSIZES; i++) + if (strcasecmp(papersizes[i].name, pp) == 0) { + if (length) + *length = papersizes[i].length; + if (width) + *width = papersizes[i].width; + if (size) + *size = papersizes[i].name; + return true; + } + if (attempt_file_open) { + FILE *fp = fopen(p, "r"); + if (fp != 0) { + if (fgets(line, 254, fp)) { + // Don't recurse on file names. + attempt_file_open = false; + char *linep = strchr(line, '\0'); + // skip final newline, if any + if (*(--linep) == '\n') + *linep = '\0'; + pp = line; + } + fclose(fp); + goto again; + } + } + } + return false; +} + +bool font::load(bool load_header_only) +{ + FILE *fp; + char *path; + if ((fp = open_file(name, &path)) == 0) + return false; + text_file t(fp, path); + t.silent = load_header_only; + char *p = 0; + bool saw_name_directive = false; + while (t.next_line()) { + p = strtok(t.buf, WS); + if (strcmp(p, "name") == 0) { + p = strtok(0, WS); + if (0 == p) { + t.error("'name' directive requires an argument"); + return false; + } + if (strcmp(p, name) != 0) { + t.error("font description file name '%1' does not match 'name'" + " argument '%2'", name, p); + return false; + } + saw_name_directive = true; + } + else if (strcmp(p, "spacewidth") == 0) { + p = strtok(0, WS); + int n; + if (0 == p) { + t.error("missing argument to 'spacewidth' directive"); + return false; + } + if (sscanf(p, "%d", &n) != 1) { + t.error("invalid argument '%1' to 'spacewidth' directive", p); + return false; + } + if (n <= 0) { + t.error("'spacewidth' argument '%1' out of range", n); + return false; + } + space_width = n; + } + else if (strcmp(p, "slant") == 0) { + p = strtok(0, WS); + double n; + if (0 == p) { + t.error("missing argument to 'slant' directive"); + return false; + } + if (sscanf(p, "%lf", &n) != 1) { + t.error("invalid argument '%1' to 'slant' directive", p); + return false; + } + if (n >= 90.0 || n <= -90.0) { + t.error("'slant' directive argument '%1' out of range", n); + return false; + } + slant = n; + } + else if (strcmp(p, "ligatures") == 0) { + for (;;) { + p = strtok(0, WS); + if (0 == p || strcmp(p, "0") == 0) + break; + if (strcmp(p, "ff") == 0) + ligatures |= LIG_ff; + else if (strcmp(p, "fi") == 0) + ligatures |= LIG_fi; + else if (strcmp(p, "fl") == 0) + ligatures |= LIG_fl; + else if (strcmp(p, "ffi") == 0) + ligatures |= LIG_ffi; + else if (strcmp(p, "ffl") == 0) + ligatures |= LIG_ffl; + else { + t.error("unrecognized ligature '%1'", p); + return false; + } + } + } + else if (strcmp(p, "internalname") == 0) { + p = strtok(0, WS); + if (0 == p) { + t.error("missing argument to 'internalname' directive"); + return false; + } + internalname = new char[strlen(p) + 1]; + strcpy(internalname, p); + } + else if (strcmp(p, "special") == 0) { + special = true; + } + else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) { + char *directive = p; + p = strtok(0, "\n"); + handle_unknown_font_command(directive, trim_arg(p), t.path, + t.lineno); + } + else + break; + } + bool saw_charset_directive = false; + char *directive = p; + t.recognize_comments = false; + while (directive) { + if (strcmp(directive, "kernpairs") == 0) { + if (load_header_only) + return true; + for (;;) { + if (!t.next_line()) { + directive = 0; + break; + } + char *c1 = strtok(t.buf, WS); + if (0 == c1) + continue; + char *c2 = strtok(0, WS); + if (0 == c2) { + directive = c1; + break; + } + p = strtok(0, WS); + if (0 == p) { + t.error("missing kern amount for kerning pair '%1 %2'", c1, + c2); + return false; + } + int n; + if (sscanf(p, "%d", &n) != 1) { + t.error("invalid kern amount '%1' for kerning pair '%2 %3'", + p, c1, c2); + return false; + } + glyph *g1 = name_to_glyph(c1); + glyph *g2 = name_to_glyph(c2); + add_kern(g1, g2, n); + } + } + else if (strcmp(directive, "charset") == 0) { + if (load_header_only) + return true; + saw_charset_directive = true; + glyph *last_glyph = 0; + for (;;) { + if (!t.next_line()) { + directive = 0; + break; + } + char *nm = strtok(t.buf, WS); + assert(nm != 0); + p = strtok(0, WS); + if (0 == p) { + directive = nm; + break; + } + if (p[0] == '"') { + if (last_glyph == 0) { + t.error("the first entry ('%1') in 'charset' subsection" + " cannot be an alias", nm); + return false; + } + if (strcmp(nm, "---") == 0) { + t.error("an unnamed character ('---') cannot be an alias"); + return false; + } + glyph *g = name_to_glyph(nm); + copy_entry(g, last_glyph); + } + else { + font_char_metric metric; + metric.height = 0; + metric.depth = 0; + metric.pre_math_space = 0; + metric.italic_correction = 0; + metric.subscript_correction = 0; + int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d", + &metric.width, &metric.height, + &metric.depth, + &metric.italic_correction, + &metric.pre_math_space, + &metric.subscript_correction); + if (nparms < 1) { + t.error("missing or invalid width for glyph '%1'", nm); + return false; + } + p = strtok(0, WS); + if (0 == p) { + t.error("missing character type for '%1'", nm); + return false; + } + int type; + if (sscanf(p, "%d", &type) != 1) { + t.error("invalid character type for '%1'", nm); + return false; + } + if (type < 0 || type > 255) { + t.error("character type '%1' out of range for '%2'", type, + nm); + return false; + } + metric.type = type; + p = strtok(0, WS); + if (0 == p) { + t.error("missing code for '%1'", nm); + return false; + } + char *ptr; + metric.code = (int)strtol(p, &ptr, 0); + if (metric.code == 0 && ptr == p) { + t.error("invalid code '%1' for character '%2'", p, nm); + return false; + } + if (is_unicode) { + int w = wcwidth(metric.code); + if (w > 1) + metric.width *= w; + } + p = strtok(0, WS); + if ((0 == p) || (strcmp(p, "--") == 0)) { + metric.special_device_coding = 0; + } + else { + char *nam = new char[strlen(p) + 1]; + strcpy(nam, p); + metric.special_device_coding = nam; + } + if (strcmp(nm, "---") == 0) { + last_glyph = number_to_glyph(metric.code); + add_entry(last_glyph, metric); + } + else { + last_glyph = name_to_glyph(nm); + add_entry(last_glyph, metric); + copy_entry(number_to_glyph(metric.code), last_glyph); + } + } + } + if (0 == last_glyph) { + t.error("no glyphs defined in font description"); + return false; + } + } + else { + t.error("unrecognized font description directive '%1' (missing" + " 'kernpairs' or 'charset'?)", directive); + return false; + } + } + compact(); + t.lineno = 0; + if (!saw_name_directive) { + t.error("font description 'name' directive missing"); + return false; + } + if (!is_unicode && !saw_charset_directive) { + t.error("font description 'charset' subsection missing"); + return false; + } + if (space_width == 0) { + t.error("font description 'spacewidth' directive missing"); + // _Don't_ return false; compute a typical one for Western glyphs. + if (zoom) + space_width = scale_round(unitwidth, res, 72 * 3 * sizescale, + zoom); + else + space_width = scale_round(unitwidth, res, 72 * 3 * sizescale); + } + return true; +} + +static struct { + const char *numeric_directive; + int *ptr; +} table[] = { + { "res", &font::res }, + { "hor", &font::hor }, + { "vert", &font::vert }, + { "unitwidth", &font::unitwidth }, + { "paperwidth", &font::paperwidth }, + { "paperlength", &font::paperlength }, + { "spare1", &font::biggestfont }, + { "biggestfont", &font::biggestfont }, + { "spare2", &font::spare2 }, + { "sizescale", &font::sizescale }, + }; + +// Return file specification of DESC file for selected output device if +// it can be located and is valid, and a null pointer otherwise. +const char *font::load_desc() +{ + int nfonts = 0; + FILE *fp; + char *path; + if ((fp = open_file("DESC", &path)) == 0) + return 0 /* nullptr */; + text_file t(fp, path); + while (t.next_line()) { + char *p = strtok(t.buf, WS); + assert(p != 0); + bool numeric_directive_found = false; + unsigned int idx; + for (idx = 0; !numeric_directive_found + && idx < sizeof(table) / sizeof(table[0]); idx++) + if (strcmp(table[idx].numeric_directive, p) == 0) + numeric_directive_found = true; + if (numeric_directive_found) { + char *q = strtok(0, WS); + if (0 == q) { + t.error("missing value for directive '%1'", p); + return 0 /* nullptr */; + } + int val; + if (sscanf(q, "%d", &val) != 1) { + t.error("'%1' directive given invalid number '%2'", p, q); + return 0 /* nullptr */; + } + if ((strcmp(p, "res") == 0 + || strcmp(p, "hor") == 0 + || strcmp(p, "vert") == 0 + || strcmp(p, "unitwidth") == 0 + || strcmp(p, "paperwidth") == 0 + || strcmp(p, "paperlength") == 0 + || strcmp(p, "sizescale") == 0) + && val < 1) { + t.error("expected argument to '%1' directive to be a" + " positive number, got '%2'", p, val); + return 0 /* nullptr */; + } + *(table[idx-1].ptr) = val; + } + else if (strcmp("family", p) == 0) { + p = strtok(0, WS); + if (0 == p) { + t.error("'family' directive requires an argument"); + return 0 /* nullptr */; + } + char *tem = new char[strlen(p)+1]; + strcpy(tem, p); + family = tem; + } + else if (strcmp("fonts", p) == 0) { + p = strtok(0, WS); + if (0 == p) { + t.error("'fonts' directive requires arguments"); + return 0 /* nullptr */; + } + if (sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) { + t.error("expected first argument to 'fonts' directive to be a" + " non-negative number, got '%1'", p); + return 0 /* nullptr */; + } + font_name_table = (const char **)new char *[nfonts+1]; + for (int i = 0; i < nfonts; i++) { + p = strtok(0, WS); + while (0 == p) { + if (!t.next_line()) { + t.error("unexpected end of file while reading font list"); + return 0 /* nullptr */; + } + p = strtok(t.buf, WS); + } + char *temp = new char[strlen(p)+1]; + strcpy(temp, p); + font_name_table[i] = temp; + } + p = strtok(0, WS); + if (p != 0) { + t.error("font count does not match declared number of fonts" + " ('%1')", nfonts); + return 0 /* nullptr */; + } + font_name_table[nfonts] = 0; + } + else if (strcmp("papersize", p) == 0) { + if (0 == res) { + t.error("'res' directive must precede 'papersize' in device" + " description file"); + return 0 /* nullptr */; + } + p = strtok(0, WS); + if (0 == p) { + t.error("'papersize' directive requires an argument"); + return 0 /* nullptr */; + } + bool found_paper = false; + char *savedp = strdup(p); + if (0 == savedp) + t.fatal("memory allocation failure while processing 'papersize'" + " directive"); + while (p) { + double unscaled_paperwidth, unscaled_paperlength; + if (scan_papersize(p, &papersize, &unscaled_paperlength, + &unscaled_paperwidth)) { + paperwidth = int(unscaled_paperwidth * res + 0.5); + paperlength = int(unscaled_paperlength * res + 0.5); + found_paper = true; + break; + } + p = strtok(0, WS); + } + assert(savedp != 0); + if (!found_paper) { + t.error("unable to determine a paper format from '%1'", savedp); + free(savedp); + return 0 /* nullptr */; + } + free(savedp); + } + else if (strcmp("unscaled_charwidths", p) == 0) + use_unscaled_charwidths = true; + else if (strcmp("pass_filenames", p) == 0) + pass_filenames = true; + else if (strcmp("sizes", p) == 0) { + int n = 16; + sizes = new int[n]; + int i = 0; + for (;;) { + p = strtok(0, WS); + while (0 == p) { + if (!t.next_line()) { + t.error("list of sizes must be terminated by '0'"); + return 0 /* nullptr */; + } + p = strtok(t.buf, WS); + } + int lower, upper; + switch (sscanf(p, "%d-%d", &lower, &upper)) { + case 1: + upper = lower; + // fall through + case 2: + if (lower <= upper && lower >= 0) + break; + // fall through + default: + t.error("invalid size range '%1'", p); + return 0 /* nullptr */; + } + if (i + 2 > n) { + int *old_sizes = sizes; + sizes = new int[n*2]; + memcpy(sizes, old_sizes, n*sizeof(int)); + n *= 2; + delete[] old_sizes; + } + sizes[i++] = lower; + if (lower == 0) + break; + sizes[i++] = upper; + } + if (i == 1) { + t.error("must have some sizes"); + return 0 /* nullptr */; + } + } + else if (strcmp("styles", p) == 0) { + int style_table_size = 5; + style_table = (const char **)new char *[style_table_size]; + int j; + for (j = 0; j < style_table_size; j++) + style_table[j] = 0; + int i = 0; + for (;;) { + p = strtok(0, WS); + if (0 == p) + break; + // leave room for terminating 0 + if (i + 1 >= style_table_size) { + const char **old_style_table = style_table; + style_table_size *= 2; + style_table = (const char **)new char*[style_table_size]; + for (j = 0; j < i; j++) + style_table[j] = old_style_table[j]; + for (; j < style_table_size; j++) + style_table[j] = 0; + delete[] old_style_table; + } + char *tem = new char[strlen(p) + 1]; + strcpy(tem, p); + style_table[i++] = tem; + } + } + else if (strcmp("tcommand", p) == 0) + has_tcommand = true; + else if (strcmp("use_charnames_in_special", p) == 0) + use_charnames_in_special = true; + else if (strcmp("unicode", p) == 0) + is_unicode = true; + else if (strcmp("image_generator", p) == 0) { + p = strtok(0, WS); + if (0 == p) { + t.error("'image_generator' directive requires an argument"); + return 0 /* nullptr */; + } + image_generator = strsave(p); + } + else if (strcmp("charset", p) == 0) + break; + else if (unknown_desc_command_handler) { + char *directive = p; + p = strtok(0, "\n"); + (*unknown_desc_command_handler)(directive, trim_arg(p), t.path, + t.lineno); + } + } + t.lineno = 0; + if (res == 0) { + t.error("device description file missing 'res' directive"); + return 0 /* nullptr */; + } + if (unitwidth == 0) { + t.error("device description file missing 'unitwidth' directive"); + return 0 /* nullptr */; + } + if (font_name_table == 0) { + t.error("device description file missing 'fonts' directive"); + return 0 /* nullptr */; + } + if (sizes == 0) { + t.error("device description file missing 'sizes' directive"); + return 0 /* nullptr */; + } + return path; +} + +void font::handle_unknown_font_command(const char *, const char *, + const char *, int) +{ +} + +FONT_COMMAND_HANDLER +font::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func) +{ + FONT_COMMAND_HANDLER prev = unknown_desc_command_handler; + unknown_desc_command_handler = func; + return prev; +} + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: |