/***** * fonts.c : XmHTML font loading & caching routines. * * This file Version $Revision: 1.10 $ * * Creation date: Mon Sep 22 09:46:00 GMT+0100 1997 * Last modification: $Date: 1999/07/29 01:26:28 $ * By: $Author: sopwith $ * Current State: $State: Exp $ * * Author: newt * * Copyright (C) 1994-1997 by Ripley Software Development * All Rights Reserved * * This file is part of the XmHTML Widget Library * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****/ /***** * ChangeLog * $Log: fonts.c,v $ * Revision 1.10 1999/07/29 01:26:28 sopwith * * * Fix all warnings. * * Revision 1.9 1999/06/02 01:00:38 unammx * * 1999-06-01 Akira Higuchi * * * libgnomeui/gnome-canvas-text.c: * * libgnomeui/gnome-icon-item.c: * * libgnomeui/gnome-less.c: Replace some gdk_font_load() calls with * gdk_fontset_load. Use a more open fontset rule to load the fonts. * * 1999-06-01 Akira Higuchi * * * gtk-xmhtml/XmHTMLP.h: Add three members lbearing, rbearing, * and width. These members are computed in allocFont(). * * * gtk-xmhtml/toolkit.h: Remove Toolkit_XFont() macro. * * * gtk-xmhtml/XmHTML.c: * * gtk-xmhtml/fonts.c: * * gtk-xmhtml/format.c: * * gtk-xmhtml/gtk-xmhtml.c: * * gtk-xmhtml/layout.c: * * gtk-xmhtml/paint.c: Add fontset support. We use gdk_fontset_load() * instead of gdk_font_load() iff a fontset is supplied for label * widgets. * * * gtk-xmhtml/test.c: Add gtk_set_locale() call before gtk_init(). * * Revision 1.8 1999/02/25 01:05:06 unammx * Missing bit of the strtok patches from Ulrich * * Revision 1.7 1998/05/11 07:54:07 kmaraas * 1998-05-11 Kjartan Maraas * * * Removed two compiler warnings in gnome-libs/gtk-xmhtml/ * fonts.c and gtk-forms.c. * * Revision 1.6 1998/02/12 03:08:37 unammx * Merge to Koen's XmHTML 1.1.2 + following fixes: * * Wed Feb 11 20:27:19 1998 Miguel de Icaza * * * gtk-forms.c (freeForm): gtk_destroy_widget is no longer needed * with the refcounting changes. * * * gtk-xmhtml.c (gtk_xmhtml_remove): Only god knows why I was * adding the just removed widget. * * Revision 1.5 1998/01/07 01:45:36 unammx * Gtk/XmHTML is ready to be used by the Gnome hackers now! * Weeeeeee! * * This afternoon: * * - Changes to integrate gtk-xmhtml into an autoconf setup. * * - Changes to make gtk-xmhtml a library to be used by Gnome * (simply include #endif #include #include #include #include /* isspace */ #include "XmHTMLP.h" #include "XmHTMLfuncs.h" #ifndef WITH_MOTIF # include #endif /*** External Function Prototype Declarations ***/ /*** Public Variable Declarations ***/ int xmhtml_fn_sizes[8], xmhtml_basefont_sizes[7], xmhtml_fn_fixed_sizes[2]; /*** Private Datatype Declarations ****/ /***** * A single font cache entry. * The font cache is a binary tree, sorted according to the name of a font. * The name field points to a field in the font field. *****/ typedef struct _fontCacheEntry{ XmHTMLfont *font; /* font data */ String name; /* font name */ Boolean is_map; /* true when this is a font mapping */ XmHTMLfont *map_to; /* ptr to real font data */ struct _fontCacheEntry *left; struct _fontCacheEntry *right; }fontCacheEntry; /***** * Definition of a display-bound font cache. * This structure contains the ID of the display this cache is used for, * the actual font cache, the default font for this display and a list of * widgets referencing this cache. *****/ typedef struct _fontCache{ Display *dpy; /* display were fonts come from */ int res_x; /* horizontal screen resolution */ int res_y; /* vertical screen resolution */ fontCacheEntry *cache; /* cached fonts for this display */ XmHTMLfont *default_font; /* default font */ int nwidgets; /* no of widgets referring this cache */ TWidgetList widgets; /* array of widget referring this cache */ struct _fontCache *next; /* ptr to next cache */ int nentries; /* no of cached fonts */ int nmaps; /* no of mapped fonts */ int nlookups; /* no of search actions */ int requests; /* no of requests made */ int hits; /* no of hits */ int misses; /* no of missed requests */ }fontCache; /*** Private Function Prototype Declarations ****/ /* create a fully valid XLFD */ static String makeFontName(String name, String foundry, String family, String weight, String slant, int points, String charset, String fam_return); static fontCacheEntry *insertFont(fontCacheEntry *entry, String name, XmHTMLfont *font, XmHTMLfont *map_to); /* get a font from the cache */ static XmHTMLfont *getFont(fontCacheEntry *entry, String name, Byte style); /* create a map for the given font */ static XmHTMLfont *mapFont(XmHTMLfont *font, String name); /* allocate a new XmHTMLfont entry */ static XmHTMLfont *allocFont(TFontStruct *xfont, String name, String family, Byte style); /* load or get a font from the cache */ static XmHTMLfont *loadAndCacheFont(TWidget w, String name, String family, Byte style); /* free all cached fonts for the given display */ static void freeFontEntries(Display *dpy, fontCacheEntry *fonts); /* (re-)initialize a font cache */ static void initializeFontSizeLists(XmHTMLWidget html); /*** Private Variable Declarations ***/ static fontCache *master_cache; /* master font cache */ static fontCache *curr_cache; /* current font cache */ /* Backup lists when sizes are not specified */ static int def_fn_sizes[8] = {140,80,240,180,160,140,120,100}; static int def_fn_fixed_sizes[2] = {120,80}; /***** * Name: makeFontName * Return Type: String * Description: creates a full 14 field font name from the given args. * In: * name: font base name, required * foundry: font foundry, optional * family: font family, optional * weight: font weight, required * slant: font slant, required * points: font pointsize (tenths of a point), required * charset: current ISO character set encoding * fam_return: XmHTML fontFamily spec, updated upon return. * Returns: * the composed font name * Note: * The name argument contains the following string: * foundry-family-width-spacing * The charset contains the last two fields of a valid font name: * iso9959-1 * When family is specified, foundry will be replaced by a wildcard * * this routine will compose the following fontname given the basename: * -foundry-family-weight-slant-width--*-points-res_x-res_y-spacing-*-charset *****/ static String makeFontName(String name, String foundry, String family, String weight, String slant, int points, String charset, String fam_return) { int i; static char fontfam[512], new_name[1024]; String fndry, fam, wd, sp; GtkWidget *dummy_label; gboolean use_fontset = 0; strncpy(fontfam, name, 511); fontfam[strlen(name)] = '\0'; /* foundry */ fndry = &fontfam[0]; /* family */ for(i = 0; fontfam[i] != '\0' && fontfam[i] != '-'; i++); fontfam[i++] = '\0'; fam = &fontfam[i]; /* set width */ for(; fontfam[i] != '\0' && fontfam[i] != '-'; i++); fontfam[i++] = '\0'; wd = &fontfam[i]; /* spacing */ for(; fontfam[i] != '\0' && fontfam[i] != '-'; i++); fontfam[i++] = '\0'; sp = &fontfam[i]; _XmHTMLFullDebug(8, ("fonts.c: makeFontName, split fontFamily " "value %s:\nfoundry : %s\nfamily : %s\nwidth : %s\nspacing : %s\n", name, fndry, fam, wd, sp)); /* determine whether we use a font or a fontset */ dummy_label = gtk_label_new("Dummy"); gtk_widget_ensure_style(dummy_label); use_fontset = (dummy_label->style->font->type == GDK_FONT_FONTSET); gtk_object_destroy(GTK_OBJECT(dummy_label)); /* screen resolutions are stored in the display-bound font cache */ if (use_fontset) { /* we try to load fonts close to the specified one */ /* FIXME: better way to determine the fontset name? */ sprintf(new_name, "-%s-%s-%s-%s-%s-*-*-%i-%i-%i-%s-*-%s," "-*-*-%s-%s-%s-*-*-%i-*-*-*-*-*," "-*-*-*-*-*-*-%i-*-*-*-*-*-*,*", foundry != NULL ? foundry : fndry, family != NULL ? family : fam, weight, slant, wd, points, curr_cache->res_x, curr_cache->res_y, sp, charset, weight, slant, wd, points, points / 10); } else { sprintf(new_name, "-%s-%s-%s-%s-%s-*-*-%i-%i-%i-%s-*-%s", foundry != NULL ? foundry : fndry, family != NULL ? family : fam, weight, slant, wd, points, curr_cache->res_x, curr_cache->res_y, sp, charset); } /* create XmHTML fontFamily spec for this font */ sprintf(fam_return, "%s-%s-%s-%s", (foundry != NULL ? foundry : fndry), (family != NULL ? family : fam), wd, sp); /* make it all lowercase */ my_locase(new_name); return(new_name); } /***** * Name: insertFont * Return Type: fontCacheEntry * Description: inserts the given font in the given font cache. * In: * entry: current font cache; * name: name of font to insert; * font: font data to be inserted; * map_to: original font data if this is a mapping; * Returns: * updated cache entry; *****/ static fontCacheEntry* insertFont(fontCacheEntry *entry, String name, XmHTMLfont *font, XmHTMLfont *map_to) { if(entry == NULL) { /* allocate new font entry */ entry = (fontCacheEntry*)malloc(sizeof(fontCacheEntry)); entry->name = font->font_name; entry->font = font; entry->is_map = map_to != NULL; entry->map_to = map_to; entry->left = (fontCacheEntry*)NULL; entry->right = (fontCacheEntry*)NULL; } else { int ret_val = strncmp(name, entry->name, strlen(name)); /* duplicate font entries can exist, so check the style as well */ if(ret_val == 0 && entry->font->style == font->style) return(entry); if(ret_val < 0) entry->left = insertFont(entry->left, name, font, map_to); else entry->right = insertFont(entry->right, name, font, map_to); } return(entry); } /***** * Name: getFont * Return Type: XmHTMLfont * Description: looks for a font in the fontcache; * In: * entry: current font cache; * name: name of font to locat; * Returns: * a valid font if name is found, NULL if not found. *****/ static XmHTMLfont* getFont(fontCacheEntry *entry, String name, Byte style) { if(entry != NULL) { int ret_val = strncmp(name, entry->name, strlen(name)); curr_cache->nlookups++; /***** * We want the styles to match as well, _XmHTMLloadQueryFont is * a bit too smart sometimes. *****/ if(ret_val == 0 && style == entry->font->style) { _XmHTMLDebug(8,("already cached.\n")); if(entry->map_to) { _XmHTMLDebug(8, ("\t(mapped to %s)\n", entry->map_to->font_name)); return(entry->map_to); } else return(entry->font); } if(ret_val < 0) return(getFont(entry->left, name, style)); else return(getFont(entry->right, name, style)); } return((XmHTMLfont*)NULL); } /***** * Name: mapFont * Return Type: XmHTMLfont* * Description: creates a font mapping; * In: * font: data to which ``name'' is mapped; * name: name of font * Returns: * a new font entry; *****/ static XmHTMLfont* mapFont(XmHTMLfont *font, String name) { static XmHTMLfont *map; map = (XmHTMLfont*)malloc(sizeof(XmHTMLfont)); /* copy everything */ memcpy(map, font, sizeof(XmHTMLfont)); /* override name */ map->font_name = strdup(name); return(map); } /***** * Name: allocFont * Return Type: XmHTMLfont * Description: allocates a new font entry and retrieves all required * font properties; * In: * xfont: ptr to an X font; * name: name of this font; * family: family to which this font belongs; * style: style of this font, see the FONT_ defines in XmHTMLP.h * Returns: * a new font entry; *****/ static XmHTMLfont* allocFont(TFontStruct *xfont, String name, String family, Byte style) { static XmHTMLfont *font; unsigned long value = 0; XFontStruct *xfs = NULL; int mb_ascent, mb_descent; #ifdef WITH_MOTIF xfs = xfont; #else switch (xfont->type) { case GDK_FONT_FONT: xfs = (XFontStruct *)(((GdkFontPrivate *)xfont)->xfont); break; case GDK_FONT_FONTSET: xfs = NULL; break; default: printf ("Passed a non-font to allocFont!\n"); exit (1); } #endif font = (XmHTMLfont*)malloc(sizeof(XmHTMLfont)); /* default items */ font->xfont = xfont; font->font_name = strdup(name); font->font_family = strdup(family); font->style = style; /* size of largest character */ if (xfs) { mb_ascent = xfs->max_bounds.ascent; mb_descent = xfs->max_bounds.descent; font->width = xfs->max_bounds.width; font->lbearing = xfs->max_bounds.lbearing; font->rbearing = xfs->max_bounds.rbearing; } #ifndef WITH_MOTIF else { XFontStruct **fsl; char **names; int n, i; XFontSet fset; fset = (XFontSet)(((GdkFontPrivate *)xfont)->xfont); n = XFontsOfFontSet(fset, &fsl, &names); font->width = font->rbearing = font->lbearing = 1; mb_ascent = mb_descent = 0; for (i = 0; i < n; i++) { XCharStruct mb; mb = fsl[i]->max_bounds; font->lbearing = MIN(font->lbearing, mb.lbearing); font->rbearing = MAX(font->rbearing, mb.rbearing); font->width = MAX(font->width, mb.width); mb_ascent = MAX(mb.ascent, mb_ascent); mb_descent = MAX(mb.descent, mb_descent); } } #endif font->height = mb_ascent + mb_descent; /* suggested lineheight */ font->lineheight = xfont->ascent + xfont->descent; /* now go get a bunch of properties */ /* normal interword spacing */ if (xfs && XGetFontProperty(xfs, XA_NORM_SPACE, &value) == True) font->isp = (Cardinal)value; else { /* use width of a single space */ #ifdef WITH_MOTIF int dir, ascent, descent; XCharStruct sp; XTextExtents(XF, " ", 1, &dir, &ascent, &descent, &sp); font->isp = sp.width; #else font->isp = gdk_char_width(xfont, ' '); #endif } /* additional end-of-line spacing */ if (xfs && XGetFontProperty(xfs, XA_END_SPACE, &value) == True) font->eol_sp = (Cardinal)value; else font->eol_sp = 0; /* superscript x-offset */ font->sup_xoffset = 0; /* superscript y-offset */ if (xfs && XGetFontProperty(xfs, XA_END_SPACE, &value) == True) font->sup_yoffset = (int)value; else font->sup_yoffset = (int)(mb_ascent * -.4); /* subscript x-offset */ if (xfs && XGetFontProperty(xfs, XA_SUBSCRIPT_X, &value) == True) font->sub_xoffset = (int)value; else font->sub_xoffset = 0; /* subscript y-offset */ if (xfs && XGetFontProperty(xfs, XA_SUBSCRIPT_Y, &value) == True) font->sub_yoffset = (int)value; else font->sub_yoffset = (int)(mb_descent * .8); /* underline offset */ if (xfs && XGetFontProperty(xfs, XA_UNDERLINE_POSITION, &value) == True) font->ul_offset = (int)value; else font->ul_offset = (int)(mb_descent-2); /* underline thickness */ if (xfs && XGetFontProperty(xfs, XA_UNDERLINE_THICKNESS, &value) == True) font->ul_thickness = (Cardinal)value; else font->ul_thickness = (Cardinal)1; /* strikeout offset */ if(xfs && XGetFontProperty(xfs, XA_STRIKEOUT_ASCENT, &value) == True) font->st_offset = (int)value; else font->st_offset = (int)(0.5*(mb_ascent))+3; /* strikeout descent */ if(xfs && XGetFontProperty(xfs, XA_STRIKEOUT_DESCENT, &value) == True) font->st_thickness = font->st_offset + (Cardinal)value; else font->st_thickness = 1; return(font); } /***** * Name: loadAndCacheFont * Return Type: XmHTMLfont * Description: retrieves a font from the cache or loads one when it isn't * already available. * In: * w: XmHTMLWidget id; * name: name of font to be loaded; * family: family to which this font belongs; * style: style of this font, see the FONT_ defines in XmHTMLP.h; * Returns: * a valid font if the font was loaded successfully, NULL if not (which * should never happen); *****/ static XmHTMLfont* loadAndCacheFont(TWidget w, String name, String family, Byte style) { XmHTMLfont *font; TFontStruct *xfont; _XmHTMLDebug(8,( "fonts.c: loadAndCacheFont: checking fontcache for\n%s: ", name)); curr_cache->requests++; /* check if we have loaded this font before */ if((font = getFont(curr_cache->cache, name, style)) != NULL) { curr_cache->hits++; return(font); } curr_cache->misses++; _XmHTMLDebug(8,( "not cached,\ntrying to load...")); #ifdef WITH_MOTIF /* A new font, try to load it */ xfont = XLoadQueryFont(XtDisplay(w), name); #else if (strchr(name, ',')) { _XmHTMLDebug(8, ( "a fontset..." )); xfont = gdk_fontset_load (name); } else { _XmHTMLDebug(8, ( "a font..." )); xfont = gdk_font_load (name); } #endif /* store it if successfull */ if(xfont != NULL) { _XmHTMLDebug(8,( "found.\n")); /* get a new fontentry */ font = allocFont(xfont, name, family, style); /* store in the cache */ curr_cache->nentries++; curr_cache->cache = insertFont(curr_cache->cache, name, font, NULL); /* return the new font */ return(font); } #ifdef DEBUG else _XmHTMLDebug(8,( "failed.\n")); #endif return((XmHTMLfont*)NULL); } /***** * Name: _XmHTMLloadQueryFont * Return Type: XFontStruct* * Description: loads a font from the given family in given size, weight and * slant. Loaded fonts are cached to minimize the overhead spent * in XLoadQueryFont(). * In: * w: Widget for which this font is to be loaded. * name: XmHTML fontFamily spec. Only used when family is NULL. * family: font family name of the font to load. When non-null this * contains the typeface of the font, e.i.: helvetica, symbol, * etc... * ptsz: size of font to load, in tenths of a point * style: style of this font. * *loaded: indicates whether the requested font was loaded or the current * font was returned. When loaded is initially True, a warning * message is displayed if the font can't be loaded. * Returns: * A XFontStruct* for the font in the requested family/size and loaded * set to True or the current font and loaded set to False. * Note: * This routine was based on the LoadQueryScalableFont() routine found in * O'Reilly's Xlib Programming Manual by Adrian Nye, but that's no longer * recognizable... * * This routine goes through *GREAT* lengths to find the requested font, and * it will almost never fail (unless the XmNfontFamily/XmNcharset resources * form an invalid pair, but then XmHTML will give up on startup immediatly). *****/ XmHTMLfont* _XmHTMLloadQueryFont(TWidget w, String name, String family, int ptsz, Byte style, Boolean *loaded) { String weight, slant, fontname = NULL, charset = NULL; XmHTMLWidget html = (XmHTMLWidget)w; char fontfamily[1024], font_mapping[1024]; XmHTMLfont *font = NULL; font_mapping[0] = '\0'; /***** * Okay, now we are going to try and load a font. * Check weight & slant styles. The order in which they are treated * is important: bold overrides any FONT_MEDIUM settings and italic * overrides any FONT_REGULAR settings. * First attempts are all made with given charset. If no font is found * we wildcard it and try all over again. *****/ if(style & FONT_BOLD) { int num_total = 0; while(num_total != 2 && font == NULL) { int num_outer = 0; charset = (num_total == 0 ? html->html.charset : "*-*"); num_total++; while(num_outer != 4 && font == NULL) { int num_inner = 0; /* weight can vary between bold, demibold, medium, regular */ switch(num_outer) { case 0 : weight = "bold" ; break; case 1 : weight = "demibold"; break; case 2 : weight = "medium" ; break; default: weight = "regular" ; break; } num_outer++; /* slant can vary between italic, oblique and roman */ if(style & FONT_ITALIC) { while(num_inner < 3 && font == NULL) { switch(num_inner) { case 0 : slant = "i"; break; /* italic */ case 1 : slant = "o"; break; /* oblique */ default: slant = "r"; break; /* roman */ } num_inner++; fontname = makeFontName(name, family ? "*" : NULL, family, weight, slant, ptsz, charset, fontfamily); font = loadAndCacheFont(w, fontname, fontfamily, style); if(font == NULL && font_mapping[0] == '\0') { strcpy(font_mapping, fontname); font_mapping[strlen(fontname)] = '\0'; } } } else { slant = "r"; /* roman */ fontname = makeFontName(name, family ? "*" : NULL, family, weight, slant, ptsz, charset, fontfamily); font = loadAndCacheFont(w, fontname, fontfamily, style); if(font == NULL && font_mapping[0] == '\0') { strcpy(font_mapping, fontname); font_mapping[strlen(fontname)] = '\0'; } } } } } /* regular font style */ else { int num_total = 0; while(num_total != 2 && font == NULL) { int num_outer = 0; charset = (num_total == 0 ? html->html.charset : "*-*"); num_total++; while(num_outer != 2 && font == NULL) { int num_inner = 0; /* weight can vary between medium and regular */ if(num_outer == 0) weight = "medium"; else weight = "regular"; num_outer++; /* slant can vary between italic, oblique and roman */ if(style & FONT_ITALIC) { while(num_inner < 3 && font == NULL) { switch(num_inner) { case 0 : slant = "i"; break; /* italic */ case 1 : slant = "o"; break; /* oblique */ default: slant = "r"; break; /* roman */ } num_inner++; fontname = makeFontName(name, family ? "*" : NULL, family, weight, slant, ptsz, charset, fontfamily); font = loadAndCacheFont(w, fontname, fontfamily, style); if(font == NULL && font_mapping[0] == '\0') { strcpy(font_mapping, fontname); font_mapping[strlen(fontname)] = '\0'; } } } else { slant = "r"; /* roman */ fontname = makeFontName(name, family ? "*" : NULL, family, weight, slant, ptsz, charset, fontfamily); font = loadAndCacheFont(w, fontname, fontfamily, style); if(font == NULL && font_mapping[0] == '\0') { strcpy(font_mapping, fontname); font_mapping[strlen(fontname)] = '\0'; } } } } } if(font) { /***** * If the requested font was mapped to another font, store the * mapping as well since it will be the same for all subsequent * requests for this font. * loaded is False only when we are just attempting to load a font * and we want this thing to fail, so we sure as hell don't want a * default mapping then. *****/ if(font_mapping[0] != '\0' && *loaded == False) { XmHTMLfont *map = mapFont(font, font_mapping); curr_cache->nentries++; curr_cache->nmaps++; curr_cache->cache = insertFont(curr_cache->cache, font_mapping, map, font); } /* we have the font */ *loaded = True; /* return the new font */ return(font); } /* we don't have the font */ if(*loaded) { _XmHTMLWarning(__WFUNC__(w, "_XmHTMLloadQueryFont"), "Failed to load " "font %s\n Font probably doesn't exist. Ignored.", fontname); } *loaded = False; /* fix 02/03/07-01, dp */ return(curr_cache->default_font); } /***** * Name: _XmHTMLaddFontMapping * Return Type: void * Description: add a fontmapping for the the given font. The name of the * font to be mapped is created in such a way that it will * be the very first match when the _XmHTMLloadQueryFont routine * is called. It's primary use is to reduce loading times when * switching between documents (we already know which font we will * get). * In: * font: actual font. * Returns: * nothing. *****/ void _XmHTMLaddFontMapping(XmHTMLWidget html, String name, String family, int ptsz, Byte style, XmHTMLfont *font) { String fontname = NULL; char fontfamily[1024]; XmHTMLfont *map; /***** * Create an XLFD that will match on the first run of _XmHTMLloadQueryFont. * !!!INCREDIBLE SPEEDUP!!! *****/ fontname = makeFontName(name, family ? "*" : NULL, family, style & FONT_BOLD ? "bold" : "medium", style & FONT_ITALIC ? "i" : "r", ptsz, html->html.charset, fontfamily); /* add a mapping */ map = mapFont(font, fontname); curr_cache->nentries++; curr_cache->nmaps++; curr_cache->cache = insertFont(curr_cache->cache, fontname, map, font); } /***** * Name: freeFontEntries * Return Type: void * Description: releases all fonts in the given cache; * In: * dpy: display on which the fonts were allocated; * fonts: cache to be freed; * Returns: * nothing. *****/ static void freeFontEntries(Display *dpy, fontCacheEntry *fonts) { if(fonts) { freeFontEntries(dpy, fonts->left); freeFontEntries(dpy, fonts->right); _XmHTMLDebug(8, ("fonts.c: freeFontEntries, releasing font\n\t%s\n", fonts->font->font_name)); /* only free the font if it isn't a mapped one */ if(!fonts->is_map) { Toolkit_Free_Font(dpy, fonts->font->xfont); free(fonts->font->font_family); } /* free all allocated strings */ free(fonts->font->font_name); /* free XmHTMLfont entry */ free(fonts->font); /* free cache entry */ free(fonts); } } /***** * Name: initializeFontSizeLists * Return Type: void * Description: fills all arrays of font sizes. * In: * w: widget containing font size specs. * Returns: * nothing, but the font lists are updated to reflect the new sizes. * * The static size lists can cause unexpected results when multiple instances * of the Widget with different sizelists are being used. *****/ static void initializeFontSizeLists(XmHTMLWidget html) { char *chPtr; char size_list[64]; int i; Boolean ok; char *tokp; _XmHTMLDebug(8,( "fonts.c: initializeFontSizeLists Start\n")); /*** Scalable font size list ***/ /* copy name, it gets destroyed */ (void)memset(&size_list, 0, 64); strncpy(size_list, html->html.font_sizes, 63); /* This list has 8 elements */ for(chPtr = strtok_r(size_list, ",", &tokp), i = 0; i < 8 && chPtr != NULL; chPtr = strtok_r(NULL, ",", &tokp), i++) { if((xmhtml_fn_sizes[i] = 10*atoi(chPtr)) == 0) xmhtml_fn_sizes[i] = def_fn_sizes[i]; } /* fill up list if it is not complete */ if(i != 8) { for(; i < 8; i++) xmhtml_fn_sizes[i] = def_fn_sizes[i]; } #ifdef DEBUG _XmHTMLDebug(8, ( "fonts.c: initializeFontSizeLists, scalable font " "size list:\n")); for(i = 0 ; i < 8 ; i++) _XmHTMLDebug(8,("%i ", xmhtml_fn_sizes[i])); #endif /*** Fixed font size list ***/ /* copy name, it gets destroyed */ (void)memset(&size_list, 0, 64); strncpy(size_list, html->html.font_sizes_fixed, 63); /* This list has 2 elements */ for(chPtr = strtok_r(size_list, ",", &tokp), i = 0; i < 2 && chPtr != NULL; chPtr = strtok_r(NULL, ",", &tokp), i++) { if((xmhtml_fn_fixed_sizes[i] = 10*atoi(chPtr)) == 0) xmhtml_fn_fixed_sizes[i] = def_fn_fixed_sizes[i]; } /* fill up list if it is not complete */ if(i != 2) { for(; i < 2; i++) xmhtml_fn_fixed_sizes[i] = def_fn_fixed_sizes[i]; } _XmHTMLDebug(8, ( "\nfonts.c: initializeFontSizeLists, fixed font " "size list:\n")); _XmHTMLDebug(8, ( "%i %i", xmhtml_fn_fixed_sizes[0], xmhtml_fn_fixed_sizes[1])); /* list of possible font de/increments using the element */ xmhtml_basefont_sizes[0] = xmhtml_fn_sizes[1]; /* sub/superscript size */ xmhtml_basefont_sizes[1] = xmhtml_fn_sizes[7]; /* H6 size */ xmhtml_basefont_sizes[2] = xmhtml_fn_sizes[6]; /* H5 size */ xmhtml_basefont_sizes[3] = xmhtml_fn_sizes[5]; /* H4 size */ xmhtml_basefont_sizes[4] = xmhtml_fn_sizes[4]; /* H3 size (def font size)*/ xmhtml_basefont_sizes[5] = xmhtml_fn_sizes[3]; /* H2 size */ xmhtml_basefont_sizes[6] = xmhtml_fn_sizes[2]; /* H1 size */ #ifdef DEBUG _XmHTMLDebug(8, ( "\nfonts.c: initializeFontSizeLists, fallback " "font size list:\n")); for(i = 0 ; i < 7 ; i++) _XmHTMLDebug(8,( "%i ", xmhtml_basefont_sizes[i])); _XmHTMLDebug(8, ("\n")); #endif /* First try to load the default font as specified by the resources */ ok = False; html->html.default_font = _XmHTMLloadQueryFont((TWidget)html, html->html.font_family, NULL, xmhtml_fn_sizes[0], FONT_SCALABLE|FONT_REGULAR|FONT_MEDIUM, &ok); /***** * We can't load the default font, try again with a wildcarded family. * This time die if it fails *****/ if(html->html.default_font == NULL) { _XmHTMLWarning(__WFUNC__(html, "initializeFontSizeLists"), "Failed to load default font %s\n Guessing for a default font.", html->html.font_family); ok = True; html->html.default_font = _XmHTMLloadQueryFont((TWidget)html, html->html.font_family, "*", xmhtml_fn_sizes[0], FONT_SCALABLE|FONT_REGULAR|FONT_MEDIUM, &ok); /* too bad, we absolutely need it */ if(ok == False) { /* die */ _XmHTMLError(__WFUNC__(html,"initializeFontSizeLists"), "Failed to find a default font for %s\n Check previous " "messages and adjust default font", html->html.font_family); } } _XmHTMLDebug(8,( "\nfonts.c: initializeFontSizeLists end.\n")); } /***** * Name: _XmHTMLSelectFontCache * Return Type: XmHTMLfont* * Description: selects a cache according to the display a widget is * being displayed on (or creates one if it isn't present yet) * In: * html: XmHTMLWidget id; * reset: hard reset flag (used by SetValues when any of the font * resources changes); * Returns: * the id of the default font for this cache; *****/ XmHTMLfont* _XmHTMLSelectFontCache(XmHTMLWidget html, Boolean reset) { Display *dpy = Toolkit_Display((TWidget)html); fontCache *cache; _XmHTMLDebug(8, ("fonts.c: _XmHTMLSelectFontCache start\n")); for(cache = master_cache; cache != NULL && cache->dpy != dpy; cache = cache->next); if(cache == NULL) { int screen = DefaultScreen(dpy); cache = (fontCache*)malloc(sizeof(fontCache)); cache->dpy = dpy; cache->cache = (fontCacheEntry*)NULL; cache->default_font = (XmHTMLfont*)NULL; cache->nwidgets = 1; cache->widgets = (TWidgetList)malloc(sizeof(TWidget)); cache->widgets[0] = (TWidget)html; cache->next = (fontCache*)NULL; /* obtain screen resolution in dpi */ cache->res_x = DisplayWidth(dpy, screen)/(DisplayWidthMM(dpy,screen)/25.4); cache->res_y = DisplayHeight(dpy, screen)/(DisplayHeightMM(dpy,screen)/25.4); /* adjust resolutions */ cache->res_x = (cache->res_x < 87 ? 75 : 100); cache->res_y = (cache->res_y < 87 ? 75 : 100); /* make sure we have the same resolution in both directions */ if(cache->res_x != cache->res_y) { if(cache->res_x > cache->res_y) cache->res_y = cache->res_x; else cache->res_x = cache->res_y; } cache->nentries = 0; cache->nmaps = 0; cache->nlookups = 0; cache->requests = 0; cache->hits = 0; cache->misses = 0; if(master_cache) { fontCache *tmp; for(tmp = master_cache; tmp->next != NULL; tmp = tmp->next); tmp->next = cache; } else master_cache = cache; _XmHTMLDebug(8, ("fonts.c: _XmHTMLSelectFontCache, created first " "entry.\n")); } else { int i; /* see if we have got a reference for this widget */ for(i = 0; i < cache->nwidgets && cache->widgets[i] != (TWidget)html; i++); if(i == cache->nwidgets) { _XmHTMLDebug(8, ("fonts.c: _XmHTMLSelectFontCache, adding " "reference entry for widget %s.\n", Toolkit_Widget_Name((TWidget)html))); cache->widgets = (TWidgetList)realloc(cache->widgets, (cache->nwidgets+1)*sizeof(TWidget)); cache->widgets[cache->nwidgets++] = (TWidget)html; } } /***** * Only initialize font lists if the cache has changed, when we * are forced to do a reset or we haven't got a default font. *****/ if(curr_cache != cache || reset || html->html.default_font == NULL) { curr_cache = cache; initializeFontSizeLists(html); } curr_cache->default_font = html->html.default_font; return(curr_cache->default_font); } /***** * Name: _XmHTMLLoadFont * Return Type: XmHTMLfont* * Description: loads a new font, with the style determined by the current * font: if current font is bold, and new is italic then a * bold-italic font will be returned. * In: * w: Widget for which to load a font * font_id: id describing type of font to load. * size: size of font to load. Only used for HT_FONT. * curr_font: current font, required for propagating font style info. * Returns: * the loaded font. *****/ XmHTMLfont* _XmHTMLLoadFont(XmHTMLWidget html, htmlEnum font_id, int size, XmHTMLfont *curr_font) { XmHTMLfont *new_font = NULL; String family; int ptsz; Byte new_style = (Byte)0, font_style; Boolean ok = True; /* enable font warnings */ /* curr_font *must* always have a value as it references a cached font */ my_assert(curr_font != NULL); /* pick up style of the current font */ font_style = curr_font->style; _XmHTMLDebug(8,("_XmHTMLLoadFont: current font is %s %s %s.\n", (font_style & FONT_FIXED ? "fixed" : "scalable"), (font_style & FONT_BOLD ? "bold" : "medium"), (font_style & FONT_ITALIC ? "italic" : "regular"))); /* See if we need to proceed with bold font */ if(font_style & FONT_BOLD) new_style = FONT_BOLD; else new_style &= ~FONT_BOLD; /* See if we need to proceed with italic font */ if(font_style & FONT_ITALIC) new_style |= FONT_ITALIC; else new_style &= ~FONT_ITALIC; /* See if we need to proceed with a fixed font */ if(font_style & FONT_FIXED) { new_style |= FONT_FIXED; family = html->html.font_family_fixed; ptsz = xmhtml_fn_fixed_sizes[0]; } else { new_style &= ~FONT_FIXED; family = curr_font->font_family; ptsz = xmhtml_fn_sizes[0]; } _XmHTMLDebug(8,("_XmHTMLLoadFont: next font is %s %s %s (inherited).\n", (new_style & FONT_FIXED ? "fixed" : "scalable"), (new_style & FONT_BOLD ? "bold" : "medium"), (new_style & FONT_ITALIC ? "italic" : "regular"))); switch(font_id) { case HT_CITE: case HT_I: case HT_EM: case HT_DFN: case HT_ADDRESS: new_font = _XmHTMLloadQueryFont((TWidget)html, family, NULL, xmhtml_basefont_sizes[size-1], new_style|FONT_ITALIC, &ok); break; case HT_STRONG: case HT_B: case HT_CAPTION: new_font = _XmHTMLloadQueryFont((TWidget)html, family, NULL, xmhtml_basefont_sizes[size-1], new_style | FONT_BOLD, &ok); break; /***** * Fixed fonts always use the font specified by the value of the * fontFamilyFixed resource. *****/ case HT_SAMP: case HT_TT: case HT_VAR: case HT_CODE: case HT_KBD: case HT_PRE: /* fix 01/20/97-03, kdh */ new_font = _XmHTMLloadQueryFont((TWidget)html, html->html.font_family_fixed, NULL, xmhtml_fn_fixed_sizes[0], new_style |FONT_FIXED, &ok); break; /* The element is useable in *every* state */ case HT_FONT: new_font = _XmHTMLloadQueryFont((TWidget)html, family, NULL, size, new_style, &ok); break; /***** * Since HTML Headings may not occur inside a declaration, * they *must* use the specified document font, and not derive their * true font from the current font. *****/ case HT_H1: new_font = _XmHTMLloadQueryFont((TWidget)html, html->html.font_family, NULL, xmhtml_fn_sizes[2], FONT_SCALABLE|FONT_BOLD, &ok); break; case HT_H2: new_font = _XmHTMLloadQueryFont((TWidget)html, html->html.font_family, NULL, xmhtml_fn_sizes[3], FONT_SCALABLE|FONT_BOLD, &ok); break; case HT_H3: new_font = _XmHTMLloadQueryFont((TWidget)html, html->html.font_family, NULL, xmhtml_fn_sizes[4], FONT_SCALABLE|FONT_BOLD, &ok); break; case HT_H4: new_font = _XmHTMLloadQueryFont((TWidget)html, html->html.font_family, NULL, xmhtml_fn_sizes[5], FONT_SCALABLE|FONT_BOLD, &ok); break; case HT_H5: new_font = _XmHTMLloadQueryFont((TWidget)html, html->html.font_family, NULL, xmhtml_fn_sizes[6], FONT_SCALABLE|FONT_BOLD, &ok); break; case HT_H6: new_font = _XmHTMLloadQueryFont((TWidget)html, html->html.font_family, NULL, xmhtml_fn_sizes[7], FONT_SCALABLE|FONT_BOLD, &ok); break; /* should never be reached */ default: #ifdef PEDANTIC _XmHTMLWarning(__WFUNC__(html, "_XmHTMLLoadFont"), "Unknown font switch. Using default font."); #endif /* PEDANTIC */ /* this will always succeed */ ok = False; new_font = _XmHTMLloadQueryFont((TWidget)html, family, NULL, ptsz, FONT_SCALABLE|FONT_REGULAR|FONT_MEDIUM, &ok); break; } return(new_font); } /***** * Name: _XmHTMLLoadFontWithFace * Return Type: XmHTMLfont* * Description: load a new font with given pixelsize and face. * Style is determined by the current font: if current font * is bold, and new is italic then a bold-italic font will be * returned. * In: * w: Widget for which to load a font * size: size of font to load. Only used for HT_FONT. * face: a comma separated list of font faces to use, contents are * destroyed when this function returns. * curr_font: current font, required for propagating font style info. * Returns: * A new font with a face found in the list of faces given upon success * or the default font on failure. *****/ XmHTMLfont* _XmHTMLLoadFontWithFace(XmHTMLWidget html, int size, String face, XmHTMLfont *curr_font) { XmHTMLfont *new_font = NULL; String chPtr, family, all_faces, first_face = NULL; Byte new_style = (Byte)0, font_style; int try; char *tokp; /* curr_font *must* always have a value as it references a cached font */ my_assert(curr_font != NULL); /* pick up style of the current font */ font_style = curr_font->style; /* See if we need to proceed with bold font */ if(font_style & FONT_BOLD) new_style = FONT_BOLD; else new_style &= ~FONT_BOLD; /* See if we need to proceed with italic font */ if(font_style & FONT_ITALIC) new_style |= FONT_ITALIC; else new_style &= ~FONT_ITALIC; /***** * See if we need to proceed with a fixed font, only used to determine * initial font family. *****/ if(font_style & FONT_FIXED) { new_style |= FONT_FIXED; family = html->html.font_family_fixed; } else { new_style &= ~FONT_FIXED; family = html->html.font_family; } /* we must have a ``,'' or strtok will fail */ if((strstr(face, ",")) == NULL) { all_faces = (String)malloc(strlen(face) + 2); strcpy(all_faces, face); strcat(all_faces, ",\0"); } else all_faces = strdup(face); /* walk all possible spaces */ try = 0; for(chPtr = strtok_r(all_faces, ",", &tokp); chPtr != NULL; chPtr = strtok_r(NULL, ",", &tokp)) { Boolean ok = False; try++; /* skip any leading spaces */ while(isspace(*chPtr)) chPtr++; _XmHTMLDebug(8, ("format.c: _XmHTMLLoadFontWithFace, trying with " "face %s\n", chPtr)); /***** * Disable font not found warning message, we are trying to find * a font of which we don't know if it exists. *****/ ok = False; new_font = _XmHTMLloadQueryFont((TWidget)html, family, chPtr, size, new_style, &ok); if(new_font && ok) { _XmHTMLDebug(8, ("format.c: _XmHTMLLoadFontWithFace, font " "loaded.\n")); break; } if(try == 1) first_face = strdup(chPtr); } free(all_faces); /***** * hmm, the first font in this face specification didn't yield a valid * font. To speed up things considerably, we add a font mapping for the * first face in the list of given spaces. There's no sense in doing this * when there is only one face specified as this will always get us the * default font. We only add a mapping if the name of the returned font * contains at least one of the allowed faces. Not doing this check would * ignore face specs which do have a face we know. We also want the font * styles to match as well. * BTW: this is a tremendous speedup!!! *****/ if(first_face) { /***** * Only add a mapping if the returned name contains one of the allowed * faces. No need to check for the presence of a comma: we only take * lists that have multiple face specifications. *****/ if(try > 1) { /***** * Walk all possible faces. Nukes the face array but that's not * bad as we are the only ones using it. *****/ for(chPtr = strtok_r(face, ",", &tokp); chPtr != NULL; chPtr = strtok_r(NULL, ",", &tokp)) { /* skip any leading spaces */ while(isspace(*chPtr)) chPtr++; /* caseless 'cause fontnames ignore case */ if(my_strcasestr(new_font->font_name, chPtr) && new_font->style == new_style) { _XmHTMLaddFontMapping(html, family, first_face, size, new_style, new_font); break; } } } free(first_face); } return(new_font); } /***** * Name: _XmHTMLUnloadFonts * Return Type: void * Description: removes a widget from the widget list of a display-bound * font cache. When the reference count of this cache reaches * zero, the cache is released; * In: * html: XmHTMLWidget id; * Returns: * nothing. *****/ void _XmHTMLUnloadFonts(XmHTMLWidget html) { Display *dpy = Toolkit_Display((TWidget)html); fontCache *cache; int i; /* get current font cache */ for(cache = master_cache; cache != NULL && cache->dpy != dpy; cache = cache->next); if(cache == NULL) { _XmHTMLWarning(__WFUNC__(html, "_XmHTMLUnloadFonts"), "Font cache corrupted: could not find an entry for this display!"); return; } for(i = 0; i < cache->nwidgets && cache->widgets[i] != (TWidget)html; i++); if(i == cache->nwidgets) { _XmHTMLWarning(__WFUNC__(html, "_XmHTMLUnloadFonts"), "Font cache corrupted: could find not an entry for this widget!"); return; } _XmHTMLDebug(8,( "\nfonts.c: _XmHTMLUnloadFonts, removing reference for " "widget %s.\n", Toolkit_Widget_Name((TWidget)html))); /* invalidate current cache? */ if(cache == curr_cache) curr_cache = (fontCache*)NULL; /* remove this widget */ cache->widgets[i] = (TWidget)NULL; for(; i < cache->nwidgets - 1; i++) cache->widgets[i] = cache->widgets[i+1]; cache->nwidgets--; /* if this was the last widget, free it */ if(cache->nwidgets == 0) { fontCache *tmp; _XmHTMLDebug(8, ("fonts.c: _XmHTMLUnloadFonts, releasing font " "cache.\n")); /* special case, first entry is to be freed */ if(cache == master_cache) master_cache = cache->next; else { for(tmp = master_cache; tmp->next != cache; tmp = tmp->next); /* connect next entry */ tmp->next = cache->next; } /* free the entire list of cached fonts */ freeFontEntries(dpy, cache->cache); free(cache->widgets); free(cache); } #ifdef DEBUG else { _XmHTMLDebug(8, ("fonts.c: _XmHTMLUnloadFonts, cache still " "referenced by the following widgets:\n")); for(i = 0; i < cache->nwidgets; i++) _XmHTMLDebug(8, ("\t%s\n", Toolkit_Widget_Name(cache->widgets[i]))); } #endif } static void fillCacheInfo(fontCacheEntry *entry, XmHTMLFontCacheInfo *info) { if(entry) { fillCacheInfo(entry->left, info); info->fonts[info->nentries] = entry->name; if(entry->is_map) info->mapping[info->nentries] = entry->map_to->font_name; else info->mapping[info->nentries] = NULL; info->nentries++; fillCacheInfo(entry->right, info); } } XmHTMLFontCacheInfo* XmHTMLGetFontCacheInfo(TWidget w) { Display *dpy; fontCache *cache; static XmHTMLFontCacheInfo *info; dpy = Toolkit_Display(w); /* sanity check */ if(dpy == NULL) { _XmHTMLWarning(__WFUNC__(NULL, "XmHTMLGetFontCacheInfo"), "XmHTMLGetFontCacheInfo: can't find display ()"); return(NULL); } /* pick up correct master cache */ for(cache = master_cache; cache != NULL && cache->dpy != dpy; cache = cache->next); /* not found */ if(cache == NULL) { _XmHTMLWarning(__WFUNC__(NULL, "XmHTMLGetFontCacheInfo"), "XmHTMLGetFontCacheInfo: can't find info for display %s.", DisplayString(dpy)); return(NULL); } info = (XmHTMLFontCacheInfo*)malloc(sizeof(XmHTMLFontCacheInfo)); info->nentries = cache->nentries; /* no of cached fonts */ info->nmaps = cache->nmaps; /* no of mapped fonts */ info->nlookups = cache->nlookups; /* no of search actions */ info->nrequests = cache->requests; /* no of requests made */ info->hits = cache->hits; /* no of hits */ info->misses = cache->misses; /* no of font cache misses */ info->nwidgets = cache->nwidgets; /* no of widgets using this cache */ info->widgets = (WidgetList)cache->widgets; info->fonts = (String*)calloc(info->nentries, sizeof(String)); info->mapping = (String*)calloc(info->nentries, sizeof(String)); info->nentries = 0; fillCacheInfo(cache->cache, info); return(info); } void XmHTMLFreeFontCacheInfo(XmHTMLFontCacheInfo *info) { if(info == NULL) return; free(info->fonts); free(info->mapping); free(info); }