summaryrefslogtreecommitdiff
path: root/backend/dvi/mdvi-lib/font.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/dvi/mdvi-lib/font.c')
-rw-r--r--backend/dvi/mdvi-lib/font.c519
1 files changed, 519 insertions, 0 deletions
diff --git a/backend/dvi/mdvi-lib/font.c b/backend/dvi/mdvi-lib/font.c
new file mode 100644
index 00000000..2f655df0
--- /dev/null
+++ b/backend/dvi/mdvi-lib/font.c
@@ -0,0 +1,519 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+
+#include "mdvi.h"
+#include "private.h"
+
+static ListHead fontlist;
+
+extern char *_mdvi_fallback_font;
+
+extern void vf_free_macros(DviFont *);
+
+#define finfo search.info
+#define TYPENAME(font) \
+ ((font)->finfo ? (font)->finfo->name : "none")
+
+int font_reopen(DviFont *font)
+{
+ if(font->in)
+ fseek(font->in, (long)0, SEEK_SET);
+ else if((font->in = fopen(font->filename, "rb")) == NULL) {
+ DEBUG((DBG_FILES, "reopen(%s) -> Error\n", font->filename));
+ return -1;
+ }
+ DEBUG((DBG_FILES, "reopen(%s) -> Ok.\n", font->filename));
+ return 0;
+}
+
+/* used from context: params and device */
+static int load_font_file(DviParams *params, DviFont *font)
+{
+ int status;
+
+ if(SEARCH_DONE(font->search))
+ return -1;
+ if(font->in == NULL && font_reopen(font) < 0)
+ return -1;
+ DEBUG((DBG_FONTS, "%s: loading %s font from `%s'\n",
+ font->fontname,
+ font->finfo->name, font->filename));
+ do {
+ status = font->finfo->load(params, font);
+ } while(status < 0 && mdvi_font_retry(params, font) == 0);
+ if(status < 0)
+ return -1;
+ if(font->in) {
+ fclose(font->in);
+ font->in = NULL;
+ }
+ DEBUG((DBG_FONTS, "reload_font(%s) -> %s\n",
+ font->fontname, status < 0 ? "Error" : "Ok"));
+ return 0;
+}
+
+void font_drop_one(DviFontRef *ref)
+{
+ DviFont *font;
+
+ font = ref->ref;
+ mdvi_free(ref);
+ /* drop all children */
+ for(ref = font->subfonts; ref; ref = ref->next) {
+ /* just adjust the reference counts */
+ ref->ref->links--;
+ }
+ if(--font->links == 0) {
+ /*
+ * this font doesn't have any more references, but
+ * we still keep it around in case a virtual font
+ * requests it.
+ */
+ if(font->in) {
+ fclose(font->in);
+ font->in = NULL;
+ }
+ if(LIST(font) != fontlist.tail) {
+ /* move it to the end of the list */
+ listh_remove(&fontlist, LIST(font));
+ listh_append(&fontlist, LIST(font));
+ }
+ }
+ DEBUG((DBG_FONTS, "%s: reference dropped, %d more left\n",
+ font->fontname, font->links));
+}
+
+void font_drop_chain(DviFontRef *head)
+{
+ DviFontRef *ptr;
+
+ for(; (ptr = head); ) {
+ head = ptr->next;
+ font_drop_one(ptr);
+ }
+}
+
+int font_free_unused(DviDevice *dev)
+{
+ DviFont *font, *next;
+ int count = 0;
+
+ DEBUG((DBG_FONTS, "destroying unused fonts\n"));
+ for(font = (DviFont *)fontlist.head; font; font = next) {
+ DviFontRef *ref;
+
+ next = font->next;
+ if(font->links)
+ continue;
+ count++;
+ DEBUG((DBG_FONTS, "removing unused %s font `%s'\n",
+ TYPENAME(font), font->fontname));
+ listh_remove(&fontlist, LIST(font));
+ if(font->in)
+ fclose(font->in);
+ /* get rid of subfonts (but can't use `drop_chain' here) */
+ for(; (ref = font->subfonts); ) {
+ font->subfonts = ref->next;
+ mdvi_free(ref);
+ }
+ /* remove this font */
+ font_reset_font_glyphs(dev, font, MDVI_FONTSEL_GLYPH);
+ /* let the font destroy its private data */
+ if(font->finfo->freedata)
+ font->finfo->freedata(font);
+ /* destroy characters */
+ if(font->chars)
+ mdvi_free(font->chars);
+ mdvi_free(font->fontname);
+ mdvi_free(font->filename);
+ mdvi_free(font);
+ }
+ DEBUG((DBG_FONTS, "%d unused fonts removed\n", count));
+ return count;
+}
+
+/* used from context: params and device */
+DviFontRef *
+font_reference(
+ DviParams *params, /* rendering parameters */
+ Int32 id, /* external id number */
+ const char *name, /* font name */
+ Int32 sum, /* checksum (from DVI of VF) */
+ int hdpi, /* resolution */
+ int vdpi,
+ Int32 scale) /* scaling factor (from DVI or VF) */
+{
+ DviFont *font;
+ DviFontRef *ref;
+ DviFontRef *subfont_ref;
+
+ /* see if there is a font with the same characteristics */
+ for(font = (DviFont *)fontlist.head; font; font = font->next) {
+ if(strcmp(name, font->fontname) == 0
+ && (!sum || !font->checksum || font->checksum == sum)
+ && font->hdpi == hdpi
+ && font->vdpi == vdpi
+ && font->scale == scale)
+ break;
+ }
+ /* try to load the font */
+ if(font == NULL) {
+ font = mdvi_add_font(name, sum, hdpi, vdpi, scale);
+ if(font == NULL)
+ return NULL;
+ listh_append(&fontlist, LIST(font));
+ }
+ if(!font->links && !font->chars && load_font_file(params, font) < 0) {
+ DEBUG((DBG_FONTS, "font_reference(%s) -> Error\n", name));
+ return NULL;
+ }
+ ref = xalloc(DviFontRef);
+ ref->ref = font;
+
+ font->links++;
+ for(subfont_ref = font->subfonts; subfont_ref; subfont_ref = subfont_ref->next) {
+ /* just adjust the reference counts */
+ subfont_ref->ref->links++;
+ }
+
+ ref->fontid = id;
+
+ if(LIST(font) != fontlist.head) {
+ listh_remove(&fontlist, LIST(font));
+ listh_prepend(&fontlist, LIST(font));
+ }
+
+ DEBUG((DBG_FONTS, "font_reference(%s) -> %d links\n",
+ font->fontname, font->links));
+ return ref;
+}
+
+void font_transform_glyph(DviOrientation orient, DviGlyph *g)
+{
+ BITMAP *map;
+ int x, y;
+
+ map = (BITMAP *)g->data;
+ if(MDVI_GLYPH_ISEMPTY(map))
+ map = NULL;
+
+ /* put the glyph in the right orientation */
+ switch(orient) {
+ case MDVI_ORIENT_TBLR:
+ break;
+ case MDVI_ORIENT_TBRL:
+ g->x = g->w - g->x;
+ if(map) bitmap_flip_horizontally(map);
+ break;
+ case MDVI_ORIENT_BTLR:
+ g->y = g->h - g->y;
+ if(map) bitmap_flip_vertically(map);
+ break;
+ case MDVI_ORIENT_BTRL:
+ g->x = g->w - g->x;
+ g->y = g->h - g->y;
+ if(map) bitmap_flip_diagonally(map);
+ break;
+ case MDVI_ORIENT_RP90:
+ if(map) bitmap_rotate_counter_clockwise(map);
+ y = g->y;
+ x = g->w - g->x;
+ g->x = y;
+ g->y = x;
+ SWAPINT(g->w, g->h);
+ break;
+ case MDVI_ORIENT_RM90:
+ if(map) bitmap_rotate_clockwise(map);
+ y = g->h - g->y;
+ x = g->x;
+ g->x = y;
+ g->y = x;
+ SWAPINT(g->w, g->h);
+ break;
+ case MDVI_ORIENT_IRP90:
+ if(map) bitmap_flip_rotate_counter_clockwise(map);
+ y = g->y;
+ x = g->x;
+ g->x = y;
+ g->y = x;
+ SWAPINT(g->w, g->h);
+ break;
+ case MDVI_ORIENT_IRM90:
+ if(map) bitmap_flip_rotate_clockwise(map);
+ y = g->h - g->y;
+ x = g->w - g->x;
+ g->x = y;
+ g->y = x;
+ SWAPINT(g->w, g->h);
+ break;
+ }
+}
+
+static int load_one_glyph(DviContext *dvi, DviFont *font, int code)
+{
+ BITMAP *map;
+ DviFontChar *ch;
+ int status;
+
+#ifndef NODEBUG
+ ch = FONTCHAR(font, code);
+ DEBUG((DBG_GLYPHS, "loading glyph code %d in %s (at %u)\n",
+ code, font->fontname, ch->offset));
+#endif
+ if(font->finfo->getglyph == NULL) {
+ /* font type does not need to load glyphs (e.g. vf) */
+ return 0;
+ }
+
+ status = font->finfo->getglyph(&dvi->params, font, code);
+ if(status < 0)
+ return -1;
+ /* get the glyph again (font->chars may have changed) */
+ ch = FONTCHAR(font, code);
+#ifndef NODEBUG
+ map = (BITMAP *)ch->glyph.data;
+ if(DEBUGGING(BITMAP_DATA)) {
+ DEBUG((DBG_BITMAP_DATA,
+ "%s: new %s bitmap for character %d:\n",
+ font->fontname, TYPENAME(font), code));
+ if(MDVI_GLYPH_ISEMPTY(map))
+ DEBUG((DBG_BITMAP_DATA, "blank bitmap\n"));
+ else
+ bitmap_print(stderr, map);
+ }
+#endif
+ /* check if we have to scale it */
+ if(!font->finfo->scalable && font->hdpi != font->vdpi) {
+ int hs, vs, d;
+
+ /* we scale it ourselves */
+ d = Max(font->hdpi, font->vdpi);
+ hs = d / font->hdpi;
+ vs = d / font->vdpi;
+ if(ch->width && ch->height && (hs > 1 || vs > 1)) {
+ int h, v;
+ DviGlyph glyph;
+
+ DEBUG((DBG_FONTS,
+ "%s: scaling glyph %d to resolution %dx%d\n",
+ font->fontname, code, font->hdpi, font->vdpi));
+ h = dvi->params.hshrink;
+ v = dvi->params.vshrink;
+ d = dvi->params.density;
+ dvi->params.hshrink = hs;
+ dvi->params.vshrink = vs;
+ dvi->params.density = 50;
+ /* shrink it */
+ font->finfo->shrink0(dvi, font, ch, &glyph);
+ /* restore parameters */
+ dvi->params.hshrink = h;
+ dvi->params.vshrink = v;
+ dvi->params.density = d;
+ /* update glyph data */
+ if(!MDVI_GLYPH_ISEMPTY(ch->glyph.data))
+ bitmap_destroy((BITMAP *)ch->glyph.data);
+ ch->glyph.data = glyph.data;
+ ch->glyph.x = glyph.x;
+ ch->glyph.y = glyph.y;
+ ch->glyph.w = glyph.w;
+ ch->glyph.h = glyph.h;
+ }
+
+ }
+ font_transform_glyph(dvi->params.orientation, &ch->glyph);
+
+ return 0;
+}
+
+DviFontChar *font_get_glyph(DviContext *dvi, DviFont *font, int code)
+{
+ DviFontChar *ch;
+
+again:
+ /* if we have not loaded the font yet, do so now */
+ if(!font->chars && load_font_file(&dvi->params, font) < 0)
+ return NULL;
+
+ /* get the unscaled glyph, maybe loading it from disk */
+ ch = FONTCHAR(font, code);
+ if(!ch || !glyph_present(ch))
+ return NULL;
+ if(!ch->loaded && load_one_glyph(dvi, font, code) == -1) {
+ if(font->chars == NULL) {
+ /* we need to try another font class */
+ goto again;
+ }
+ return NULL;
+ }
+ /* yes, we have to do this again */
+ ch = FONTCHAR(font, code);
+
+ /* Got the glyph. If we also have the right scaled glyph, do no more */
+ if(!ch->width || !ch->height ||
+ font->finfo->getglyph == NULL ||
+ (dvi->params.hshrink == 1 && dvi->params.vshrink == 1))
+ return ch;
+
+ /* If the glyph is empty, we just need to shrink the box */
+ if(ch->missing || MDVI_GLYPH_ISEMPTY(ch->glyph.data)) {
+ if(MDVI_GLYPH_UNSET(ch->shrunk.data))
+ mdvi_shrink_box(dvi, font, ch, &ch->shrunk);
+ return ch;
+ } else if(MDVI_ENABLED(dvi, MDVI_PARAM_ANTIALIASED)) {
+ if(ch->grey.data &&
+ !MDVI_GLYPH_ISEMPTY(ch->grey.data) &&
+ ch->fg == dvi->curr_fg &&
+ ch->bg == dvi->curr_bg)
+ return ch;
+ if(ch->grey.data &&
+ !MDVI_GLYPH_ISEMPTY(ch->grey.data)) {
+ if(dvi->device.free_image)
+ dvi->device.free_image(ch->grey.data);
+ ch->grey.data = NULL;
+ }
+ font->finfo->shrink1(dvi, font, ch, &ch->grey);
+ } else if(!ch->shrunk.data)
+ font->finfo->shrink0(dvi, font, ch, &ch->shrunk);
+
+ return ch;
+}
+
+void font_reset_one_glyph(DviDevice *dev, DviFontChar *ch, int what)
+{
+ if(!glyph_present(ch))
+ return;
+ if(what & MDVI_FONTSEL_BITMAP) {
+ if(MDVI_GLYPH_NONEMPTY(ch->shrunk.data))
+ bitmap_destroy((BITMAP *)ch->shrunk.data);
+ ch->shrunk.data = NULL;
+ }
+ if(what & MDVI_FONTSEL_GREY) {
+ if(MDVI_GLYPH_NONEMPTY(ch->grey.data)) {
+ if(dev->free_image)
+ dev->free_image(ch->grey.data);
+ }
+ ch->grey.data = NULL;
+ }
+ if(what & MDVI_FONTSEL_GLYPH) {
+ if(MDVI_GLYPH_NONEMPTY(ch->glyph.data))
+ bitmap_destroy((BITMAP *)ch->glyph.data);
+ ch->glyph.data = NULL;
+ ch->loaded = 0;
+ }
+}
+
+void font_reset_font_glyphs(DviDevice *dev, DviFont *font, int what)
+{
+ int i;
+ DviFontChar *ch;
+
+ if(what & MDVI_FONTSEL_GLYPH)
+ what |= MDVI_FONTSEL_BITMAP|MDVI_FONTSEL_GREY;
+ if(font->subfonts) {
+ DviFontRef *ref;
+
+ for(ref = font->subfonts; ref; ref = ref->next)
+ font_reset_font_glyphs(dev, ref->ref, what);
+ }
+ if(font->in) {
+ DEBUG((DBG_FILES, "close(%s)\n", font->filename));
+ fclose(font->in);
+ font->in = NULL;
+ }
+ if(font->finfo->getglyph == NULL)
+ return;
+ DEBUG((DBG_FONTS, "resetting glyphs in font `%s'\n", font->fontname));
+ for(ch = font->chars, i = font->loc; i <= font->hic; ch++, i++) {
+ if(glyph_present(ch))
+ font_reset_one_glyph(dev, ch, what);
+ }
+ if((what & MDVI_FONTSEL_GLYPH) && font->finfo->reset)
+ font->finfo->reset(font);
+}
+
+void font_reset_chain_glyphs(DviDevice *dev, DviFontRef *head, int what)
+{
+ DviFontRef *ref;
+
+ for(ref = head; ref; ref = ref->next)
+ font_reset_font_glyphs(dev, ref->ref, what);
+}
+
+static int compare_refs(const void *p1, const void *p2)
+{
+ return ((*(DviFontRef **)p1)->fontid - (*(DviFontRef **)p2)->fontid);
+}
+
+void font_finish_definitions(DviContext *dvi)
+{
+ int count;
+ DviFontRef **map, *ref;
+
+ /* first get rid of unused fonts */
+ font_free_unused(&dvi->device);
+
+ if(dvi->fonts == NULL) {
+ mdvi_warning(_("%s: no fonts defined\n"), dvi->filename);
+ return;
+ }
+ map = xnalloc(DviFontRef *, dvi->nfonts);
+ for(count = 0, ref = dvi->fonts; ref; ref = ref->next)
+ map[count++] = ref;
+ /* sort the array by font id */
+ qsort(map, dvi->nfonts, sizeof(DviFontRef *), compare_refs);
+ dvi->fontmap = map;
+}
+
+DviFontRef *font_find_flat(DviContext *dvi, Int32 id)
+{
+ DviFontRef *ref;
+
+ for(ref = dvi->fonts; ref; ref = ref->next)
+ if(ref->fontid == id)
+ break;
+ return ref;
+}
+
+DviFontRef *font_find_mapped(DviContext *dvi, Int32 id)
+{
+ int lo, hi, n;
+ DviFontRef **map;
+
+ /* do a binary search */
+ lo = 0; hi = dvi->nfonts;
+ map = dvi->fontmap;
+ while(lo < hi) {
+ int sign;
+
+ n = (hi + lo) >> 1;
+ sign = (map[n]->fontid - id);
+ if(sign == 0)
+ break;
+ else if(sign < 0)
+ lo = n;
+ else
+ hi = n;
+ }
+ if(lo >= hi)
+ return NULL;
+ return map[n];
+}
+