/* font.h - multiple font handling Copyright (C) 1996-2000 Paul Sheer 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 "coolwidget.h" #include "coollocal.h" #include "aafont.h" #include "mad.h" #define TEST_FONT_STRING "The Quick Brown Fox Jumps Over The Lazy Dog" int option_no_font_set = 0; static struct font_stack { struct font_object *f; struct font_stack *next; } *font_stack = 0; /* only ImageStrings can bee anti-aliased */ int CImageTextWidth (const char *s, int l) { if (FONT_USE_FONT_SET) return XmbTextEscapement (current_font->font_set, s, l); if (FONT_ANTIALIASING) return XAaTextWidth (current_font->font_struct, (char *) s, l); return XTextWidth (current_font->font_struct, s, l); } static XChar2b *wchar_t_to_XChar2b (wchar_t * swc, int l) { XChar2b *p, *s; int r = l; p = s = malloc (l * sizeof (XChar2b)); while (r--) { p->byte1 = (unsigned char) (*swc >> 8); (p++)->byte2 = (unsigned char) (*(swc++) & 0xFF); } return s; } int CImageTextWidthWC (XChar2b * s, wchar_t * swc, int l) { if (FONT_USE_FONT_SET) return XwcTextEscapement (current_font->font_set, swc, l); if (!s) { int r; if (FONT_ANTIALIASING) r = XAaTextWidth16 (current_font->font_struct, s = wchar_t_to_XChar2b (swc, l), l); else r = XTextWidth16 (current_font->font_struct, s = wchar_t_to_XChar2b (swc, l), l); free (s); return r; } if (FONT_ANTIALIASING) return XAaTextWidth16 (current_font->font_struct, s, l); return XTextWidth16 (current_font->font_struct, s, l); } int CImageStringWidth (const char *s) { return CImageTextWidth (s, strlen (s)); } int CImageText (Window w, int x, int y, const char *s, int l) { if (FONT_USE_FONT_SET) { XmbDrawImageString (CDisplay, w, current_font->font_set, CGC, x, y, s, l); return XmbTextEscapement (current_font->font_set, s, l); } if (FONT_ANTIALIASING) return XAaDrawImageString (CDisplay, w, CGC, x, y, (char *) s, l); return XDrawImageString (CDisplay, w, CGC, x, y, s, l); } int CText (Window w, int x, int y, const char *s, int l) { if (FONT_USE_FONT_SET) { XmbDrawString (CDisplay, w, current_font->font_set, CGC, x, y, s, l); return XmbTextEscapement (current_font->font_set, s, l); } return XDrawString (CDisplay, w, CGC, x, y, s, l); } int CImageTextWC (Window w, int x, int y, XChar2b * s, wchar_t * swc, int l) { if (FONT_USE_FONT_SET) { XwcDrawImageString (CDisplay, w, current_font->font_set, CGC, x, y, swc, l); return XwcTextEscapement (current_font->font_set, swc, l); } if (!s) { int r; if (FONT_ANTIALIASING) r = XAaDrawImageString16 (CDisplay, w, CGC, x, y, s = wchar_t_to_XChar2b (swc, l), l); else r = XDrawImageString16 (CDisplay, w, CGC, x, y, s = wchar_t_to_XChar2b (swc, l), l); free (s); return r; } if (FONT_ANTIALIASING) return XAaDrawImageString16 (CDisplay, w, CGC, x, y, s, l); return XDrawImageString16 (CDisplay, w, CGC, x, y, s, l); } int CImageString (Window w, int x, int y, const char *s) { return CImageText (w, x, y, s, strlen (s)); } static struct font_object *find_font (char *name) { struct font_object *n; if (current_font) if (!strcmp (current_font->name, name)) return current_font; for (n = all_fonts; n; n = n->next) if (!strcmp (n->name, name)) return n; return 0; } /* * ● If the min_byte1 and max_byte1 members are both zero, * min_char_or_byte2 specifies the linear character * index corresponding to the first element of the * per_char array, and max_char_or_byte2 specifies the * linear character index of the last element. * * If either min_byte1 or max_byte1 are nonzero, both * min_char_or_byte2 and max_char_or_byte2 are less than * 256, and the 2-byte character index values corre­ * sponding to the per_char array element N (counting * from 0) are: * * byte1 = N/D + min_byte1 * byte2 = N%D + min_char_or_byte2 * * where: * * D = max_char_or_byte2 - min_char_or_byte2 + 1 * / = integer division * % = integer modulus */ /* returns width. the descent is only ever used for drawing an underbar. the ascent is only ever used in calculating FONT_PIX_PER_LINE */ static int get_wchar_dimension (wchar_t ch, int *height, int *ascent, int *ink_descent) { int width, direction; XRectangle ink; XRectangle logical; if (FONT_USE_FONT_SET) { width = XwcTextExtents (current_font->font_set, &ch, 1, &ink, &logical); if (height) *height = logical.height; if (ascent) *ascent = (-logical.y); if (ink_descent) *ink_descent = ink.height + ink.y; } else { XCharStruct c; XChar2b s; int ascent_, descent, w; s.byte2 = ch & 0xFF; s.byte1 = (ch >> 8) & 0xFF; XTextExtents16 (current_font->font_struct, &s, 1, &direction, &ascent_, &descent, &c); width = c.width; if (FONT_ANTIALIASING) { width = SHRINK_WIDTH (width); if (ascent) *ascent = ascent_ / 3; if (height) *height = SHRINK_HEIGHT (ascent_ + descent); } else { if (ascent) *ascent = ascent_; if (height) *height = ascent_ + descent; } w = current_font->font_struct->max_char_or_byte2 - current_font->font_struct->min_char_or_byte2 + 1; if (w == 1) w = 0; if (ink_descent) { if (s.byte2 >= current_font->font_struct->min_char_or_byte2 && s.byte2 <= current_font->font_struct->max_char_or_byte2 && s.byte1 >= current_font->font_struct->min_byte1 && s.byte1 <= current_font->font_struct->max_byte1) { if (current_font->font_struct->per_char) { *ink_descent = current_font->font_struct->per_char[ (s.byte2 - current_font->font_struct-> min_char_or_byte2) + w * (s.byte1 - current_font-> font_struct-> min_byte1)]. descent; } else { /* this happens when you try to scale a non-scalable font */ *ink_descent = current_font->font_struct->max_bounds.descent; } } else { *ink_descent = 0; } if (FONT_ANTIALIASING) *ink_descent = (*ink_descent + 3) / 3; } } return width; } /* returns width. the descent is only ever used for drawing an underbar. the ascent is only ever used in calculating FONT_PIX_PER_LINE */ static int get_string_dimensions (char *s, int n, int *height, int *ascent, int *ink_descent) { int width, direction; XRectangle ink; XRectangle logical; if (FONT_USE_FONT_SET) { width = XmbTextExtents (current_font->font_set, s, n, &ink, &logical); if (height) *height = logical.height; if (ascent) *ascent = (-logical.y); if (ink_descent) *ink_descent = ink.height + ink.y; } else { XCharStruct c; int ascent_, descent; XTextExtents (current_font->font_struct, s, n, &direction, &ascent_, &descent, &c); if (FONT_ANTIALIASING) { width = SHRINK_WIDTH (c.width); if (ascent) *ascent = ascent_ / 3; if (height) *height = SHRINK_HEIGHT (ascent_ + descent); } else { width = c.width; if (ascent) *ascent = ascent_; if (height) *height = ascent_ + descent; } if (ink_descent) { if (n == 1) { int i; i = (unsigned char) *s; if (i >= current_font->font_struct->min_char_or_byte2 && i <= current_font->font_struct->max_char_or_byte2) *ink_descent = current_font->font_struct->per_char[i - current_font-> font_struct->min_char_or_byte2].descent; else *ink_descent = 0; } else *ink_descent = descent; if (FONT_ANTIALIASING) *ink_descent = (*ink_descent + 3) / 3; } #if 0 width = CTextWidth (s, n); #endif } return width; } static int check_font_fixed (void) { int m; char *p; m = get_string_dimensions ("M", 1, 0, 0, 0); for (p = "!MI .1@~"; *p; p++) if (m != get_string_dimensions (p, 1, 0, 0, 0)) return 0; return m; } static void get_font_dimensions (void) { unsigned char *p, q[256]; int i; /* fill an array with graphical characters of the current locale */ for (p = q, i = 1; i < 255; i++) if (isgraph ((unsigned char) i)) *p++ = (unsigned char) i; *p = '\0'; /* get the dimensions of the complete character set */ get_string_dimensions ((char *) q, i, &FONT_HEIGHT, &FONT_ASCENT, &FONT_DESCENT); /* create an example sentance */ p = (unsigned char *) TEST_FONT_STRING; /* get the mean font width from the example sentance */ FONT_MEAN_WIDTH = get_string_dimensions ((char *) p, strlen ((char *) p), 0, 0, 0) / strlen ((char *) p); for (i = 255; i >= 0; i--) current_font->per_char_256[i] = FONT_PER_CHAR (i); } /* this tries to keep the array of cached of font widths as small ever needed - i.e. only enlarges on lookups */ static void _font_per_char (wchar_t c) { if (!current_font->per_char) { current_font->num_per_char = c + 1; current_font->per_char = CMalloc (current_font->num_per_char * sizeof (fontdim_t)); memset (current_font->per_char, 0xFF, current_font->num_per_char * sizeof (fontdim_t)); } else if (c >= current_font->num_per_char) { long l; fontdim_t *t; l = c + 256; t = CMalloc (l * sizeof (fontdim_t)); memcpy (t, current_font->per_char, current_font->num_per_char * sizeof (fontdim_t)); memset (t + current_font->num_per_char, 0xFF, (l - current_font->num_per_char) * sizeof (fontdim_t)); free (current_font->per_char); current_font->per_char = t; current_font->num_per_char = l; } if (current_font->per_char[c].width == 0xFF) { int d; current_font->per_char[c].width = get_wchar_dimension (c, 0, 0, &d); current_font->per_char[c].descent = (unsigned char) d; if (FIXED_FONT) if ((current_font->per_char[c].width != FIXED_FONT) && current_font->per_char[c].width) FIXED_FONT = 0; } } int font_per_char (wchar_t c) { if ((unsigned long) c > FONT_LAST_UNICHAR) return 0; _font_per_char (c); return current_font->per_char[c].width; } int font_per_char_descent (wchar_t c) { if ((unsigned long) c > FONT_LAST_UNICHAR) return 0; _font_per_char (c); return current_font->per_char[c].descent; } /* seems you cannot draw different fonts in the same GC. this is strange???, so we create a dummy GC for each font */ static Window get_dummy_gc (void) { static Window dummy_window = 0; XGCValues gcv; if (!dummy_window) { XSetWindowAttributes xswa; xswa.override_redirect = 1; dummy_window = XCreateWindow (CDisplay, CRoot, 0, 0, 1, 1, 0, CDepth, InputOutput, CVisual, CWOverrideRedirect, &xswa); } gcv.foreground = COLOR_BLACK; if (current_font->font_struct) { gcv.font = current_font->font_struct->fid; CGC = XCreateGC (CDisplay, dummy_window, GCForeground | GCFont, &gcv); } else { CGC = XCreateGC (CDisplay, dummy_window, GCForeground, &gcv); } return dummy_window; } static XFontSet get_font_set (char *name) { XFontSet fontset; char **a = 0; int b; if (!XSupportsLocale ()) #ifdef LC_CTYPE fprintf (stderr, "X does not support the locale: %s\n", setlocale (LC_CTYPE, 0)); #else fprintf (stderr, "X does not support locale's\n"); #endif fontset = XCreateFontSet (CDisplay, name, &a, &b, 0); if (!fontset) return 0; XFreeStringList (a); return fontset; } /* loads a font and sets the current font to it */ static struct font_object *load_font (char *name, char *xname) { int aa = 0; struct font_object *n; Window w; char *p; xname = (char *) strdup (xname); if ((p = strchr (xname, '/'))) { aa = 1; if (atoi (p + 1) != 3) { fprintf (stderr, _("%s: cannot load font\n\t%s\n/3 is allowed only.\n"), CAppName, xname); free (xname); return 0; } *p = '\0'; } n = CMalloc (sizeof (struct font_object)); memset (n, 0, sizeof (struct font_object)); if (!option_no_font_set) { n->font_set = get_font_set (xname); if (!n->font_set) fprintf (stderr, _("%s: display %s cannot load font\n\t%s\nas a font set - trying raw load.\n"), CAppName, DisplayString (CDisplay), xname); /* font set may have failed only because of an invalid locale, but we try load the font anyway */ } if (!n->font_set && !strchr (xname, ',')) { n->font_struct = XLoadQueryFont (CDisplay, xname); n->free_font_struct = 1; } if (!n->font_struct && !n->font_set) { fprintf (stderr, _("%s: display %s cannot load font\n\t%s\n"), CAppName, DisplayString (CDisplay), xname); free (n); free (xname); return 0; } n->next = all_fonts; current_font = all_fonts = n; n->name = (char *) strdup (name); /* font set may have worked, but if there is only one font, we might as well try use a font struct (which draws faster) */ if (current_font->font_set && !current_font->font_struct) { int i, num_fonts, single_font = 1; XFontStruct **font_struct_list_return = 0; char **font_name_list_return = 0; num_fonts = XFontsOfFontSet (current_font->font_set, &font_struct_list_return, &font_name_list_return); /* check if there is really only one font */ for (i = 1; i < num_fonts; i++) { if (strcmp (font_name_list_return[0], font_name_list_return[1])) { single_font = 0; break; } } if (single_font) { current_font->font_struct = XQueryFont (CDisplay, font_struct_list_return[0]->fid); current_font->free_font_struct = 0; } } if (current_font->font_struct) FONT_ANTIALIASING = aa; w = get_dummy_gc (); if (FONT_USE_FONT_SET) XmbDrawImageString (CDisplay, w, current_font->font_set, CGC, 0, 0, " AZ~", 4); else XDrawImageString (CDisplay, w, CGC, 0, 0, " AZ~", 4); FIXED_FONT = check_font_fixed (); get_font_dimensions (); /* -----> FIXME: yes, you actually HAVE to draw with the GC for the font to stick. can someone tell me what I am doing wrong? */ if (!current_font->font_set) /* font list like rxvt. FIXME: make rxvt and this see same font list */ current_font->font_set = get_font_set ("7x14,6x10,6x13,8x13,9x15"); free (xname); return current_font; } int CPushFont (char *name, char *xname) { struct font_stack *p; struct font_object *f; f = find_font (name); if (f) { f->ref++; } else { f = load_font (name, xname); if (!f) return 1; f->ref = 1; } p = CMalloc (sizeof (struct font_stack)); p->f = f; p->next = font_stack; font_stack = p; current_font = font_stack->f; return 0; } void CPopFont (void) { struct font_stack *p; if (!font_stack) { fprintf (stderr, "Huh\n?"); abort (); } if (!--font_stack->f->ref) { if (font_stack->f->gc) XFreeGC (CDisplay, font_stack->f->gc); if (font_stack->f->font_set) XFreeFontSet (CDisplay, font_stack->f->font_set); if (font_stack->f->font_struct) { XAaFree (font_stack->f->font_struct->fid); if (font_stack->f->free_font_struct) XFreeFont (CDisplay, font_stack->f->font_struct); else XFreeFontInfo (0, font_stack->f->font_struct, 0); } if (font_stack->f->per_char) free (font_stack->f->per_char); free (font_stack->f->name); free (font_stack->f); } p = font_stack->next; if (p) { current_font = p->f; } else { current_font = 0; } free (font_stack); font_stack = p; } void CFreeAllFonts (void) { int i = 0; while (font_stack) { CPopFont (); i++; } } int CIsFixedFont (void) { return FIXED_FONT; }