/* encoding.c - functions to manipulate encodings and fontmaps */ /* * Copyright (C) 2000, Matias Atria * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "mdvi.h" #include "private.h" #include #include typedef struct _DviFontMap DviFontMap; struct _DviFontMap { ListHead entries; DviHashTable fonts; }; typedef struct _PSFontMap { struct _PSFontMap *next; struct _PSFontMap *prev; char *psname; char *mapname; char *fullname; } PSFontMap; /* these variables control PS font maps */ static char *pslibdir = NULL; /* path where we look for PS font maps */ static char *psfontdir = NULL; /* PS font search path */ static int psinitialized = 0; /* did we expand the path already? */ static ListHead psfonts = MDVI_EMPTY_LIST_HEAD; static DviHashTable pstable = MDVI_EMPTY_HASH_TABLE; static ListHead fontmaps; static DviHashTable maptable; static int fontmaps_loaded = 0; #define MAP_HASH_SIZE 57 #define ENC_HASH_SIZE 31 #define PSMAP_HASH_SIZE 57 /* this hash table should be big enough to * hold (ideally) one glyph name per bucket */ #define ENCNAME_HASH_SIZE 131 /* most TeX fonts have 128 glyphs */ static ListHead encodings = MDVI_EMPTY_LIST_HEAD; static DviEncoding *tex_text_encoding = NULL; static DviEncoding *default_encoding = NULL; /* we keep two hash tables for encodings: one for their base files (e.g. * "8r.enc"), and another one for their names (e.g. "TeXBase1Encoding") */ static DviHashTable enctable = MDVI_EMPTY_HASH_TABLE; static DviHashTable enctable_file = MDVI_EMPTY_HASH_TABLE; /* the TeX text encoding, from dvips */ static char *tex_text_vector[256] = { "Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma", "Upsilon", "Phi", "Psi", "Omega", "arrowup", "arrowdown", "quotesingle", "exclamdown", "questiondown", "dotlessi", "dotlessj", "grave", "acute", "caron", "breve", "macron", "ring", "cedilla", "germandbls", "ae", "oe", "oslash", "AE", "OE", "Oslash", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "circumflex", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "tilde", "dieresis", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static void ps_init_default_paths __PROTO((void)); /* * What we do here is allocate one block large enough to hold the entire * file (these files are small) minus the leading comments. This is much * better than allocating up to 256 tiny strings per encoding vector. */ static int read_encoding(DviEncoding *enc) { FILE *in; int curr; char *line; char *name; char *next; struct stat st; ASSERT(enc->private == NULL); in = fopen(enc->filename, "r"); if(in == NULL) { DEBUG((DBG_FMAP, "%s: could not read `%s' (%s)\n", enc->name, enc->filename, strerror(errno))); return -1; } if(fstat(fileno(in), &st) < 0) { /* should not happen */ fclose(in); return -1; } st.st_size -= enc->offset; /* this will be one big string */ enc->private = (char *)malloc(st.st_size + 1); /* setup the hash table */ mdvi_hash_create(&enc->nametab, ENCNAME_HASH_SIZE); /* setup the encoding vector */ enc->vector = (char **)xmalloc(256 * sizeof(char *)); /* jump to the beginning of the interesting part */ fseek(in, enc->offset, SEEK_SET); /* and read everything */ if(fread(enc->private, st.st_size, 1, in) != 1) { fclose(in); xfree(enc->private); enc->private = NULL; return -1; } /* we don't need this anymore */ fclose(in); curr = 0; next = name = NULL; DEBUG((DBG_FMAP, "%s: reading encoding vector\n", enc->name)); for(line = enc->private; *line && curr < 256; line = next) { SKIPSP(line); if(*line == ']') { line++; SKIPSP(line); if(STRNEQ(line, "def", 3)) break; } name = getword(line, " \t\n", &next); if(name == NULL) break; /* next > line */ if(*name < ' ') continue; if(*name == '%') { while(*next && *next != '\n') next++; if(*next) next++; /* skip \n */ continue; } /* got a name */ if(*next) *next++ = 0; if(*name == '/') name++; enc->vector[curr] = name; /* add it to the hash table */ if(!STREQ(name, ".notdef")) { mdvi_hash_add(&enc->nametab, MDVI_KEY(name), Int2Ptr(curr + 1), MDVI_HASH_REPLACE); } curr++; } if(curr == 0) { mdvi_hash_reset(&enc->nametab, 0); xfree(enc->private); xfree(enc); return -1; } while(curr < 256) enc->vector[curr++] = NULL; return 0; } static DviEncoding *find_encoding(const char *name) { return (DviEncoding *)(encodings.count ? mdvi_hash_lookup(&enctable, MDVI_KEY(name)) : NULL); } static void destroy_encoding(DviEncoding *enc) { if(enc == default_encoding) { default_encoding = tex_text_encoding; /* now we use reference counts again */ mdvi_release_encoding(enc, 1); } if(enc != tex_text_encoding) { mdvi_hash_reset(&enc->nametab, 0); if(enc->private) { xfree(enc->private); xfree(enc->vector); } if(enc->name) xfree(enc->name); if(enc->filename) xfree(enc->filename); xfree(enc); } } /* this is used for the `enctable_file' hash table */ static void file_hash_free(DviHashKey key, void *data) { xfree(key); } static DviEncoding *register_encoding(const char *basefile, int replace) { DviEncoding *enc; FILE *in; char *filename; char *name; Dstring input; char *line; long offset; DEBUG((DBG_FMAP, "register_encoding(%s)\n", basefile)); if(encodings.count) { enc = mdvi_hash_lookup(&enctable_file, MDVI_KEY(basefile)); if(enc != NULL) { DEBUG((DBG_FMAP, "%s: already there\n", basefile)); return enc; /* no error */ } } /* try our own files first */ filename = kpse_find_file(basefile, kpse_program_text_format, 0); /* then try the system-wide ones */ if(filename == NULL) filename = kpse_find_file(basefile, kpse_tex_ps_header_format, 0); if(filename == NULL) filename = kpse_find_file(basefile, kpse_dvips_config_format, 0); /* finally try the given name */ if(filename == NULL) filename = xstrdup(basefile); in = fopen(filename, "r"); if(in == NULL) { xfree(filename); return NULL; } /* just lookup the name of the encoding */ name = NULL; dstring_init(&input); while((line = dgets(&input, in)) != NULL) { if(STRNEQ(line, "Encoding=", 9)) { name = getword(line + 9, " \t", &line); if(*line) *line++ = 0; break; } else if(*line == '/') { char *label = getword(line + 1, " \t", &line); if(*line) { *line++ = 0; SKIPSP(line); if(*line == '[') { *line = 0; name = label; break; } } } } offset = ftell(in); fclose(in); if(name == NULL || *name == 0) { DEBUG((DBG_FMAP, "%s: could not determine name of encoding\n", basefile)); xfree(filename); return NULL; } /* check if the encoding is already there */ enc = find_encoding(name); if(enc == tex_text_encoding) { /* A special case: if the vector we found is the static one, * allow the user to override it with an external file */ listh_remove(&encodings, LIST(enc)); mdvi_hash_remove(&enctable, MDVI_KEY(enc->name)); if(enc == default_encoding) default_encoding = NULL; } else if(enc) { /* if the encoding is being used, refuse to remove it */ if(enc->links) { xfree(filename); dstring_reset(&input); return NULL; } if(replace) { mdvi_hash_remove(&enctable, MDVI_KEY(name)); mdvi_hash_remove(&enctable_file, MDVI_KEY(basefile)); listh_remove(&encodings, LIST(enc)); if(enc == default_encoding) { default_encoding = NULL; mdvi_release_encoding(enc, 1); } DEBUG((DBG_FMAP, "%s: overriding encoding\n", name)); destroy_encoding(enc); } else { xfree(filename); dstring_reset(&input); return enc; /* no error */ } } enc = xalloc(DviEncoding); enc->name = xstrdup(name); enc->filename = filename; enc->links = 0; enc->offset = offset; enc->private = NULL; enc->vector = NULL; mdvi_hash_init(&enc->nametab); dstring_reset(&input); if(default_encoding == NULL) default_encoding = enc; mdvi_hash_add(&enctable, MDVI_KEY(enc->name), enc, MDVI_HASH_UNCHECKED); mdvi_hash_add(&enctable_file, MDVI_KEY(xstrdup(basefile)), enc, MDVI_HASH_REPLACE); listh_prepend(&encodings, LIST(enc)); DEBUG((DBG_FMAP, "%s: encoding `%s' registered\n", basefile, enc->name)); return enc; } DviEncoding *mdvi_request_encoding(const char *name) { DviEncoding *enc = find_encoding(name); if(enc == NULL) { DEBUG((DBG_FMAP, "%s: encoding not found, returning default `%s'\n", name, default_encoding->name)); return default_encoding; } /* we don't keep reference counts for this */ if(enc == tex_text_encoding) return enc; if(!enc->private && read_encoding(enc) < 0) return NULL; enc->links++; /* if the hash table is empty, rebuild it */ if(enc->nametab.nkeys == 0) { int i; DEBUG((DBG_FMAP, "%s: rehashing\n", enc->name)); for(i = 0; i < 256; i++) { if(enc->vector[i] == NULL) continue; mdvi_hash_add(&enc->nametab, MDVI_KEY(enc->vector[i]), (DviHashKey)Int2Ptr(i), MDVI_HASH_REPLACE); } } return enc; } void mdvi_release_encoding(DviEncoding *enc, int should_free) { /* ignore our static encoding */ if(enc == tex_text_encoding) return; if(!enc->links || --enc->links > 0 || !should_free) return; DEBUG((DBG_FMAP, "%s: resetting encoding vector\n", enc->name)); mdvi_hash_reset(&enc->nametab, 1); /* we'll reuse it */ } int mdvi_encode_glyph(DviEncoding *enc, const char *name) { void *data; data = mdvi_hash_lookup(&enc->nametab, MDVI_KEY(name)); if(data == NULL) return -1; /* we added +1 to the hashed index just to distinguish * a failed lookup from a zero index. Adjust it now. */ return (Ptr2Int(data) - 1); } /**************** * Fontmaps * ****************/ static void parse_spec(DviFontMapEnt *ent, char *spec) { char *arg, *command; /* this is a ridiculously simple parser, and recognizes only * things of the form . Of these, only * command=SlantFont, ExtendFont and ReEncodeFont are handled */ while(*spec) { arg = getword(spec, " \t", &spec); if(*spec) *spec++ = 0; command = getword(spec, " \t", &spec); if(*spec) *spec++ = 0; if(!arg || !command) continue; if(STREQ(command, "SlantFont")) { double x = 10000 * strtod(arg, 0); /* SFROUND evaluates arguments twice */ ent->slant = SFROUND(x); } else if(STREQ(command, "ExtendFont")) { double x = 10000 * strtod(arg, 0); ent->extend = SFROUND(x); } else if(STREQ(command, "ReEncodeFont")) { if(ent->encoding) xfree(ent->encoding); ent->encoding = xstrdup(arg); } } } #if 0 static void print_ent(DviFontMapEnt *ent) { printf("Entry for `%s':\n", ent->fontname); printf(" PS name: %s\n", ent->psname ? ent->psname : "(none)"); printf(" Encoding: %s\n", ent->encoding ? ent->encoding : "(default)"); printf(" EncFile: %s\n", ent->encfile ? ent->encfile : "(none)"); printf(" FontFile: %s\n", ent->fontfile ? ent->fontfile : "(same)"); printf(" Extend: %ld\n", ent->extend); printf(" Slant: %ld\n", ent->slant); } #endif DviFontMapEnt *mdvi_load_fontmap(const char *file) { char *ptr; FILE *in; int lineno = 1; Dstring input; ListHead list; DviFontMapEnt *ent; DviEncoding *last_encoding; char *last_encfile; ptr = kpse_find_file(file, kpse_program_text_format, 0); if(ptr == NULL) ptr = kpse_find_file(file, kpse_tex_ps_header_format, 0); if(ptr == NULL) ptr = kpse_find_file(file, kpse_dvips_config_format, 0); if(ptr == NULL) in = fopen(file, "r"); else { in = fopen(ptr, "r"); xfree(ptr); } if(in == NULL) return NULL; ent = NULL; listh_init(&list); dstring_init(&input); last_encoding = NULL; last_encfile = NULL; while((ptr = dgets(&input, in)) != NULL) { char *font_file; char *tex_name; char *ps_name; char *vec_name; int is_encoding; DviEncoding *enc; lineno++; SKIPSP(ptr); /* we skip what dvips does */ if(*ptr <= ' ' || *ptr == '*' || *ptr == '#' || *ptr == ';' || *ptr == '%') continue; font_file = NULL; tex_name = NULL; ps_name = NULL; vec_name = NULL; is_encoding = 0; if(ent == NULL) { ent = xalloc(DviFontMapEnt); ent->encoding = NULL; ent->slant = 0; ent->extend = 0; } while(*ptr) { char *hdr_name = NULL; while(*ptr && *ptr <= ' ') ptr++; if(*ptr == 0) break; if(*ptr == '"') { char *str; str = getstring(ptr, " \t", &ptr); if(*ptr) *ptr++ = 0; parse_spec(ent, str); continue; } else if(*ptr == '<') { ptr++; if(*ptr == '<') ptr++; else if(*ptr == '[') { is_encoding = 1; ptr++; } SKIPSP(ptr); hdr_name = ptr; } else if(!tex_name) tex_name = ptr; else if(!ps_name) ps_name = ptr; else hdr_name = ptr; /* get next word */ getword(ptr, " \t", &ptr); if(*ptr) *ptr++ = 0; if(hdr_name) { const char *ext = file_extension(hdr_name); if(is_encoding || (ext && STRCEQ(ext, "enc"))) vec_name = hdr_name; else font_file = hdr_name; } } if(tex_name == NULL) continue; ent->fontname = xstrdup(tex_name); ent->psname = ps_name ? xstrdup(ps_name) : NULL; ent->fontfile = font_file ? xstrdup(font_file) : NULL; ent->encfile = vec_name ? xstrdup(vec_name) : NULL; ent->fullfile = NULL; enc = NULL; /* we don't have this yet */ /* if we have an encoding file, register it */ if(ent->encfile) { /* register_encoding is smart enough not to load the * same file twice */ if(!last_encfile || !STREQ(last_encfile, ent->encfile)) { last_encfile = ent->encfile; last_encoding = register_encoding(ent->encfile, 1); } enc = last_encoding; } if(ent->encfile && enc){ if(ent->encoding && !STREQ(ent->encoding, enc->name)) { warning( _("%s: %d: [%s] requested encoding `%s' does not match vector `%s'\n"), file, lineno); } else if(!ent->encoding) ent->encoding = xstrdup(enc->name); } /* add it to the list */ /*print_ent(ent);*/ listh_append(&list, LIST(ent)); ent = NULL; } dstring_reset(&input); fclose(in); return (DviFontMapEnt *)list.head; } static void free_ent(DviFontMapEnt *ent) { ASSERT(ent->fontname != NULL); xfree(ent->fontname); if(ent->psname) xfree(ent->psname); if(ent->fontfile) xfree(ent->fontfile); if(ent->encoding) xfree(ent->encoding); if(ent->encfile) xfree(ent->encfile); if(ent->fullfile) xfree(ent->fullfile); xfree(ent); } void mdvi_install_fontmap(DviFontMapEnt *head) { DviFontMapEnt *ent, *next; for(ent = head; ent; ent = next) { /* add all the entries, overriding old ones */ DviFontMapEnt *old; old = (DviFontMapEnt *) mdvi_hash_remove(&maptable, MDVI_KEY(ent->fontname)); if(old != NULL) { DEBUG((DBG_FMAP, "%s: overriding fontmap entry\n", old->fontname)); listh_remove(&fontmaps, LIST(old)); free_ent(old); } next = ent->next; mdvi_hash_add(&maptable, MDVI_KEY(ent->fontname), ent, MDVI_HASH_UNCHECKED); listh_append(&fontmaps, LIST(ent)); } } static void init_static_encoding() { DviEncoding *encoding; int i; DEBUG((DBG_FMAP, "installing static TeX text encoding\n")); encoding = xalloc(DviEncoding); encoding->private = ""; encoding->filename = ""; encoding->name = "TeXTextEncoding"; encoding->vector = tex_text_vector; encoding->links = 1; encoding->offset = 0; mdvi_hash_create(&encoding->nametab, ENCNAME_HASH_SIZE); for(i = 0; i < 256; i++) { if(encoding->vector[i]) { mdvi_hash_add(&encoding->nametab, MDVI_KEY(encoding->vector[i]), (DviHashKey)Int2Ptr(i), MDVI_HASH_UNCHECKED); } } ASSERT_VALUE(encodings.count, 0); mdvi_hash_create(&enctable, ENC_HASH_SIZE); mdvi_hash_create(&enctable_file, ENC_HASH_SIZE); enctable_file.hash_free = file_hash_free; mdvi_hash_add(&enctable, MDVI_KEY(encoding->name), encoding, MDVI_HASH_UNCHECKED); listh_prepend(&encodings, LIST(encoding)); tex_text_encoding = encoding; default_encoding = tex_text_encoding; } int mdvi_set_default_encoding(const char *name) { DviEncoding *enc, *old; enc = find_encoding(name); if(enc == NULL) return -1; if(enc == default_encoding) return 0; /* this will read it from file if necessary, * but it can fail if the file is corrupted */ enc = mdvi_request_encoding(name); if(enc == NULL) return -1; old = default_encoding; default_encoding = enc; if(old != tex_text_encoding) mdvi_release_encoding(old, 1); return 0; } int mdvi_init_fontmaps(void) { char *file; char *line; FILE *in; Dstring input; int count = 0; char *config; if(fontmaps_loaded) return 0; /* we will only try this once */ fontmaps_loaded = 1; DEBUG((DBG_FMAP, "reading fontmaps\n")); /* make sure the static encoding is there */ init_static_encoding(); /* create the fontmap hash table */ mdvi_hash_create(&maptable, MAP_HASH_SIZE); /* get the name of our configuration file */ config = kpse_cnf_get("mdvi-config"); if(config == NULL) config = MDVI_DEFAULT_CONFIG; /* let's ask kpathsea for the file first */ file = kpse_find_file(config, kpse_program_text_format, 0); if(file == NULL) in = fopen(config, "r"); else { in = fopen(file, "r"); xfree(file); } if(in == NULL) return -1; dstring_init(&input); while((line = dgets(&input, in)) != NULL) { char *arg; SKIPSP(line); if(*line < ' ' || *line == '#' || *line == '%') continue; if(STRNEQ(line, "fontmap", 7)) { DviFontMapEnt *ent; arg = getstring(line + 7, " \t", &line); *line = 0; DEBUG((DBG_FMAP, "%s: loading fontmap\n", arg)); ent = mdvi_load_fontmap(arg); if(ent == NULL) warning(_("%s: could not load fontmap\n"), arg); else { DEBUG((DBG_FMAP, "%s: installing fontmap\n", arg)); mdvi_install_fontmap(ent); count++; } } else if(STRNEQ(line, "encoding", 8)) { arg = getstring(line + 8, " \t", &line); *line = 0; if(arg && *arg) register_encoding(arg, 1); } else if(STRNEQ(line, "default-encoding", 16)) { arg = getstring(line + 16, " \t", &line); *line = 0; if(mdvi_set_default_encoding(arg) < 0) warning(_("%s: could not set as default encoding\n"), arg); } else if(STRNEQ(line, "psfontpath", 10)) { arg = getstring(line + 11, " \t", &line); *line = 0; if(!psinitialized) ps_init_default_paths(); if(psfontdir) xfree(psfontdir); psfontdir = kpse_path_expand(arg); } else if(STRNEQ(line, "pslibpath", 9)) { arg = getstring(line + 10, " \t", &line); *line = 0; if(!psinitialized) ps_init_default_paths(); if(pslibdir) xfree(pslibdir); pslibdir = kpse_path_expand(arg); } else if(STRNEQ(line, "psfontmap", 9)) { arg = getstring(line + 9, " \t", &line); *line = 0; if(mdvi_ps_read_fontmap(arg) < 0) warning("%s: %s: could not read PS fontmap\n", config, arg); } } fclose(in); dstring_reset(&input); fontmaps_loaded = 1; DEBUG((DBG_FMAP, "%d files installed, %d fontmaps\n", count, fontmaps.count)); return count; } int mdvi_query_fontmap(DviFontMapInfo *info, const char *fontname) { DviFontMapEnt *ent; if(!fontmaps_loaded && mdvi_init_fontmaps() < 0) return -1; ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(fontname)); if(ent == NULL) return -1; info->psname = ent->psname; info->encoding = ent->encoding; info->fontfile = ent->fontfile; info->extend = ent->extend; info->slant = ent->slant; info->fullfile = ent->fullfile; return 0; } int mdvi_add_fontmap_file(const char *name, const char *fullpath) { DviFontMapEnt *ent; if(!fontmaps_loaded && mdvi_init_fontmaps() < 0) return -1; ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(name)); if(ent == NULL) return -1; if(ent->fullfile) xfree(ent->fullfile); ent->fullfile = xstrdup(fullpath); return 0; } void mdvi_flush_encodings(void) { DviEncoding *enc; if(enctable.nbucks == 0) return; DEBUG((DBG_FMAP, "flushing %d encodings\n", encodings.count)); /* asked to remove all encodings */ for(; (enc = (DviEncoding *)encodings.head); ) { encodings.head = LIST(enc->next); if((enc != tex_text_encoding && enc->links) || enc->links > 1) { warning(_("encoding vector `%s' is in use\n"), enc->name); } destroy_encoding(enc); } /* destroy the static encoding */ if(tex_text_encoding->nametab.buckets) mdvi_hash_reset(&tex_text_encoding->nametab, 0); mdvi_hash_reset(&enctable, 0); mdvi_hash_reset(&enctable_file, 0); } void mdvi_flush_fontmaps(void) { DviFontMapEnt *ent; if(!fontmaps_loaded) return; DEBUG((DBG_FMAP, "flushing %d fontmaps\n", fontmaps.count)); for(; (ent = (DviFontMapEnt *)fontmaps.head); ) { fontmaps.head = LIST(ent->next); free_ent(ent); } mdvi_hash_reset(&maptable, 0); fontmaps_loaded = 0; } /* reading of PS fontmaps */ void ps_init_default_paths(void) { char *kppath = mdvi_getenv("MDVI_PS_LIBPATH"); char *kfpath = mdvi_getenv("MDVI_PS_FONTPATH"); ASSERT(psinitialized == 0); if(kppath == NULL) kppath = getenv("GS_LIB"); if(kfpath == NULL) kfpath = getenv("GS_FONTPATH"); if(kppath != NULL) pslibdir = kpse_path_expand(kppath); if(kfpath != NULL) psfontdir = kpse_path_expand(kfpath); listh_init(&psfonts); mdvi_hash_create(&pstable, PSMAP_HASH_SIZE); psinitialized = 1; } int mdvi_ps_read_fontmap(const char *name) { char *fullname; FILE *in; Dstring dstr; char *line; int count = 0; if(!psinitialized) ps_init_default_paths(); if(pslibdir) fullname = kpse_path_search(pslibdir, name, 1); else fullname = (char *)name; in = fopen(fullname, "r"); if(in == NULL) { if(fullname != name) xfree(fullname); return -1; } dstring_init(&dstr); while((line = dgets(&dstr, in)) != NULL) { char *name; char *mapname; const char *ext; PSFontMap *ps; SKIPSP(line); /* we're looking for lines of the form * /FONT-NAME (fontfile) * /FONT-NAME /FONT-ALIAS */ if(*line != '/') continue; name = getword(line + 1, " \t", &line); if(*line) *line++ = 0; mapname = getword(line, " \t", &line); if(*line) *line++ = 0; if(!name || !mapname || !*name) continue; if(*mapname == '(') { char *end; mapname++; for(end = mapname; *end && *end != ')'; end++); *end = 0; } if(!*mapname) continue; /* dont add `.gsf' fonts, which require a full blown * PostScript interpreter */ ext = file_extension(mapname); if(ext && STREQ(ext, "gsf")) { DEBUG((DBG_FMAP, "(ps) %s: font `%s' ignored\n", name, mapname)); continue; } ps = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(name)); if(ps != NULL) { if(STREQ(ps->mapname, mapname)) continue; DEBUG((DBG_FMAP, "(ps) replacing font `%s' (%s) by `%s'\n", name, ps->mapname, mapname)); xfree(ps->mapname); ps->mapname = xstrdup(mapname); if(ps->fullname) { xfree(ps->fullname); ps->fullname = NULL; } } else { DEBUG((DBG_FMAP, "(ps) adding font `%s' as `%s'\n", name, mapname)); ps = xalloc(PSFontMap); ps->psname = xstrdup(name); ps->mapname = xstrdup(mapname); ps->fullname = NULL; listh_append(&psfonts, LIST(ps)); mdvi_hash_add(&pstable, MDVI_KEY(ps->psname), ps, MDVI_HASH_UNCHECKED); count++; } } fclose(in); dstring_reset(&dstr); DEBUG((DBG_FMAP, "(ps) %s: %d PostScript fonts registered\n", fullname, count)); return 0; } void mdvi_ps_flush_fonts(void) { PSFontMap *map; if(!psinitialized) return; DEBUG((DBG_FMAP, "(ps) flushing PS font map (%d) entries\n", psfonts.count)); mdvi_hash_reset(&pstable, 0); for(; (map = (PSFontMap *)psfonts.head); ) { psfonts.head = LIST(map->next); xfree(map->psname); xfree(map->mapname); if(map->fullname) xfree(map->fullname); xfree(map); } listh_init(&psfonts); if(pslibdir) { xfree(pslibdir); pslibdir = NULL; } if(psfontdir) { xfree(psfontdir); psfontdir = NULL; } psinitialized = 0; } char *mdvi_ps_find_font(const char *psname) { PSFontMap *map, *smap; char *filename; int recursion_limit = 32; DEBUG((DBG_FMAP, "(ps) resolving PS font `%s'\n", psname)); if(!psinitialized) return NULL; map = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(psname)); if(map == NULL) return NULL; if(map->fullname) return xstrdup(map->fullname); /* is it an alias? */ smap = map; while(recursion_limit-- > 0 && smap && *smap->mapname == '/') smap = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(smap->mapname + 1)); if(smap == NULL) { if(recursion_limit == 0) DEBUG((DBG_FMAP, "(ps) %s: possible loop in PS font map\n", psname)); return NULL; } if(psfontdir) filename = kpse_path_search(psfontdir, smap->mapname, 1); else if(file_exists(map->mapname)) filename = xstrdup(map->mapname); else filename = NULL; if(filename) map->fullname = xstrdup(filename); return filename; } /* * To get metric info for a font, we proceed as follows: * - We try to find NAME.. * - We query the fontmap for NAME. * - We get back a PSNAME, and use to find the file in the PS font map. * - We get the PSFONT file name, replace its extension by "afm" and * lookup the file in GS's font search path. * - We finally read the data, transform it as specified in our font map, * and return it to the caller. The new data is left in the font metrics * cache, so the next time it will be found at the first step (when we look * up NAME.afm). * * The name `_ps_' in this function is not meant to imply that it can be * used for Type1 fonts only. It should be usable for TrueType fonts as well. * * The returned metric info is subjected to the same caching mechanism as * all the other metric data, as returned by get_font_metrics(). One should * not modify the returned data at all, and it should be disposed with * free_font_metrics(). */ TFMInfo *mdvi_ps_get_metrics(const char *fontname) { TFMInfo *info; DviFontMapInfo map; char buffer[64]; /* to avoid mallocs */ char *psfont; char *basefile; char *afmfile; char *ext; int baselen; int nc; TFMChar *ch; double efactor; double sfactor; DEBUG((DBG_FMAP, "(ps) %s: looking for metric data\n", fontname)); info = get_font_metrics(fontname, DviFontAny, NULL); if(info != NULL) return info; /* query the fontmap */ if(mdvi_query_fontmap(&map, fontname) < 0 || !map.psname) return NULL; /* get the PS font */ psfont = mdvi_ps_find_font(map.psname); if(psfont == NULL) return NULL; DEBUG((DBG_FMAP, "(ps) %s: found as PS font `%s'\n", fontname, psfont)); /* replace its extension */ basefile = strrchr(psfont, '/'); if(basefile == NULL) basefile = psfont; baselen = strlen(basefile); ext = strrchr(basefile, '.'); if(ext != NULL) *ext = 0; if(baselen + 4 < 64) afmfile = &buffer[0]; else afmfile = xmalloc(baselen + 5); strcpy(afmfile, basefile); strcpy(afmfile + baselen, ".afm"); /* we don't need this anymore */ xfree(psfont); DEBUG((DBG_FMAP, "(ps) %s: looking for `%s'\n", fontname, afmfile)); /* lookup the file */ psfont = kpse_path_search(psfontdir, afmfile, 1); /* don't need this anymore */ if(afmfile != &buffer[0]) xfree(afmfile); if(psfont != NULL) { info = get_font_metrics(fontname, DviFontAFM, psfont); xfree(psfont); } else info = NULL; if(info == NULL || (!map.extend && !map.slant)) return info; /* * transform the data as prescribed -- keep in mind that `info' * points to CACHED data, so we're modifying the metric cache * in place. */ #define DROUND(x) ((x) >= 0 ? floor((x) + 0.5) : ceil((x) - 0.5)) #define TRANSFORM(x,y) DROUND(efactor * (x) + sfactor * (y)) efactor = (double)map.extend / 10000.0; sfactor = (double)map.slant / 10000.0; DEBUG((DBG_FMAP, "(ps) %s: applying extend=%f, slant=%f\n", efactor, sfactor)); nc = info->hic - info->loc + 1; for(ch = info->chars; ch < info->chars + nc; ch++) { /* the AFM bounding box is: * wx = ch->advance * llx = ch->left * lly = -ch->depth * urx = ch->right * ury = ch->height * what we do here is transform wx, llx, and urx by * newX = efactor * oldX + sfactor * oldY * where for `wx' oldY = 0. Also, these numbers are all in * TFM units (i.e. TFM's fix-words, which is just the actual * number times 2^20, no need to do anything to it). */ if(ch->present) { ch->advance = TRANSFORM(ch->advance, 0); ch->left = TRANSFORM(ch->left, -ch->depth); ch->right = TRANSFORM(ch->right, ch->height); } } return info; }