diff options
Diffstat (limited to 'backend/dvi/mdvi-lib/fontsrch.c')
-rw-r--r-- | backend/dvi/mdvi-lib/fontsrch.c | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/backend/dvi/mdvi-lib/fontsrch.c b/backend/dvi/mdvi-lib/fontsrch.c new file mode 100644 index 00000000..8ebaa5ca --- /dev/null +++ b/backend/dvi/mdvi-lib/fontsrch.c @@ -0,0 +1,371 @@ +/* fontsearch.c -- implements the font lookup mechanism in MDVI */ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * How this works: + * Fonts are divided into MAX_CLASS priority classes. The first + * MAX_CLASS-1 ones correspond to `real' fonts (pk, gf, vf, type1, truetype, + * etc). The last one corresponds to `metric' fonts that are used as a last + * resort (tfm, afm, ofm, ...). When a font is looked up, it is tried in a + * `high' priority class (0 being the highest priority). The priority is + * lowered until it reaches MAX_CLASS-1. Then the whole thing is repeated + * for the fallback font. When the search reaches MAX_CLASS-1, we lookup the + * original font, and then the fallback font. The search can be done + * incrementally, with several calls to mdvi_lookup_font(). If this function + * is called again to continue a search, the function assumes the previous + * font it returned was not valid, and it goes on to the next step. + * + * Reason for this: + * Some font types are quite expensive to load (e.g. Type1), so loading + * them is deferred until the last possible moment. This means that a font that + * was supposed to exist may have to be discarded. Until now, MDVI had no ability to + * "resume" a search, so in this case it would have produced an error, regardless + * of whether the offending font existed in other formats. + * Also, given the large number of font types supported by MDVI, some mechanism + * was necessary to bring some order into the chaos. + * + * This mechanism fixes these two problems. For the first one, a search can + * be "resumed" and all the font formats tried for the missing font, and + * again for the fallback font (see above). As for the second, the + * hierarchical division in classes gives a lot of flexibility in how the + * fonts are configured. + */ + +#include <config.h> +#include "mdvi.h" + +#define HAVE_PROTOTYPES 1 +#include <kpathsea/tex-file.h> +#include <kpathsea/tex-glyph.h> + +struct _DviFontClass { + DviFontClass *next; + DviFontClass *prev; + DviFontInfo info; + int links; + int id; +}; + +char *_mdvi_fallback_font = MDVI_FALLBACK_FONT; + +/* this leaves classes 0 and 1 for `real' fonts */ +#define MAX_CLASS 3 +static ListHead font_classes[MAX_CLASS]; +static int initialized = 0; + +static void init_font_classes(void) +{ + int i; + + for(i = 0; i < MAX_CLASS; i++) + listh_init(&font_classes[i]); + initialized = 1; +} + +int mdvi_get_font_classes(void) +{ + return (MAX_CLASS - 2); +} + +char **mdvi_list_font_class(int klass) +{ + char **list; + int i, n; + DviFontClass *fc; + + if(klass == -1) + klass = MAX_CLASS-1; + if(klass < 0 || klass >= MAX_CLASS) + return NULL; + n = font_classes[klass].count; + list = xnalloc(char *, n + 1); + fc = (DviFontClass *)font_classes[klass].head; + for(i = 0; i < n; fc = fc->next, i++) { + list[i] = mdvi_strdup(fc->info.name); + } + list[i] = NULL; + return list; +} + +int mdvi_register_font_type(DviFontInfo *info, int klass) +{ + DviFontClass *fc; + + if(klass == -1) + klass = MAX_CLASS-1; + if(klass < 0 || klass >= MAX_CLASS) + return -1; + if(!initialized) + init_font_classes(); + fc = xalloc(struct _DviFontClass); + fc->links = 0; + fc->id = klass; + fc->info.name = mdvi_strdup(info->name); + fc->info.scalable = info->scalable; + fc->info.load = info->load; + fc->info.getglyph = info->getglyph; + fc->info.shrink0 = info->shrink0; + fc->info.shrink1 = info->shrink1; + fc->info.freedata = info->freedata; + fc->info.reset = info->reset; + fc->info.lookup = info->lookup; + fc->info.kpse_type = info->kpse_type; + listh_append(&font_classes[klass], LIST(fc)); + return 0; +} + +int mdvi_unregister_font_type(const char *name, int klass) +{ + DviFontClass *fc; + int k; + + if(klass == -1) + klass = MAX_CLASS - 1; + + if(klass >= 0 && klass < MAX_CLASS) { + k = klass; + LIST_FOREACH(fc, DviFontClass, &font_classes[k]) { + if(STREQ(fc->info.name, name)) + break; + } + } else if(klass < 0) { + for(k = 0; k < MAX_CLASS; k++) { + LIST_FOREACH(fc, DviFontClass, &font_classes[k]) { + if(STREQ(fc->info.name, name)) + break; + } + if(fc) break; + } + } else + return -1; + + if(fc == NULL || fc->links) + return -1; + /* remove it */ + listh_remove(&font_classes[k], LIST(fc)); + + /* and destroy it */ + mdvi_free(fc->info.name); + mdvi_free(fc); + return 0; +} + +static char *lookup_font(DviFontClass *ptr, const char *name, Ushort *h, Ushort *v) +{ + char *filename; + + /* + * If the font type registered a function to do the lookup, use that. + * Otherwise we use kpathsea. + */ + if(ptr->info.lookup) + filename = ptr->info.lookup(name, h, v); + else if(ptr->info.kpse_type <= kpse_any_glyph_format) { + kpse_glyph_file_type type; + + filename = kpse_find_glyph(name, Max(*h, *v), + ptr->info.kpse_type, &type); + /* if kpathsea returned a fallback font, reject it */ + if(filename && type.source == kpse_glyph_source_fallback) { + mdvi_free(filename); + filename = NULL; + } else if(filename) + *h = *v = type.dpi; + } else + filename = kpse_find_file(name, ptr->info.kpse_type, 1); + return filename; +} + +/* + * Class MAX_CLASS-1 is special: it consists of `metric' fonts that should + * be tried as a last resort + */ +char *mdvi_lookup_font(DviFontSearch *search) +{ + int kid; + int k; + DviFontClass *ptr; + DviFontClass *last; + char *filename = NULL; + const char *name; + Ushort hdpi, vdpi; + + if(search->id < 0) + return NULL; + + if(search->curr == NULL) { + /* this is the initial search */ + name = search->wanted_name; + hdpi = search->hdpi; + vdpi = search->vdpi; + kid = 0; + last = NULL; + } else { + name = search->actual_name; + hdpi = search->actual_hdpi; + vdpi = search->actual_vdpi; + kid = search->id; + last = search->curr; + } + + ptr = NULL; +again: + /* try all classes except MAX_CLASS-1 */ + for(k = kid; !filename && k < MAX_CLASS-1; k++) { + if(last == NULL) + ptr = (DviFontClass *)font_classes[k].head; + else + ptr = last->next; + while(ptr) { + DEBUG((DBG_FONTS, "%d: trying `%s' at (%d,%d)dpi as `%s'\n", + k, name, hdpi, vdpi, ptr->info.name)); + /* lookup the font in this class */ + filename = lookup_font(ptr, name, &hdpi, &vdpi); + if(filename) + break; + ptr = ptr->next; + } + last = NULL; + } + if(filename != NULL) { + search->id = k-1; + search->curr = ptr; + search->actual_name = name; + search->actual_hdpi = hdpi; + search->actual_vdpi = vdpi; + search->info = &ptr->info; + ptr->links++; + return filename; + } + + if(kid < MAX_CLASS - 1 && !STREQ(name, _mdvi_fallback_font)) { + mdvi_warning("font `%s' at %dx%d not found, trying `%s' instead\n", + name, hdpi, vdpi, _mdvi_fallback_font); + name = _mdvi_fallback_font; + kid = 0; + goto again; + } + + /* we tried the fallback font, and all the `real' classes. Let's + * try the `metric' class now */ + name = search->wanted_name; + hdpi = search->hdpi; + vdpi = search->vdpi; + if(kid == MAX_CLASS-1) { + /* we were looking into this class from the beginning */ + if(last == NULL) { + /* no more fonts to try */ + return NULL; + } + ptr = last->next; + } else { + mdvi_warning("font `%s' not found, trying metric files instead\n", + name); + ptr = (DviFontClass *)font_classes[MAX_CLASS-1].head; + } + +metrics: + while(ptr) { + DEBUG((DBG_FONTS, "metric: trying `%s' at (%d,%d)dpi as `%s'\n", + name, hdpi, vdpi, ptr->info.name)); + filename = lookup_font(ptr, name, &hdpi, &vdpi); + if(filename) + break; + ptr = ptr->next; + } + if(filename != NULL) { + if(STREQ(name, _mdvi_fallback_font)) + search->id = MAX_CLASS; + else + search->id = MAX_CLASS - 1; + search->curr = ptr; + search->actual_name = name; + search->actual_hdpi = hdpi; + search->actual_vdpi = vdpi; + search->info = &ptr->info; + ptr->links++; + return filename; + } + if(!STREQ(name, _mdvi_fallback_font)) { + mdvi_warning("metric file for `%s' not found, trying `%s' instead\n", + name, _mdvi_fallback_font); + name = _mdvi_fallback_font; + ptr = (DviFontClass *)font_classes[MAX_CLASS-1].head; + goto metrics; + } + + search->id = -1; + search->actual_name = NULL; + + /* tough luck, nothing found */ + return NULL; +} + +/* called by `font_reference' to do the initial lookup */ +DviFont *mdvi_add_font(const char *name, Int32 sum, + int hdpi, int vdpi, Int32 scale) +{ + DviFont *font; + + font = xalloc(DviFont); + font->fontname = mdvi_strdup(name); + SEARCH_INIT(font->search, font->fontname, hdpi, vdpi); + font->filename = mdvi_lookup_font(&font->search); + if(font->filename == NULL) { + /* this answer is final */ + mdvi_free(font->fontname); + mdvi_free(font); + return NULL; + } + font->hdpi = font->search.actual_hdpi; + font->vdpi = font->search.actual_vdpi; + font->scale = scale; + font->design = 0; + font->checksum = sum; + font->type = 0; + font->links = 0; + font->loc = 0; + font->hic = 0; + font->in = NULL; + font->chars = NULL; + font->subfonts = NULL; + + return font; +} + +int mdvi_font_retry(DviParams *params, DviFont *font) +{ + /* try the search again */ + char *filename; + + ASSERT(font->search.curr != NULL); + /* we won't be using this class anymore */ + font->search.curr->links--; + + filename = mdvi_lookup_font(&font->search); + if(filename == NULL) + return -1; + mdvi_free(font->filename); + font->filename = filename; + /* copy the new information */ + font->hdpi = font->search.actual_hdpi; + font->vdpi = font->search.actual_vdpi; + + return 0; +} |