diff options
Diffstat (limited to 'unix/tkUnixFont.c')
-rw-r--r-- | unix/tkUnixFont.c | 979 |
1 files changed, 979 insertions, 0 deletions
diff --git a/unix/tkUnixFont.c b/unix/tkUnixFont.c new file mode 100644 index 0000000..d25f157 --- /dev/null +++ b/unix/tkUnixFont.c @@ -0,0 +1,979 @@ +/* + * tkUnixFont.c -- + * + * Contains the Unix implementation of the platform-independant + * font package interface. + * + * Copyright (c) 1996 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * SCCS: @(#) tkUnixFont.c 1.16 97/10/23 12:47:53 + */ + +#include "tkPort.h" +#include "tkInt.h" +#include "tkUnixInt.h" + +#include "tkFont.h" + +#ifndef ABS +#define ABS(n) (((n) < 0) ? -(n) : (n)) +#endif + +/* + * The following structure represents Unix's implementation of a font. + */ + +typedef struct UnixFont { + TkFont font; /* Stuff used by generic font package. Must + * be first in structure. */ + Display *display; /* The display to which font belongs. */ + XFontStruct *fontStructPtr; /* X information about font. */ + char types[256]; /* Array giving types of all characters in + * the font, used when displaying control + * characters. See below for definition. */ + int widths[256]; /* Array giving widths of all possible + * characters in the font. */ + int underlinePos; /* Offset from baseline to origin of + * underline bar (used for simulating a native + * underlined font). */ + int barHeight; /* Height of underline or overstrike bar + * (used for simulating a native underlined or + * strikeout font). */ +} UnixFont; + +/* + * Possible values for entries in the "types" field in a UnixFont structure, + * which classifies the types of all characters in the given font. This + * information is used when measuring and displaying characters. + * + * NORMAL: Standard character. + * REPLACE: This character doesn't print: instead of + * displaying character, display a replacement + * sequence like "\n" (for those characters where + * ANSI C defines such a sequence) or a sequence + * of the form "\xdd" where dd is the hex equivalent + * of the character. + * SKIP: Don't display anything for this character. This + * is only used where the font doesn't contain + * all the characters needed to generate + * replacement sequences. + */ + +#define NORMAL 0 +#define REPLACE 1 +#define SKIP 2 + +/* + * Characters used when displaying control sequences. + */ + +static char hexChars[] = "0123456789abcdefxtnvr\\"; + +/* + * The following table maps some control characters to sequences like '\n' + * rather than '\x10'. A zero entry in the table means no such mapping + * exists, and the table only maps characters less than 0x10. + */ + +static char mapChars[] = { + 0, 0, 0, 0, 0, 0, 0, + 'a', 'b', 't', 'n', 'v', 'f', 'r', + 0 +}; + + +static UnixFont * AllocFont _ANSI_ARGS_((TkFont *tkFontPtr, + Tk_Window tkwin, XFontStruct *fontStructPtr, + CONST char *fontName)); +static void DrawChars _ANSI_ARGS_((Display *display, + Drawable drawable, GC gc, UnixFont *fontPtr, + CONST char *source, int numChars, int x, + int y)); +static int GetControlCharSubst _ANSI_ARGS_((int c, char buf[4])); + + +/* + *--------------------------------------------------------------------------- + * + * TkpGetNativeFont -- + * + * Map a platform-specific native font name to a TkFont. + * + * Results: + * The return value is a pointer to a TkFont that represents the + * native font. If a native font by the given name could not be + * found, the return value is NULL. + * + * Every call to this procedure returns a new TkFont structure, + * even if the name has already been seen before. The caller should + * call TkpDeleteFont() when the font is no longer needed. + * + * The caller is responsible for initializing the memory associated + * with the generic TkFont when this function returns and releasing + * the contents of the generic TkFont before calling TkpDeleteFont(). + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +TkFont * +TkpGetNativeFont(tkwin, name) + Tk_Window tkwin; /* For display where font will be used. */ + CONST char *name; /* Platform-specific font name. */ +{ + XFontStruct *fontStructPtr; + + fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), name); + if (fontStructPtr == NULL) { + return NULL; + } + + return (TkFont *) AllocFont(NULL, tkwin, fontStructPtr, name); +} + +/* + *--------------------------------------------------------------------------- + * + * TkpGetFontFromAttributes -- + * + * Given a desired set of attributes for a font, find a font with + * the closest matching attributes. + * + * Results: + * The return value is a pointer to a TkFont that represents the + * font with the desired attributes. If a font with the desired + * attributes could not be constructed, some other font will be + * substituted automatically. + * + * Every call to this procedure returns a new TkFont structure, + * even if the specified attributes have already been seen before. + * The caller should call TkpDeleteFont() to free the platform- + * specific data when the font is no longer needed. + * + * The caller is responsible for initializing the memory associated + * with the generic TkFont when this function returns and releasing + * the contents of the generic TkFont before calling TkpDeleteFont(). + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ +TkFont * +TkpGetFontFromAttributes(tkFontPtr, tkwin, faPtr) + TkFont *tkFontPtr; /* If non-NULL, store the information in + * this existing TkFont structure, rather than + * allocating a new structure to hold the + * font; the existing contents of the font + * will be released. If NULL, a new TkFont + * structure is allocated. */ + Tk_Window tkwin; /* For display where font will be used. */ + CONST TkFontAttributes *faPtr; /* Set of attributes to match. */ +{ + int numNames, score, i, scaleable, pixelsize, xaPixelsize; + int bestIdx, bestScore, bestScaleableIdx, bestScaleableScore; + TkXLFDAttributes xa; + char buf[256]; + UnixFont *fontPtr; + char **nameList; + XFontStruct *fontStructPtr; + CONST char *fmt, *family; + double d; + + family = faPtr->family; + if (family == NULL) { + family = "*"; + } + + pixelsize = -faPtr->pointsize; + if (pixelsize < 0) { + d = -pixelsize * 25.4 / 72; + d *= WidthOfScreen(Tk_Screen(tkwin)); + d /= WidthMMOfScreen(Tk_Screen(tkwin)); + d += 0.5; + pixelsize = (int) d; + } + + /* + * Replace the standard Windows and Mac family names with the names that + * X likes. + */ + + if ((strcasecmp("Times New Roman", family) == 0) + || (strcasecmp("New York", family) == 0)) { + family = "Times"; + } else if ((strcasecmp("Courier New", family) == 0) + || (strcasecmp("Monaco", family) == 0)) { + family = "Courier"; + } else if ((strcasecmp("Arial", family) == 0) + || (strcasecmp("Geneva", family) == 0)) { + family = "Helvetica"; + } + + /* + * First try for the Q&D exact match. + */ + +#if 0 + sprintf(buf, "-*-%.200s-%s-%c-normal-*-*-%d-*-*-*-*-iso8859-1", family, + ((faPtr->weight > TK_FW_NORMAL) ? "bold" : "medium"), + ((faPtr->slant == TK_FS_ROMAN) ? 'r' : + (faPtr->slant == TK_FS_ITALIC) ? 'i' : 'o'), + faPtr->pointsize * 10); + fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), buf); +#else + fontStructPtr = NULL; +#endif + + if (fontStructPtr != NULL) { + goto end; + } + /* + * Couldn't find exact match. Now fall back to other available + * physical fonts. + */ + + fmt = "-*-%.240s-*-*-*-*-*-*-*-*-*-*-*-*"; + sprintf(buf, fmt, family); + nameList = XListFonts(Tk_Display(tkwin), buf, 10000, &numNames); + if (numNames == 0) { + /* + * Try getting some system font. + */ + + sprintf(buf, fmt, "fixed"); + nameList = XListFonts(Tk_Display(tkwin), buf, 10000, &numNames); + if (numNames == 0) { + getsystem: + fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), "fixed"); + if (fontStructPtr == NULL) { + fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), "*"); + if (fontStructPtr == NULL) { + panic("TkpGetFontFromAttributes: cannot get any font"); + } + } + goto end; + } + } + + /* + * Inspect each of the XLFDs and pick the one that most closely + * matches the desired attributes. + */ + + bestIdx = 0; + bestScore = INT_MAX; + bestScaleableIdx = 0; + bestScaleableScore = INT_MAX; + + for (i = 0; i < numNames; i++) { + score = 0; + scaleable = 0; + if (TkParseXLFD(nameList[i], &xa) != TCL_OK) { + continue; + } + xaPixelsize = -xa.fa.pointsize; + + /* + * Since most people used to use -adobe-* in their XLFDs, + * preserve the preference for "adobe" foundry. Otherwise + * some applications looks may change slightly if another foundry + * is chosen. + */ + + if (strcasecmp(xa.foundry, "adobe") != 0) { + score += 3000; + } + if (xa.fa.pointsize == 0) { + /* + * A scaleable font is almost always acceptable, but the + * corresponding bitmapped font would be better. + */ + + score += 10; + scaleable = 1; + } else { + /* + * A font that is too small is better than one that is too + * big. + */ + + if (xaPixelsize > pixelsize) { + score += (xaPixelsize - pixelsize) * 120; + } else { + score += (pixelsize - xaPixelsize) * 100; + } + } + + score += ABS(xa.fa.weight - faPtr->weight) * 30; + score += ABS(xa.fa.slant - faPtr->slant) * 25; + if (xa.slant == TK_FS_OBLIQUE) { + /* + * Italic fonts are preferred over oblique. */ + + score += 4; + } + + if (xa.setwidth != TK_SW_NORMAL) { + /* + * The normal setwidth is highly preferred. + */ + score += 2000; + } + if (xa.charset == TK_CS_OTHER) { + /* + * The standard character set is highly preferred over + * foreign languages charsets (because we don't support + * other languages yet). + */ + score += 11000; + } + if ((xa.charset == TK_CS_NORMAL) && (xa.encoding != 1)) { + /* + * The '1' encoding for the characters above 0x7f is highly + * preferred over the other encodings. + */ + score += 8000; + } + + if (scaleable) { + if (score < bestScaleableScore) { + bestScaleableIdx = i; + bestScaleableScore = score; + } + } else { + if (score < bestScore) { + bestIdx = i; + bestScore = score; + } + } + if (score == 0) { + break; + } + } + + /* + * Now we know which is the closest matching scaleable font and the + * closest matching bitmapped font. If the scaleable font was a + * better match, try getting the scaleable font; however, if the + * scalable font was not actually available in the desired + * pointsize, fall back to the closest bitmapped font. + */ + + fontStructPtr = NULL; + if (bestScaleableScore < bestScore) { + char *str, *rest; + + /* + * Fill in the desired pointsize info for this font. + */ + + tryscale: + str = nameList[bestScaleableIdx]; + for (i = 0; i < XLFD_PIXEL_SIZE - 1; i++) { + str = strchr(str + 1, '-'); + } + rest = str; + for (i = XLFD_PIXEL_SIZE - 1; i < XLFD_REGISTRY; i++) { + rest = strchr(rest + 1, '-'); + } + *str = '\0'; + sprintf(buf, "%.240s-*-%d-*-*-*-*-*%s", nameList[bestScaleableIdx], + pixelsize, rest); + *str = '-'; + fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), buf); + bestScaleableScore = INT_MAX; + } + if (fontStructPtr == NULL) { + strcpy(buf, nameList[bestIdx]); + fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), buf); + if (fontStructPtr == NULL) { + /* + * This shouldn't happen because the font name is one of the + * names that X gave us to use, but it does anyhow. + */ + + if (bestScaleableScore < INT_MAX) { + goto tryscale; + } else { + XFreeFontNames(nameList); + goto getsystem; + } + } + } + XFreeFontNames(nameList); + + end: + fontPtr = AllocFont(tkFontPtr, tkwin, fontStructPtr, buf); + fontPtr->font.fa.underline = faPtr->underline; + fontPtr->font.fa.overstrike = faPtr->overstrike; + + return (TkFont *) fontPtr; +} + + +/* + *--------------------------------------------------------------------------- + * + * TkpDeleteFont -- + * + * Called to release a font allocated by TkpGetNativeFont() or + * TkpGetFontFromAttributes(). The caller should have already + * released the fields of the TkFont that are used exclusively by + * the generic TkFont code. + * + * Results: + * None. + * + * Side effects: + * TkFont is deallocated. + * + *--------------------------------------------------------------------------- + */ + +void +TkpDeleteFont(tkFontPtr) + TkFont *tkFontPtr; /* Token of font to be deleted. */ +{ + UnixFont *fontPtr; + + fontPtr = (UnixFont *) tkFontPtr; + + XFreeFont(fontPtr->display, fontPtr->fontStructPtr); + ckfree((char *) fontPtr); +} + +/* + *--------------------------------------------------------------------------- + * + * TkpGetFontFamilies -- + * + * Return information about the font families that are available + * on the display of the given window. + * + * Results: + * interp->result is modified to hold a list of all the available + * font families. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +void +TkpGetFontFamilies(interp, tkwin) + Tcl_Interp *interp; + Tk_Window tkwin; +{ + int i, new, numNames; + char *family, *end, *p; + Tcl_HashTable familyTable; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + char **nameList; + + Tcl_InitHashTable(&familyTable, TCL_STRING_KEYS); + + nameList = XListFonts(Tk_Display(tkwin), "*", 10000, &numNames); + for (i = 0; i < numNames; i++) { + if (nameList[i][0] != '-') { + continue; + } + family = strchr(nameList[i] + 1, '-'); + if (family == NULL) { + continue; + } + family++; + end = strchr(family, '-'); + if (end == NULL) { + continue; + } + *end = '\0'; + for (p = family; *p != '\0'; p++) { + if (isupper(UCHAR(*p))) { + *p = tolower(UCHAR(*p)); + } + } + Tcl_CreateHashEntry(&familyTable, family, &new); + } + + hPtr = Tcl_FirstHashEntry(&familyTable, &search); + while (hPtr != NULL) { + Tcl_AppendElement(interp, Tcl_GetHashKey(&familyTable, hPtr)); + hPtr = Tcl_NextHashEntry(&search); + } + + Tcl_DeleteHashTable(&familyTable); + XFreeFontNames(nameList); +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_MeasureChars -- + * + * Determine the number of characters from the string that will fit + * in the given horizontal span. The measurement is done under the + * assumption that Tk_DrawChars() will be used to actually display + * the characters. + * + * Results: + * The return value is the number of characters from source that + * fit into the span that extends from 0 to maxLength. *lengthPtr is + * filled with the x-coordinate of the right edge of the last + * character that did fit. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ +int +Tk_MeasureChars(tkfont, source, numChars, maxLength, flags, lengthPtr) + Tk_Font tkfont; /* Font in which characters will be drawn. */ + CONST char *source; /* Characters to be displayed. Need not be + * '\0' terminated. */ + int numChars; /* Maximum number of characters to consider + * from source string. */ + int maxLength; /* If > 0, maxLength specifies the longest + * permissible line length; don't consider any + * character that would cross this + * x-position. If <= 0, then line length is + * unbounded and the flags argument is + * ignored. */ + int flags; /* Various flag bits OR-ed together: + * TK_PARTIAL_OK means include the last char + * which only partially fit on this line. + * TK_WHOLE_WORDS means stop on a word + * boundary, if possible. + * TK_AT_LEAST_ONE means return at least one + * character even if no characters fit. */ + int *lengthPtr; /* Filled with x-location just after the + * terminating character. */ +{ + UnixFont *fontPtr; + CONST char *p; /* Current character. */ + CONST char *term; /* Pointer to most recent character that + * may legally be a terminating character. */ + int termX; /* X-position just after term. */ + int curX; /* X-position corresponding to p. */ + int newX; /* X-position corresponding to p+1. */ + int c, sawNonSpace; + + fontPtr = (UnixFont *) tkfont; + + if (numChars == 0) { + *lengthPtr = 0; + return 0; + } + + if (maxLength <= 0) { + maxLength = INT_MAX; + } + + newX = curX = termX = 0; + p = term = source; + sawNonSpace = !isspace(UCHAR(*p)); + + /* + * Scan the input string one character at a time, calculating width. + */ + + for (c = UCHAR(*p); ; ) { + newX += fontPtr->widths[c]; + if (newX > maxLength) { + break; + } + curX = newX; + numChars--; + p++; + if (numChars == 0) { + term = p; + termX = curX; + break; + } + + c = UCHAR(*p); + if (isspace(c)) { + if (sawNonSpace) { + term = p; + termX = curX; + sawNonSpace = 0; + } + } else { + sawNonSpace = 1; + } + } + + /* + * P points to the first character that doesn't fit in the desired + * span. Use the flags to figure out what to return. + */ + + if ((flags & TK_PARTIAL_OK) && (numChars > 0) && (curX < maxLength)) { + /* + * Include the first character that didn't quite fit in the desired + * span. The width returned will include the width of that extra + * character. + */ + + numChars--; + curX = newX; + p++; + } + if ((flags & TK_AT_LEAST_ONE) && (term == source) && (numChars > 0)) { + term = p; + termX = curX; + if (term == source) { + term++; + termX = newX; + } + } else if ((numChars == 0) || !(flags & TK_WHOLE_WORDS)) { + term = p; + termX = curX; + } + + *lengthPtr = termX; + return term-source; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_DrawChars, DrawChars -- + * + * Draw a string of characters on the screen. Tk_DrawChars() + * expands control characters that occur in the string to \X or + * \xXX sequences. DrawChars() just draws the strings. + * + * Results: + * None. + * + * Side effects: + * Information gets drawn on the screen. + * + *--------------------------------------------------------------------------- + */ + +void +Tk_DrawChars(display, drawable, gc, tkfont, source, numChars, x, y) + Display *display; /* Display on which to draw. */ + Drawable drawable; /* Window or pixmap in which to draw. */ + GC gc; /* Graphics context for drawing characters. */ + Tk_Font tkfont; /* Font in which characters will be drawn; + * must be the same as font used in GC. */ + CONST char *source; /* Characters to be displayed. Need not be + * '\0' terminated. All Tk meta-characters + * (tabs, control characters, and newlines) + * should be stripped out of the string that + * is passed to this function. If they are + * not stripped out, they will be displayed as + * regular printing characters. */ + int numChars; /* Number of characters in string. */ + int x, y; /* Coordinates at which to place origin of + * string when drawing. */ +{ + UnixFont *fontPtr; + CONST char *p; + int i, type; + char buf[4]; + + fontPtr = (UnixFont *) tkfont; + + p = source; + for (i = 0; i < numChars; i++) { + type = fontPtr->types[UCHAR(*p)]; + if (type != NORMAL) { + DrawChars(display, drawable, gc, fontPtr, source, p - source, x, y); + x += XTextWidth(fontPtr->fontStructPtr, source, p - source); + if (type == REPLACE) { + DrawChars(display, drawable, gc, fontPtr, buf, + GetControlCharSubst(UCHAR(*p), buf), x, y); + x += fontPtr->widths[UCHAR(*p)]; + } + source = p + 1; + } + p++; + } + + DrawChars(display, drawable, gc, fontPtr, source, p - source, x, y); +} + +static void +DrawChars(display, drawable, gc, fontPtr, source, numChars, x, y) + Display *display; /* Display on which to draw. */ + Drawable drawable; /* Window or pixmap in which to draw. */ + GC gc; /* Graphics context for drawing characters. */ + UnixFont *fontPtr; /* Font in which characters will be drawn; + * must be the same as font used in GC. */ + CONST char *source; /* Characters to be displayed. Need not be + * '\0' terminated. All Tk meta-characters + * (tabs, control characters, and newlines) + * should be stripped out of the string that + * is passed to this function. If they are + * not stripped out, they will be displayed as + * regular printing characters. */ + int numChars; /* Number of characters in string. */ + int x, y; /* Coordinates at which to place origin of + * string when drawing. */ +{ + XDrawString(display, drawable, gc, x, y, source, numChars); + + if (fontPtr->font.fa.underline != 0) { + XFillRectangle(display, drawable, gc, x, + y + fontPtr->underlinePos, + (unsigned) XTextWidth(fontPtr->fontStructPtr, source, numChars), + (unsigned) fontPtr->barHeight); + } + if (fontPtr->font.fa.overstrike != 0) { + y -= fontPtr->font.fm.descent + (fontPtr->font.fm.ascent) / 10; + XFillRectangle(display, drawable, gc, x, y, + (unsigned) XTextWidth(fontPtr->fontStructPtr, source, numChars), + (unsigned) fontPtr->barHeight); + } +} + +/* + *--------------------------------------------------------------------------- + * + * AllocFont -- + * + * Helper for TkpGetNativeFont() and TkpGetFontFromAttributes(). + * Allocates and intializes the memory for a new TkFont that + * wraps the platform-specific data. + * + * Results: + * Returns pointer to newly constructed TkFont. + * + * The caller is responsible for initializing the fields of the + * TkFont that are used exclusively by the generic TkFont code, and + * for releasing those fields before calling TkpDeleteFont(). + * + * Side effects: + * Memory allocated. + * + *--------------------------------------------------------------------------- + */ + +static UnixFont * +AllocFont(tkFontPtr, tkwin, fontStructPtr, fontName) + TkFont *tkFontPtr; /* If non-NULL, store the information in + * this existing TkFont structure, rather than + * allocating a new structure to hold the + * font; the existing contents of the font + * will be released. If NULL, a new TkFont + * structure is allocated. */ + Tk_Window tkwin; /* For display where font will be used. */ + XFontStruct *fontStructPtr; /* X information about font. */ + CONST char *fontName; /* The string passed to XLoadQueryFont() to + * construct the fontStructPtr. */ +{ + UnixFont *fontPtr; + unsigned long value; + int i, width, firstChar, lastChar, n, replaceOK; + char *name, *p; + char buf[4]; + TkXLFDAttributes xa; + double d; + + if (tkFontPtr != NULL) { + fontPtr = (UnixFont *) tkFontPtr; + XFreeFont(fontPtr->display, fontPtr->fontStructPtr); + } else { + fontPtr = (UnixFont *) ckalloc(sizeof(UnixFont)); + } + + /* + * Encapsulate the generic stuff in the TkFont. + */ + + fontPtr->font.fid = fontStructPtr->fid; + + if (XGetFontProperty(fontStructPtr, XA_FONT, &value) && (value != 0)) { + name = Tk_GetAtomName(tkwin, (Atom) value); + TkInitFontAttributes(&xa.fa); + if (TkParseXLFD(name, &xa) == TCL_OK) { + goto ok; + } + } + TkInitFontAttributes(&xa.fa); + if (TkParseXLFD(fontName, &xa) != TCL_OK) { + TkInitFontAttributes(&fontPtr->font.fa); + fontPtr->font.fa.family = Tk_GetUid(fontName); + } else { + ok: + fontPtr->font.fa = xa.fa; + } + + if (fontPtr->font.fa.pointsize < 0) { + d = -fontPtr->font.fa.pointsize * 72 / 25.4; + d *= WidthMMOfScreen(Tk_Screen(tkwin)); + d /= WidthOfScreen(Tk_Screen(tkwin)); + d += 0.5; + fontPtr->font.fa.pointsize = (int) d; + } + + fontPtr->font.fm.ascent = fontStructPtr->ascent; + fontPtr->font.fm.descent = fontStructPtr->descent; + fontPtr->font.fm.maxWidth = fontStructPtr->max_bounds.width; + fontPtr->font.fm.fixed = 1; + fontPtr->display = Tk_Display(tkwin); + fontPtr->fontStructPtr = fontStructPtr; + + /* + * Classify the characters. + */ + + firstChar = fontStructPtr->min_char_or_byte2; + lastChar = fontStructPtr->max_char_or_byte2; + for (i = 0; i < 256; i++) { + if ((i == 0177) || (i < firstChar) || (i > lastChar)) { + fontPtr->types[i] = REPLACE; + } else { + fontPtr->types[i] = NORMAL; + } + } + + /* + * Compute the widths for all the normal characters. Any other + * characters are given an initial width of 0. Also, this determines + * if this is a fixed or variable width font, by comparing the widths + * of all the normal characters. + */ + + width = 0; + for (i = 0; i < 256; i++) { + if (fontPtr->types[i] != NORMAL) { + n = 0; + } else if (fontStructPtr->per_char == NULL) { + n = fontStructPtr->max_bounds.width; + } else { + n = fontStructPtr->per_char[i - firstChar].width; + } + fontPtr->widths[i] = n; + if (n != 0) { + if (width == 0) { + width = n; + } else if (width != n) { + fontPtr->font.fm.fixed = 0; + } + } + } + + /* + * Compute the widths of the characters that should be replaced with + * control character expansions. If the appropriate chars are not + * available in this font, then control character expansions will not + * be used; control chars will be invisible & zero-width. + */ + + replaceOK = 1; + for (p = hexChars; *p != '\0'; p++) { + if ((UCHAR(*p) < firstChar) || (UCHAR(*p) > lastChar)) { + replaceOK = 0; + break; + } + } + for (i = 0; i < 256; i++) { + if (fontPtr->types[i] == REPLACE) { + if (replaceOK) { + n = GetControlCharSubst(i, buf); + for ( ; --n >= 0; ) { + fontPtr->widths[i] += fontPtr->widths[UCHAR(buf[n])]; + } + } else { + fontPtr->types[i] = SKIP; + } + } + } + + if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_POSITION, &value)) { + fontPtr->underlinePos = value; + } else { + /* + * If the XA_UNDERLINE_POSITION property does not exist, the X + * manual recommends using the following value: + */ + + fontPtr->underlinePos = fontStructPtr->descent / 2; + } + fontPtr->barHeight = 0; + if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_THICKNESS, &value)) { + /* + * Sometimes this is 0 even though it shouldn't be. + */ + fontPtr->barHeight = value; + } + if (fontPtr->barHeight == 0) { + /* + * If the XA_UNDERLINE_THICKNESS property does not exist, the X + * manual recommends using the width of the stem on a capital + * letter. I don't know of a way to get the stem width of a letter, + * so guess and use 1/3 the width of a capital I. + */ + + fontPtr->barHeight = fontPtr->widths['I'] / 3; + if (fontPtr->barHeight == 0) { + fontPtr->barHeight = 1; + } + } + if (fontPtr->underlinePos + fontPtr->barHeight > fontStructPtr->descent) { + /* + * If this set of cobbled together values would cause the bottom of + * the underline bar to stick below the descent of the font, jack + * the underline up a bit higher. + */ + + fontPtr->barHeight = fontStructPtr->descent - fontPtr->underlinePos; + if (fontPtr->barHeight == 0) { + fontPtr->underlinePos--; + fontPtr->barHeight = 1; + } + } + + return fontPtr; +} + +/* + *--------------------------------------------------------------------------- + * + * GetControlCharSubst -- + * + * When displaying text in a widget, a backslashed escape sequence + * is substituted for control characters that occur in the text. + * Given a control character, fill in a buffer with the replacement + * string that should be displayed. + * + * Results: + * The return value is the length of the substitute string. buf is + * filled with the substitute string; it is not '\0' terminated. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static int +GetControlCharSubst(c, buf) + int c; /* The control character to be replaced. */ + char buf[4]; /* Buffer that gets replacement string. It + * only needs to be 4 characters long. */ +{ + buf[0] = '\\'; + if ((c < sizeof(mapChars)) && (mapChars[c] != 0)) { + buf[1] = mapChars[c]; + return 2; + } else { + buf[1] = 'x'; + buf[2] = hexChars[(c >> 4) & 0xf]; + buf[3] = hexChars[c & 0xf]; + return 4; + } +} |