diff options
Diffstat (limited to 'unix/tkUnixFont.c')
-rw-r--r-- | unix/tkUnixFont.c | 2770 |
1 files changed, 2163 insertions, 607 deletions
diff --git a/unix/tkUnixFont.c b/unix/tkUnixFont.c index 638db14..43312e9 100644 --- a/unix/tkUnixFont.c +++ b/unix/tkUnixFont.c @@ -4,96 +4,388 @@ * Contains the Unix implementation of the platform-independant * font package interface. * - * Copyright (c) 1996 Sun Microsystems, Inc. + * Copyright (c) 1996-1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkUnixFont.c,v 1.5 1999/03/10 07:04:45 stanton Exp $ + * RCS: @(#) $Id: tkUnixFont.c,v 1.6 1999/04/16 01:51:46 stanton Exp $ */ -#include "tkPort.h" -#include "tkInt.h" #include "tkUnixInt.h" - #include "tkFont.h" -#ifndef ABS -#define ABS(n) (((n) < 0) ? -(n) : (n)) -#endif +/* + * The preferred font encodings. + */ + +static CONST char *encodingList[] = { + "iso8859-1", "jis0208", "jis0212", NULL +}; + +/* + * The following structure represents a font family. It is assumed that + * all screen fonts constructed from the same "font family" share certain + * properties; all screen fonts with the same "font family" point to a + * shared instance of this structure. The most important shared property + * is the character existence metrics, used to determine if a screen font + * can display a given Unicode character. + * + * Under Unix, there are three attributes that uniquely identify a "font + * family": the foundry, face name, and charset. + */ + +#define FONTMAP_SHIFT 10 + +#define FONTMAP_PAGES (1 << (sizeof(Tcl_UniChar)*8 - FONTMAP_SHIFT)) +#define FONTMAP_BITSPERPAGE (1 << FONTMAP_SHIFT) + +typedef struct FontFamily { + struct FontFamily *nextPtr; /* Next in list of all known font families. */ + int refCount; /* How many SubFonts are referring to this + * FontFamily. When the refCount drops to + * zero, this FontFamily may be freed. */ + /* + * Key. + */ + + Tk_Uid foundry; /* Foundry key for this FontFamily. */ + Tk_Uid faceName; /* Face name key for this FontFamily. */ + Tcl_Encoding encoding; /* Encoding key for this FontFamily. */ + + /* + * Derived properties. + */ + + int isTwoByteFont; /* 1 if this is a double-byte font, 0 + * otherwise. */ + char *fontMap[FONTMAP_PAGES]; + /* Two-level sparse table used to determine + * quickly if the specified character exists. + * As characters are encountered, more pages + * in this table are dynamically alloced. The + * contents of each page is a bitmask + * consisting of FONTMAP_BITSPERPAGE bits, + * representing whether this font can be used + * to display the given character at the + * corresponding bit position. The high bits + * of the character are used to pick which + * page of the table is used. */ +} FontFamily; + +/* + * The following structure encapsulates an individual screen font. A font + * object is made up of however many SubFonts are necessary to display a + * stream of multilingual characters. + */ + +typedef struct SubFont { + char **fontMap; /* Pointer to font map from the FontFamily, + * cached here to save a dereference. */ + XFontStruct *fontStructPtr; /* The specific screen font that will be + * used when displaying/measuring chars + * belonging to the FontFamily. */ + FontFamily *familyPtr; /* The FontFamily for this SubFont. */ +} SubFont; /* - * The following structure represents Unix's implementation of a font. + * The following structure represents Unix's implementation of a font + * object. */ +#define SUBFONT_SPACE 3 +#define BASE_CHARS 256 + 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. */ + SubFont staticSubFonts[SUBFONT_SPACE]; + /* Builtin space for a limited number of + * SubFonts. */ + int numSubFonts; /* Length of following array. */ + SubFont *subFontArray; /* Array of SubFonts that have been loaded + * in order to draw/measure all the characters + * encountered by this font so far. All fonts + * start off with one SubFont initialized by + * AllocFont() from the original set of font + * attributes. Usually points to + * staticSubFonts, but may point to malloced + * space if there are lots of SubFonts. */ + SubFont controlSubFont; /* Font to use to display control-character + * expansions. */ + + Display *display; /* Display that owns font. */ + int pixelSize; /* Original pixel size used when font was + * constructed. */ + TkXLFDAttributes xa; /* Additional attributes that specify the + * preferred foundry and encoding to use when + * constructing additional SubFonts. */ + int widths[BASE_CHARS]; /* Widths of first 256 chars in the base + * font, for handling common case. */ int underlinePos; /* Offset from baseline to origin of - * underline bar (used for simulating a native - * underlined font). */ + * underline bar (used when drawing underlined + * font) (pixels). */ int barHeight; /* Height of underline or overstrike bar - * (used for simulating a native underlined or - * strikeout font). */ + * (used when drawing underlined or strikeout + * font) (pixels). */ } 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 + * The following structure and definition is used to keep track of the + * alternative names for various encodings. Asking for an encoding that + * matches one of the alias patterns will result in actually getting the + * encoding by its real name. + */ + +typedef struct EncodingAlias { + char *realName; /* The real name of the encoding to load if + * the provided name matched the pattern. */ + char *aliasPattern; /* Pattern for encoding name, of the form + * that is acceptable to Tcl_StringMatch. */ +} EncodingAlias; /* - * Characters used when displaying control sequences. + * Just some utility structures used for passing around values in helper + * procedures. */ + +typedef struct FontAttributes { + TkFontAttributes fa; + TkXLFDAttributes xa; +} FontAttributes; + + +typedef struct ThreadSpecificData { + FontFamily *fontFamilyList; /* The list of font families that are + * currently loaded. As screen fonts + * are loaded, this list grows to hold + * information about what characters + * exist in each font family. */ + FontFamily controlFamily; /* FontFamily used to handle control + * character expansions. The encoding + * of this FontFamily converts UTF-8 to + * backslashed escape sequences. */ +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; -static char hexChars[] = "0123456789abcdefxtnvr\\"; +/* + * The set of builtin encoding alises to convert the XLFD names for the + * encodings into the names expected by the Tcl encoding package. + */ + +static EncodingAlias encodingAliases[] = { + {"gb2312", "gb2312*"}, + {"big5", "big5*"}, + {"cns11643-1", "cns11643*-1"}, + {"cns11643-1", "cns11643*.1-0"}, + {"cns11643-2", "cns11643*-2"}, + {"cns11643-2", "cns11643*.2-0"}, + {"jis0201", "jisx0202*"}, + {"jis0208", "jisc6226*"}, + {"jis0208", "jisx0208*"}, + {"jis0212", "jisx0212*"}, + {"tis620", "tis620*"}, + {"ksc5601", "ksc5601*"}, + {"dingbats", "*dingbats"}, + {NULL, NULL} +}; /* - * 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. + * Procedures used only in this file. */ -static char mapChars[] = { - 0, 0, 0, 0, 0, 0, 0, - 'a', 'b', 't', 'n', 'v', 'f', 'r', - 0 -}; +static FontFamily * AllocFontFamily _ANSI_ARGS_((Display *display, + XFontStruct *fontStructPtr, int base)); +static SubFont * CanUseFallback _ANSI_ARGS_((UnixFont *fontPtr, + char *fallbackName, int ch)); +static SubFont * CanUseFallbackWithAliases _ANSI_ARGS_(( + UnixFont *fontPtr, char *fallbackName, + int ch, Tcl_DString *nameTriedPtr)); +static int ControlUtfProc _ANSI_ARGS_((ClientData clientData, + CONST char *src, int srcLen, int flags, + Tcl_EncodingState *statePtr, char *dst, + int dstLen, int *srcReadPtr, int *dstWrotePtr, + int *dstCharsPtr)); +static XFontStruct * CreateClosestFont _ANSI_ARGS_((Tk_Window tkwin, + CONST TkFontAttributes *faPtr, + CONST TkXLFDAttributes *xaPtr)); +static SubFont * FindSubFontForChar _ANSI_ARGS_((UnixFont *fontPtr, + int ch)); +static void FontMapInsert _ANSI_ARGS_((SubFont *subFontPtr, + int ch)); +static void FontMapLoadPage _ANSI_ARGS_((SubFont *subFontPtr, + int row)); +static int FontMapLookup _ANSI_ARGS_((SubFont *subFontPtr, + int ch)); +static void FreeFontFamily _ANSI_ARGS_((FontFamily *afPtr)); +static CONST char * GetEncodingAlias _ANSI_ARGS_((CONST char *name)); +static int GetFontAttributes _ANSI_ARGS_((Display *display, + XFontStruct *fontStructPtr, FontAttributes *faPtr)); +static XFontStruct * GetScreenFont _ANSI_ARGS_((Display *display, + FontAttributes *wantPtr, char **nameList, + int bestIdx[], unsigned int bestScore[])); +static XFontStruct * GetSystemFont _ANSI_ARGS_((Display *display)); +static int IdentifySymbolEncodings _ANSI_ARGS_(( + FontAttributes *faPtr)); +static void InitFont _ANSI_ARGS_((Tk_Window tkwin, + XFontStruct *fontStructPtr, UnixFont *fontPtr)); +static void InitSubFont _ANSI_ARGS_((Display *display, + XFontStruct *fontStructPtr, int base, + SubFont *subFontPtr)); +static char ** ListFonts _ANSI_ARGS_((Display *display, + CONST char *faceName, int *numNamesPtr)); +static char ** ListFontOrAlias _ANSI_ARGS_((Display *display, + CONST char *faceName, int *numNamesPtr)); +static unsigned int RankAttributes _ANSI_ARGS_((FontAttributes *wantPtr, + FontAttributes *gotPtr)); +static void ReleaseFont _ANSI_ARGS_((UnixFont *fontPtr)); +static void ReleaseSubFont _ANSI_ARGS_((Display *display, + SubFont *subFontPtr)); +static int SeenName _ANSI_ARGS_((CONST char *name, + Tcl_DString *dsPtr)); + +/* + *------------------------------------------------------------------------- + * + * TkpFontPkgInit -- + * + * This procedure is called when an application is created. It + * initializes all the structures that are used by the + * platform-dependent code on a per application basis. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ -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])); +void +TkpFontPkgInit(mainPtr) + TkMainInfo *mainPtr; /* The application being created. */ +{ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + Tcl_EncodingType type; + SubFont dummy; + int i; + + if (tsdPtr->controlFamily.encoding == NULL) { + type.encodingName = "X11ControlChars"; + type.toUtfProc = ControlUtfProc; + type.fromUtfProc = ControlUtfProc; + type.freeProc = NULL; + type.clientData = NULL; + type.nullSize = 0; + + tsdPtr->controlFamily.refCount = 2; + tsdPtr->controlFamily.encoding = Tcl_CreateEncoding(&type); + tsdPtr->controlFamily.isTwoByteFont = 0; + + dummy.familyPtr = &tsdPtr->controlFamily; + dummy.fontMap = tsdPtr->controlFamily.fontMap; + for (i = 0x00; i < 0x20; i++) { + FontMapInsert(&dummy, i); + FontMapInsert(&dummy, i + 0x80); + } + } +} + +/* + *------------------------------------------------------------------------- + * + * ControlUtfProc -- + * + * Convert from UTF-8 into the ASCII expansion of a control + * character. + * + * Results: + * Returns TCL_OK if conversion was successful. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ +static int +ControlUtfProc(clientData, src, srcLen, flags, statePtr, dst, dstLen, + srcReadPtr, dstWrotePtr, dstCharsPtr) + ClientData clientData; /* Not used. */ + CONST char *src; /* Source string in UTF-8. */ + int srcLen; /* Source string length in bytes. */ + int flags; /* Conversion control flags. */ + Tcl_EncodingState *statePtr;/* Place for conversion routine to store + * state information used during a piecewise + * conversion. Contents of statePtr are + * initialized and/or reset by conversion + * routine under control of flags argument. */ + char *dst; /* Output buffer in which converted string + * is stored. */ + int dstLen; /* The maximum length of output buffer in + * bytes. */ + int *srcReadPtr; /* Filled with the number of bytes from the + * source string that were converted. This + * may be less than the original source length + * if there was a problem converting some + * source characters. */ + int *dstWrotePtr; /* Filled with the number of bytes that were + * stored in the output buffer as a result of + * the conversion. */ + int *dstCharsPtr; /* Filled with the number of characters that + * correspond to the bytes stored in the + * output buffer. */ +{ + CONST char *srcStart, *srcEnd; + char *dstStart, *dstEnd; + Tcl_UniChar ch; + int result; + static char hexChars[] = "0123456789abcdef"; + static char mapChars[] = { + 0, 0, 0, 0, 0, 0, 0, + 'a', 'b', 't', 'n', 'v', 'f', 'r' + }; + + result = TCL_OK; + + srcStart = src; + srcEnd = src + srcLen; + + dstStart = dst; + dstEnd = dst + dstLen - 6; + + for ( ; src < srcEnd; ) { + if (dst > dstEnd) { + result = TCL_CONVERT_NOSPACE; + break; + } + src += Tcl_UtfToUniChar(src, &ch); + dst[0] = '\\'; + if ((ch < sizeof(mapChars)) && (mapChars[ch] != 0)) { + dst[1] = mapChars[ch]; + dst += 2; + } else if (ch < 256) { + dst[1] = 'x'; + dst[2] = hexChars[(ch >> 4) & 0xf]; + dst[3] = hexChars[ch & 0xf]; + dst += 4; + } else { + dst[1] = 'u'; + dst[2] = hexChars[(ch >> 12) & 0xf]; + dst[3] = hexChars[(ch >> 8) & 0xf]; + dst[4] = hexChars[(ch >> 4) & 0xf]; + dst[5] = hexChars[ch & 0xf]; + dst += 6; + } + } + *srcReadPtr = src - srcEnd; + *dstWrotePtr = dst - dstStart; + *dstCharsPtr = dst - dstStart; + return result; +} /* *--------------------------------------------------------------------------- @@ -116,18 +408,20 @@ static int GetControlCharSubst _ANSI_ARGS_((int c, char buf[4])); * the contents of the generic TkFont before calling TkpDeleteFont(). * * Side effects: - * None. + * Memory allocated. * *--------------------------------------------------------------------------- */ - + TkFont * TkpGetNativeFont(tkwin, name) Tk_Window tkwin; /* For display where font will be used. */ CONST char *name; /* Platform-specific font name. */ { + UnixFont *fontPtr; XFontStruct *fontStructPtr; - char *p; + FontAttributes fa; + CONST char *p; int hasSpace, dashes, hasWild; /* @@ -141,7 +435,7 @@ TkpGetNativeFont(tkwin, name) */ hasSpace = dashes = hasWild = 0; - for (p = (char *) name; *p != '\0'; p++) { + for (p = name; *p != '\0'; p++) { if (*p == ' ') { if (p[1] == '-') { return NULL; @@ -159,10 +453,36 @@ TkpGetNativeFont(tkwin, name) fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), name); if (fontStructPtr == NULL) { - return NULL; + /* + * Handle all names that look like XLFDs here. Otherwise, when + * TkpGetFontFromAttributes is called from generic code, any + * foundry or encoding information specified in the XLFD will have + * been parsed out and lost. But make sure we don't have an + * "-option value" string since TkFontParseXLFD would return a + * false success when attempting to parse it. + */ + + if (name[0] == '-') { + if (name[1] != '*') { + char *dash; + + dash = strchr(name + 1, '-'); + if ((dash == NULL) || (isspace(UCHAR(dash[-1])))) { + return NULL; + } + } + } else if (name[0] != '*') { + return NULL; + } + if (TkFontParseXLFD(name, &fa.fa, &fa.xa) != TCL_OK) { + return NULL; + } + fontStructPtr = CreateClosestFont(tkwin, &fa.fa, &fa.xa); } + fontPtr = (UnixFont *) ckalloc(sizeof(UnixFont)); + InitFont(tkwin, fontStructPtr, fontPtr); - return (TkFont *) AllocFont(NULL, tkwin, fontStructPtr, name); + return (TkFont *) fontPtr; } /* @@ -189,7 +509,7 @@ TkpGetNativeFont(tkwin, name) * the contents of the generic TkFont before calling TkpDeleteFont(). * * Side effects: - * None. + * Memory allocated. * *--------------------------------------------------------------------------- */ @@ -202,249 +522,29 @@ TkpGetFontFromAttributes(tkFontPtr, tkwin, faPtr) * 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. */ + 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; + TkXLFDAttributes xa; 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. - */ + TkInitXLFDAttributes(&xa); + fontStructPtr = CreateClosestFont(tkwin, faPtr, &xa); - 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; - } - } + fontPtr = (UnixFont *) tkFontPtr; + if (fontPtr == NULL) { + fontPtr = (UnixFont *) ckalloc(sizeof(UnixFont)); + } else { + ReleaseFont(fontPtr); } - XFreeFontNames(nameList); + InitFont(tkwin, fontStructPtr, fontPtr); - end: - fontPtr = AllocFont(tkFontPtr, tkwin, fontStructPtr, buf); - fontPtr->font.fa.underline = faPtr->underline; + fontPtr->font.fa.underline = faPtr->underline; fontPtr->font.fa.overstrike = faPtr->overstrike; return (TkFont *) fontPtr; } - /* *--------------------------------------------------------------------------- @@ -472,9 +572,7 @@ TkpDeleteFont(tkFontPtr) UnixFont *fontPtr; fontPtr = (UnixFont *) tkFontPtr; - - XFreeFont(fontPtr->display, fontPtr->fontStructPtr); - ckfree((char *) fontPtr); + ReleaseFont(fontPtr); } /* @@ -486,7 +584,7 @@ TkpDeleteFont(tkFontPtr) * on the display of the given window. * * Results: - * interp->result is modified to hold a list of all the available + * Modifies interp's result object to hold a list of all the available * font families. * * Side effects: @@ -494,52 +592,80 @@ TkpDeleteFont(tkFontPtr) * *--------------------------------------------------------------------------- */ - + void TkpGetFontFamilies(interp, tkwin) - Tcl_Interp *interp; - Tk_Window tkwin; + Tcl_Interp *interp; /* Interp to hold result. */ + Tk_Window tkwin; /* For display to query. */ { int i, new, numNames; - char *family, *end, *p; + char *family; Tcl_HashTable familyTable; Tcl_HashEntry *hPtr; Tcl_HashSearch search; char **nameList; + Tcl_Obj *resultPtr, *strPtr; - Tcl_InitHashTable(&familyTable, TCL_STRING_KEYS); + resultPtr = Tcl_GetObjResult(interp); - nameList = XListFonts(Tk_Display(tkwin), "*", 10000, &numNames); + Tcl_InitHashTable(&familyTable, TCL_STRING_KEYS); + nameList = ListFonts(Tk_Display(tkwin), "*", &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)); - } - } + family = strchr(nameList[i] + 1, '-') + 1; + strchr(family, '-')[0] = '\0'; Tcl_CreateHashEntry(&familyTable, family, &new); } + XFreeFontNames(nameList); hPtr = Tcl_FirstHashEntry(&familyTable, &search); while (hPtr != NULL) { - Tcl_AppendElement(interp, Tcl_GetHashKey(&familyTable, hPtr)); + strPtr = Tcl_NewStringObj(Tcl_GetHashKey(&familyTable, hPtr), -1); + Tcl_ListObjAppendElement(NULL, resultPtr, strPtr); hPtr = Tcl_NextHashEntry(&search); } Tcl_DeleteHashTable(&familyTable); - XFreeFontNames(nameList); +} + +/* + *------------------------------------------------------------------------- + * + * TkpGetSubFonts -- + * + * A function used by the testing package for querying the actual + * screen fonts that make up a font object. + * + * Results: + * Modifies interp's result object to hold a list containing the + * names of the screen fonts that make up the given font object. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ + +void +TkpGetSubFonts(interp, tkfont) + Tcl_Interp *interp; + Tk_Font tkfont; +{ + int i; + Tcl_Obj *objv[3]; + Tcl_Obj *resultPtr, *listPtr; + UnixFont *fontPtr; + FontFamily *familyPtr; + + resultPtr = Tcl_GetObjResult(interp); + fontPtr = (UnixFont *) tkfont; + for (i = 0; i < fontPtr->numSubFonts; i++) { + familyPtr = fontPtr->subFontArray[i].familyPtr; + objv[0] = Tcl_NewStringObj(familyPtr->faceName, -1); + objv[1] = Tcl_NewStringObj(familyPtr->foundry, -1); + objv[2] = Tcl_NewStringObj(Tcl_GetEncodingName(familyPtr->encoding), -1); + listPtr = Tcl_NewListObj(3, objv); + Tcl_ListObjAppendElement(NULL, resultPtr, listPtr); + } } /* @@ -553,7 +679,7 @@ TkpGetFontFamilies(interp, tkwin) * the characters. * * Results: - * The return value is the number of characters from source that + * The return value is the number of bytes 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. @@ -563,18 +689,19 @@ TkpGetFontFamilies(interp, tkwin) * *--------------------------------------------------------------------------- */ + int -Tk_MeasureChars(tkfont, source, numChars, maxLength, flags, lengthPtr) +Tk_MeasureChars(tkfont, source, numBytes, maxLength, flags, lengthPtr) Tk_Font tkfont; /* Font in which characters will be drawn. */ - CONST char *source; /* Characters to be displayed. Need not be + CONST char *source; /* UTF-8 string to be displayed. Need not be * '\0' terminated. */ - int numChars; /* Maximum number of characters to consider + int numBytes; /* Maximum number of bytes 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 + int maxLength; /* If >= 0, maxLength specifies the longest + * permissible line length in pixels; 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 @@ -587,99 +714,179 @@ Tk_MeasureChars(tkfont, source, numChars, maxLength, flags, lengthPtr) * 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; + SubFont *lastSubFontPtr; + int curX, curByte; - fontPtr = (UnixFont *) tkfont; + /* + * Unix does not use kerning or fractional character widths when + * displaying text on the screen. So that means we can safely measure + * individual characters or spans of characters and add up the widths + * w/o any "off-by-one-pixel" errors. + */ - if (numChars == 0) { - *lengthPtr = 0; - return 0; - } + fontPtr = (UnixFont *) tkfont; - if (maxLength <= 0) { - maxLength = INT_MAX; - } + lastSubFontPtr = &fontPtr->subFontArray[0]; - newX = curX = termX = 0; - p = term = source; - sawNonSpace = !isspace(UCHAR(*p)); + if (numBytes == 0) { + curX = 0; + curByte = 0; + } else if (maxLength < 0) { + CONST char *p, *end, *next; + Tcl_UniChar ch; + SubFont *thisSubFontPtr; + FontFamily *familyPtr; + Tcl_DString runString; - /* - * Scan the input string one character at a time, calculating width. - */ + /* + * A three step process: + * 1. Find a contiguous range of characters that can all be + * represented by a single screen font. + * 2. Convert those chars to the encoding of that font. + * 3. Measure converted chars. + */ - for (c = UCHAR(*p); ; ) { - newX += fontPtr->widths[c]; - if (newX > maxLength) { - break; + curX = 0; + end = source + numBytes; + for (p = source; p < end; ) { + next = p + Tcl_UtfToUniChar(p, &ch); + thisSubFontPtr = FindSubFontForChar(fontPtr, ch); + if (thisSubFontPtr != lastSubFontPtr) { + familyPtr = lastSubFontPtr->familyPtr; + Tcl_UtfToExternalDString(familyPtr->encoding, source, + p - source, &runString); + if (familyPtr->isTwoByteFont) { + curX += XTextWidth16(lastSubFontPtr->fontStructPtr, + (XChar2b *) Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) / 2); + } else { + curX += XTextWidth(lastSubFontPtr->fontStructPtr, + Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString)); + } + Tcl_DStringFree(&runString); + lastSubFontPtr = thisSubFontPtr; + source = p; + } + p = next; } - curX = newX; - numChars--; - p++; - if (numChars == 0) { - term = p; - termX = curX; - break; + familyPtr = lastSubFontPtr->familyPtr; + Tcl_UtfToExternalDString(familyPtr->encoding, source, p - source, + &runString); + if (familyPtr->isTwoByteFont) { + curX += XTextWidth16(lastSubFontPtr->fontStructPtr, + (XChar2b *) Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) >> 1); + } else { + curX += XTextWidth(lastSubFontPtr->fontStructPtr, + Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString)); } + Tcl_DStringFree(&runString); + curByte = numBytes; + } else { + CONST char *p, *end, *next, *term; + int newX, termX, sawNonSpace, dstWrote; + Tcl_UniChar ch; + FontFamily *familyPtr; + char buf[16]; + + /* + * How many chars will fit in the space allotted? + * This first version may be inefficient because it measures + * every character individually. + */ - c = UCHAR(*p); - if (isspace(c)) { - if (sawNonSpace) { - term = p; + next = source + Tcl_UtfToUniChar(source, &ch); + newX = curX = termX = 0; + + term = source; + end = source + numBytes; + + sawNonSpace = (ch > 255) || !isspace(ch); + familyPtr = lastSubFontPtr->familyPtr; + for (p = source; ; ) { + if ((ch < BASE_CHARS) && (fontPtr->widths[ch] != 0)) { + newX += fontPtr->widths[ch]; + } else { + lastSubFontPtr = FindSubFontForChar(fontPtr, ch); + familyPtr = lastSubFontPtr->familyPtr; + Tcl_UtfToExternal(NULL, familyPtr->encoding, p, next - p, + 0, NULL, buf, sizeof(buf), NULL, &dstWrote, NULL); + if (familyPtr->isTwoByteFont) { + newX += XTextWidth16(lastSubFontPtr->fontStructPtr, + (XChar2b *) buf, dstWrote >> 1); + } else { + newX += XTextWidth(lastSubFontPtr->fontStructPtr, buf, + dstWrote); + } + } + if (newX > maxLength) { + break; + } + curX = newX; + p = next; + if (p >= end) { + term = end; termX = curX; - sawNonSpace = 0; + break; } - } 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. - */ + next += Tcl_UtfToUniChar(next, &ch); + if ((ch < 256) && isspace(ch)) { + if (sawNonSpace) { + term = p; + termX = curX; + sawNonSpace = 0; + } + } else { + sawNonSpace = 1; + } + } - 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. + * P points to the first character that doesn't fit in the desired + * span. Use the flags to figure out what to return. */ - numChars--; - curX = newX; - p++; - } - if ((flags & TK_AT_LEAST_ONE) && (term == source) && (numChars > 0)) { - term = p; - termX = curX; - if (term == source) { - term++; - termX = newX; + if ((flags & TK_PARTIAL_OK) && (p < end) && (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. + */ + + curX = newX; + p += Tcl_UtfToUniChar(p, &ch); + } + if ((flags & TK_AT_LEAST_ONE) && (term == source) && (p < end)) { + term = p; + termX = curX; + if (term == source) { + term += Tcl_UtfToUniChar(term, &ch); + termX = newX; + } + } else if ((p >= end) || !(flags & TK_WHOLE_WORDS)) { + term = p; + termX = curX; } - } else if ((numChars == 0) || !(flags & TK_WHOLE_WORDS)) { - term = p; - termX = curX; + + curX = termX; + curByte = term - source; } - *lengthPtr = termX; - return term-source; + *lengthPtr = curX; + return curByte; } /* *--------------------------------------------------------------------------- * - * Tk_DrawChars, DrawChars -- + * Tk_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. + * expands control characters that occur in the string to + * \xNN sequences. * * Results: * None. @@ -691,255 +898,359 @@ Tk_MeasureChars(tkfont, source, numChars, maxLength, flags, lengthPtr) */ void -Tk_DrawChars(display, drawable, gc, tkfont, source, numChars, x, y) +Tk_DrawChars(display, drawable, gc, tkfont, source, numBytes, 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 + CONST char *source; /* UTF-8 string 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 numBytes; /* Number of bytes 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]; + SubFont *thisSubFontPtr, *lastSubFontPtr; + Tcl_DString runString; + CONST char *p, *end, *next; + int xStart, needWidth; + Tcl_UniChar ch; + FontFamily *familyPtr; 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)]; + lastSubFontPtr = &fontPtr->subFontArray[0]; + + xStart = x; + + end = source + numBytes; + for (p = source; p < end; ) { + next = p + Tcl_UtfToUniChar(p, &ch); + thisSubFontPtr = FindSubFontForChar(fontPtr, ch); + if (thisSubFontPtr != lastSubFontPtr) { + if (p > source) { + familyPtr = lastSubFontPtr->familyPtr; + Tcl_UtfToExternalDString(familyPtr->encoding, source, + p - source, &runString); + if (familyPtr->isTwoByteFont) { + XDrawString16(display, drawable, gc, x, y, + (XChar2b *) Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) / 2); + + x += XTextWidth16(lastSubFontPtr->fontStructPtr, + (XChar2b *) Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) / 2); + } else { + XDrawString(display, drawable, gc, x, y, + Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString)); + x += XTextWidth(lastSubFontPtr->fontStructPtr, + Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString)); + } + Tcl_DStringFree(&runString); } - source = p + 1; + lastSubFontPtr = thisSubFontPtr; + source = p; + XSetFont(display, gc, lastSubFontPtr->fontStructPtr->fid); } - p++; + p = next; } - 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. */ -{ - /* - * Perform a quick sanity check to ensure we won't overflow the X - * coordinate space. - */ - - if ((x + (fontPtr->fontStructPtr->max_bounds.width * numChars) > 0x7fff)) { - int length; - - /* - * The string we are being asked to draw is too big and would overflow - * the X coordinate space. Unfortunatley X servers aren't too bright - * and so they won't deal with this case cleanly. We need to truncate - * the string before sending it to X. - */ - - numChars = Tk_MeasureChars((Tk_Font) fontPtr, source, numChars, - 0x7fff - x, 0, &length); + needWidth = fontPtr->font.fa.underline + fontPtr->font.fa.overstrike; + if (p > source) { + familyPtr = lastSubFontPtr->familyPtr; + Tcl_UtfToExternalDString(familyPtr->encoding, source, p - source, + &runString); + if (familyPtr->isTwoByteFont) { + XDrawString16(display, drawable, gc, x, y, + (XChar2b *) Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) >> 1); + if (needWidth) { + x += XTextWidth16(lastSubFontPtr->fontStructPtr, + (XChar2b *) Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) >> 1); + } + } else { + XDrawString(display, drawable, gc, x, y, + Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString)); + if (needWidth) { + x += XTextWidth(lastSubFontPtr->fontStructPtr, + Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString)); + } + } + Tcl_DStringFree(&runString); } - XDrawString(display, drawable, gc, x, y, source, numChars); + if (lastSubFontPtr != &fontPtr->subFontArray[0]) { + XSetFont(display, gc, fontPtr->subFontArray[0].fontStructPtr->fid); + } if (fontPtr->font.fa.underline != 0) { - XFillRectangle(display, drawable, gc, x, + XFillRectangle(display, drawable, gc, xStart, y + fontPtr->underlinePos, - (unsigned) XTextWidth(fontPtr->fontStructPtr, source, numChars), - (unsigned) fontPtr->barHeight); + (unsigned) (x - xStart), (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); + XFillRectangle(display, drawable, gc, xStart, y, + (unsigned) (x - xStart), (unsigned) fontPtr->barHeight); } } /* - *--------------------------------------------------------------------------- + *------------------------------------------------------------------------- * - * AllocFont -- + * CreateClosestFont -- * * Helper for TkpGetNativeFont() and TkpGetFontFromAttributes(). - * Allocates and intializes the memory for a new TkFont that - * wraps the platform-specific data. + * Given a set of font attributes, construct a close XFontStruct. + * If requested face name is not available, automatically + * substitutes an alias for requested face name. If encoding is + * not specified (or the requested one is not available), + * automatically chooses another encoding from the list of + * preferred encodings. If the foundry is not specified (or + * is not available) automatically prefers "adobe" foundry. + * For all other attributes, if the requested value was not + * available, the appropriate "close" value will be used. * * 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(). + * Return value is the XFontStruct that best matched the + * requested attributes. The return value is never NULL; some + * font will always be returned. * * Side effects: - * Memory allocated. + * None. * - *--------------------------------------------------------------------------- - */ + *------------------------------------------------------------------------- + */ -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. */ +static XFontStruct * +CreateClosestFont(tkwin, faPtr, xaPtr) 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. */ + CONST TkFontAttributes *faPtr; + /* Set of generic attributes to match. */ + CONST TkXLFDAttributes *xaPtr; + /* Set of X-specific attributes to match. */ { - 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)); + FontAttributes want; + char **nameList; + int numNames, nameIdx; + Display *display; + XFontStruct *fontStructPtr; + int bestIdx[2]; + unsigned int bestScore[2]; + + want.fa = *faPtr; + want.xa = *xaPtr; + + if (want.xa.foundry == NULL) { + want.xa.foundry = Tk_GetUid("adobe"); + } + if (want.fa.family == NULL) { + want.fa.family = Tk_GetUid("fixed"); + } + want.fa.size = -TkFontGetPixels(tkwin, faPtr->size); + if (want.xa.charset == NULL || *want.xa.charset == '\0') { + want.xa.charset = Tk_GetUid("iso8859-1"); /* locale. */ } + display = Tk_Display(tkwin); + /* - * Encapsulate the generic stuff in the TkFont. + * Algorithm to get the closest font to the name requested. + * + * try fontname + * try all aliases for fontname + * foreach fallback for fontname + * try the fallback + * try all aliases for the fallback */ - 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; + nameList = ListFontOrAlias(display, want.fa.family, &numNames); + if (numNames == 0) { + char ***fontFallbacks; + int i, j; + char *fallback; + + fontFallbacks = TkFontGetFallbacks(); + for (i = 0; fontFallbacks[i] != NULL; i++) { + for (j = 0; (fallback = fontFallbacks[i][j]) != NULL; j++) { + if (strcasecmp(want.fa.family, fallback) == 0) { + break; + } + } + if (fallback != NULL) { + for (j = 0; (fallback = fontFallbacks[i][j]) != NULL; j++) { + nameList = ListFontOrAlias(display, fallback, &numNames); + if (numNames != 0) { + goto found; + } + } + } + } + nameList = ListFonts(display, "fixed", &numNames); + if (numNames == 0) { + nameList = ListFonts(display, "*", &numNames); + } + if (numNames == 0) { + return GetSystemFont(display); } } - 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; + found: + bestIdx[0] = -1; + bestIdx[1] = -1; + bestScore[0] = (unsigned int) -1; + bestScore[1] = (unsigned int) -1; + for (nameIdx = 0; nameIdx < numNames; nameIdx++) { + FontAttributes got; + int scalable; + unsigned int score; + + if (TkFontParseXLFD(nameList[nameIdx], &got.fa, &got.xa) != TCL_OK) { + continue; + } + IdentifySymbolEncodings(&got); + scalable = (got.fa.size == 0); + score = RankAttributes(&want, &got); + if (score <= bestScore[scalable]) { + bestIdx[scalable] = nameIdx; + bestScore[scalable] = score; + } + if (score == 0) { + break; + } } - 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; + fontStructPtr = GetScreenFont(display, &want, nameList, bestIdx, bestScore); + XFreeFontNames(nameList); + + if (fontStructPtr == NULL) { + return GetSystemFont(display); } - - 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; + return fontStructPtr; +} + +/* + *--------------------------------------------------------------------------- + * + * InitFont -- + * + * Helper for TkpGetNativeFont() and TkpGetFontFromAttributes(). + * Initializes the memory for a new UnixFont that wraps the + * platform-specific data. + * + * 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(). + * + * Results: + * Fills the WinFont structure. + * + * Side effects: + * Memory allocated. + * + *--------------------------------------------------------------------------- + */ + +static void +InitFont(tkwin, fontStructPtr, fontPtr) + Tk_Window tkwin; /* For screen where font will be used. */ + XFontStruct *fontStructPtr; /* X information about font. */ + UnixFont *fontPtr; /* Filled with information constructed from + * the above arguments. */ +{ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + unsigned long value; + int minHi, maxHi, minLo, maxLo, fixed, width, limit, i, n; + FontAttributes fa; + TkFontAttributes *faPtr; + TkFontMetrics *fmPtr; + SubFont *controlPtr, *subFontPtr; + char *pageMap; + Display *display; /* - * Classify the characters. + * Get all font attributes and metrics. */ - - 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; + + display = Tk_Display(tkwin); + GetFontAttributes(display, fontStructPtr, &fa); + + minHi = fontStructPtr->min_byte1; + maxHi = fontStructPtr->max_byte1; + minLo = fontStructPtr->min_char_or_byte2; + maxLo = fontStructPtr->max_char_or_byte2; + + fixed = 1; + if (fontStructPtr->per_char != NULL) { + width = 0; + limit = (maxHi - minHi + 1) * (maxLo - minLo + 1); + for (i = 0; i < limit; i++) { + n = fontStructPtr->per_char[i].width; + if (n != 0) { + if (width == 0) { + width = n; + } else if (width != n) { + fixed = 0; + break; + } + } } } - /* - * 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; + fontPtr->font.fid = fontStructPtr->fid; + + faPtr = &fontPtr->font.fa; + faPtr->family = fa.fa.family; + faPtr->size = TkFontGetPoints(tkwin, fa.fa.size); + faPtr->weight = fa.fa.weight; + faPtr->slant = fa.fa.slant; + faPtr->underline = 0; + faPtr->overstrike = 0; + + fmPtr = &fontPtr->font.fm; + fmPtr->ascent = fontStructPtr->ascent; + fmPtr->descent = fontStructPtr->descent; + fmPtr->maxWidth = fontStructPtr->max_bounds.width; + fmPtr->fixed = fixed; + + fontPtr->display = display; + fontPtr->pixelSize = TkFontGetPixels(tkwin, fa.fa.size); + fontPtr->xa = fa.xa; + + fontPtr->numSubFonts = 1; + fontPtr->subFontArray = fontPtr->staticSubFonts; + InitSubFont(display, fontStructPtr, 1, &fontPtr->subFontArray[0]); + + fontPtr->controlSubFont = fontPtr->subFontArray[0]; + subFontPtr = FindSubFontForChar(fontPtr, '0'); + controlPtr = &fontPtr->controlSubFont; + controlPtr->fontStructPtr = subFontPtr->fontStructPtr; + controlPtr->familyPtr = &tsdPtr->controlFamily; + controlPtr->fontMap = tsdPtr->controlFamily.fontMap; + + pageMap = fontPtr->subFontArray[0].fontMap[0]; for (i = 0; i < 256; i++) { - if (fontPtr->types[i] != NORMAL) { + if ((minHi > 0) || (i < minLo) || (i > maxLo) || + (((pageMap[i >> 3] >> (i & 7)) & 1) == 0)) { n = 0; } else if (fontStructPtr->per_char == NULL) { n = fontStructPtr->max_bounds.width; } else { - n = fontStructPtr->per_char[i - firstChar].width; + n = fontStructPtr->per_char[i - minLo].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; @@ -953,9 +1264,6 @@ AllocFont(tkFontPtr, tkwin, fontStructPtr, fontName) } 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) { @@ -984,23 +1292,627 @@ AllocFont(tkFontPtr, tkwin, fontStructPtr, fontName) fontPtr->barHeight = 1; } } +} + +/* + *------------------------------------------------------------------------- + * + * ReleaseFont -- + * + * Called to release the unix-specific contents of a TkFont. + * The caller is responsible for freeing the memory used by the + * font itself. + * + * Results: + * None. + * + * Side effects: + * Memory is freed. + * + *--------------------------------------------------------------------------- + */ + +static void +ReleaseFont(fontPtr) + UnixFont *fontPtr; /* The font to delete. */ +{ + int i; - return fontPtr; + for (i = 0; i < fontPtr->numSubFonts; i++) { + ReleaseSubFont(fontPtr->display, &fontPtr->subFontArray[i]); + } + if (fontPtr->subFontArray != fontPtr->staticSubFonts) { + ckfree((char *) fontPtr->subFontArray); + } } /* + *------------------------------------------------------------------------- + * + * InitSubFont -- + * + * Wrap a screen font and load the FontFamily that represents + * it. Used to prepare a SubFont so that characters can be mapped + * from UTF-8 to the charset of the font. + * + * Results: + * The subFontPtr is filled with information about the font. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ + +static void +InitSubFont(display, fontStructPtr, base, subFontPtr) + Display *display; /* Display in which font will be used. */ + XFontStruct *fontStructPtr; /* The screen font. */ + int base; /* Non-zero if this SubFont is being used + * as the base font for a font object. */ + SubFont *subFontPtr; /* Filled with SubFont constructed from + * above attributes. */ +{ + subFontPtr->fontStructPtr = fontStructPtr; + subFontPtr->familyPtr = AllocFontFamily(display, fontStructPtr, base); + subFontPtr->fontMap = subFontPtr->familyPtr->fontMap; +} + +/* + *------------------------------------------------------------------------- + * + * ReleaseSubFont -- + * + * Called to release the contents of a SubFont. The caller is + * responsible for freeing the memory used by the SubFont itself. + * + * Results: + * None. + * + * Side effects: + * Memory and resources are freed. + * *--------------------------------------------------------------------------- + */ + +static void +ReleaseSubFont(display, subFontPtr) + Display *display; /* Display which owns screen font. */ + SubFont *subFontPtr; /* The SubFont to delete. */ +{ + XFreeFont(display, subFontPtr->fontStructPtr); + FreeFontFamily(subFontPtr->familyPtr); +} + +/* + *------------------------------------------------------------------------- + * + * AllocFontFamily -- * - * GetControlCharSubst -- + * Find the FontFamily structure associated with the given font + * name. The information should be stored by the caller in a + * SubFont and used when determining if that SubFont supports a + * character. * - * 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. + * Cannot use the string name used to construct the font as the + * key, because the capitalization may not be canonical. Therefore + * use the face name actually retrieved from the font metrics as + * the key. * * Results: - * The return value is the length of the substitute string. buf is - * filled with the substitute string; it is not '\0' terminated. + * A pointer to a FontFamily. The reference count in the FontFamily + * is automatically incremented. When the SubFont is released, the + * reference count is decremented. When no SubFont is using this + * FontFamily, it may be deleted. + * + * Side effects: + * A new FontFamily structure will be allocated if this font family + * has not been seen. TrueType character existence metrics are + * loaded into the FontFamily structure. + * + *------------------------------------------------------------------------- + */ + +static FontFamily * +AllocFontFamily(display, fontStructPtr, base) + Display *display; /* Display in which font will be used. */ + XFontStruct *fontStructPtr; /* Screen font whose FontFamily is to be + * returned. */ + int base; /* Non-zero if this font family is to be + * used in the base font of a font object. */ +{ + FontFamily *familyPtr; + FontAttributes fa; + Tcl_Encoding encoding; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + GetFontAttributes(display, fontStructPtr, &fa); + encoding = Tcl_GetEncoding(NULL, GetEncodingAlias(fa.xa.charset)); + + familyPtr = tsdPtr->fontFamilyList; + for (; familyPtr != NULL; familyPtr = familyPtr->nextPtr) { + if ((familyPtr->faceName == fa.fa.family) + && (familyPtr->foundry == fa.xa.foundry) + && (familyPtr->encoding == encoding)) { + Tcl_FreeEncoding(encoding); + familyPtr->refCount++; + return familyPtr; + } + } + + familyPtr = (FontFamily *) ckalloc(sizeof(FontFamily)); + memset(familyPtr, 0, sizeof(FontFamily)); + familyPtr->nextPtr = tsdPtr->fontFamilyList; + tsdPtr->fontFamilyList = familyPtr; + + /* + * Set key for this FontFamily. + */ + + familyPtr->foundry = fa.xa.foundry; + familyPtr->faceName = fa.fa.family; + familyPtr->encoding = encoding; + + /* + * An initial refCount of 2 means that FontFamily information will + * persist even when the SubFont that loaded the FontFamily is released. + * Change it to 1 to cause FontFamilies to be unloaded when not in use. + */ + + familyPtr->refCount = 2; + familyPtr->isTwoByteFont = (fontStructPtr->min_byte1 > 0); + return familyPtr; +} + +/* + *------------------------------------------------------------------------- + * + * FreeFontFamily -- + * + * Called to free an FontFamily when the SubFont is finished using + * it. Frees the contents of the FontFamily and the memory used by + * the FontFamily itself. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ + +static void +FreeFontFamily(familyPtr) + FontFamily *familyPtr; /* The FontFamily to delete. */ +{ + FontFamily **familyPtrPtr; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + int i; + + if (familyPtr == NULL) { + return; + } + familyPtr->refCount--; + if (familyPtr->refCount > 0) { + return; + } + Tcl_FreeEncoding(familyPtr->encoding); + for (i = 0; i < FONTMAP_PAGES; i++) { + if (familyPtr->fontMap[i] != NULL) { + ckfree(familyPtr->fontMap[i]); + } + } + + /* + * Delete from list. + */ + + for (familyPtrPtr = &tsdPtr->fontFamilyList; ; ) { + if (*familyPtrPtr == familyPtr) { + *familyPtrPtr = familyPtr->nextPtr; + break; + } + familyPtrPtr = &(*familyPtrPtr)->nextPtr; + } + + ckfree((char *) familyPtr); +} + +/* + *------------------------------------------------------------------------- + * + * FindSubFontForChar -- + * + * Determine which screen font is necessary to use to + * display the given character. If the font object does not have + * a screen font that can display the character, another screen font + * may be loaded into the font object, following a set of preferred + * fallback rules. + * + * Results: + * The return value is the SubFont to use to display the given + * character. + * + * Side effects: + * The contents of fontPtr are modified to cache the results + * of the lookup and remember any SubFonts that were dynamically + * loaded. + * + *------------------------------------------------------------------------- + */ + +static SubFont * +FindSubFontForChar(fontPtr, ch) + UnixFont *fontPtr; /* The font object with which the character + * will be displayed. */ + int ch; /* The Unicode character to be displayed. */ +{ + int i, j, k, numNames; + char *faceName, *fallback; + char **aliases, **nameList, **anyFallbacks; + char ***fontFallbacks; + SubFont *subFontPtr; + Tcl_DString ds; + + if (FontMapLookup(&fontPtr->subFontArray[0], ch)) { + return &fontPtr->subFontArray[0]; + } + + for (i = 1; i < fontPtr->numSubFonts; i++) { + if (FontMapLookup(&fontPtr->subFontArray[i], ch)) { + return &fontPtr->subFontArray[i]; + } + } + + if (FontMapLookup(&fontPtr->controlSubFont, ch)) { + return &fontPtr->controlSubFont; + } + + /* + * Keep track of all face names that we check, so we don't check some + * name multiple times if it can be reached by multiple paths. + */ + + Tcl_DStringInit(&ds); + + /* + * Are there any other fonts with the same face name as the base + * font that could display this character, e.g., if the base font + * is adobe:fixed:iso8859-1, we could might be able to use + * misc:fixed:iso8859-8 or sony:fixed:jisx0208.1983-0 + */ + + faceName = fontPtr->font.fa.family; + if (SeenName(faceName, &ds) == 0) { + subFontPtr = CanUseFallback(fontPtr, faceName, ch); + if (subFontPtr != NULL) { + goto end; + } + } + + aliases = TkFontGetAliasList(faceName); + + subFontPtr = NULL; + fontFallbacks = TkFontGetFallbacks(); + for (i = 0; fontFallbacks[i] != NULL; i++) { + for (j = 0; (fallback = fontFallbacks[i][j]) != NULL; j++) { + if (strcasecmp(fallback, faceName) == 0) { + /* + * If the base font has a fallback... + */ + + goto tryfallbacks; + } else if (aliases != NULL) { + /* + * Or if an alias for the base font has a fallback... + */ + + for (k = 0; aliases[k] != NULL; k++) { + if (strcasecmp(fallback, aliases[k]) == 0) { + goto tryfallbacks; + } + } + } + } + continue; + + tryfallbacks: + + /* + * ...then see if we can use one of the fallbacks, or an + * alias for one of the fallbacks. + */ + + for (j = 0; (fallback = fontFallbacks[i][j]) != NULL; j++) { + subFontPtr = CanUseFallbackWithAliases(fontPtr, fallback, ch, &ds); + if (subFontPtr != NULL) { + goto end; + } + } + } + + /* + * See if we can use something from the global fallback list. + */ + + anyFallbacks = TkFontGetGlobalClass(); + for (i = 0; (fallback = anyFallbacks[i]) != NULL; i++) { + subFontPtr = CanUseFallbackWithAliases(fontPtr, fallback, ch, &ds); + if (subFontPtr != NULL) { + goto end; + } + } + + /* + * Try all face names available in the whole system until we + * find one that can be used. + */ + + nameList = ListFonts(fontPtr->display, "*", &numNames); + for (i = 0; i < numNames; i++) { + fallback = strchr(nameList[i] + 1, '-') + 1; + strchr(fallback, '-')[0] = '\0'; + if (SeenName(fallback, &ds) == 0) { + subFontPtr = CanUseFallback(fontPtr, fallback, ch); + if (subFontPtr != NULL) { + XFreeFontNames(nameList); + goto end; + } + } + } + XFreeFontNames(nameList); + + end: + Tcl_DStringFree(&ds); + + if (subFontPtr == NULL) { + /* + * No font can display this character, so it will be displayed as a + * control character expansion. + */ + + subFontPtr = &fontPtr->controlSubFont; + FontMapInsert(subFontPtr, ch); + } + return subFontPtr; +} + +/* + *------------------------------------------------------------------------- + * + * FontMapLookup -- + * + * See if the screen font can display the given character. + * + * Results: + * The return value is 0 if the screen font cannot display the + * character, non-zero otherwise. + * + * Side effects: + * New pages are added to the font mapping cache whenever the + * character belongs to a page that hasn't been seen before. + * When a page is loaded, information about all the characters on + * that page is stored, not just for the single character in + * question. + * + *------------------------------------------------------------------------- + */ + +static int +FontMapLookup(subFontPtr, ch) + SubFont *subFontPtr; /* Contains font mapping cache to be queried + * and possibly updated. */ + int ch; /* Character to be tested. */ +{ + int row, bitOffset; + + row = ch >> FONTMAP_SHIFT; + if (subFontPtr->fontMap[row] == NULL) { + FontMapLoadPage(subFontPtr, row); + } + bitOffset = ch & (FONTMAP_BITSPERPAGE - 1); + return (subFontPtr->fontMap[row][bitOffset >> 3] >> (bitOffset & 7)) & 1; +} + +/* + *------------------------------------------------------------------------- + * + * FontMapInsert -- + * + * Tell the font mapping cache that the given screen font should be + * used to display the specified character. This is called when no + * font on the system can be be found that can display that + * character; we lie to the font and tell it that it can display + * the character, otherwise we would end up re-searching the entire + * fallback hierarchy every time that character was seen. + * + * Results: + * None. + * + * Side effects: + * New pages are added to the font mapping cache whenever the + * character belongs to a page that hasn't been seen before. + * When a page is loaded, information about all the characters on + * that page is stored, not just for the single character in + * question. + * + *------------------------------------------------------------------------- + */ + +static void +FontMapInsert(subFontPtr, ch) + SubFont *subFontPtr; /* Contains font mapping cache to be + * updated. */ + int ch; /* Character to be added to cache. */ +{ + int row, bitOffset; + + row = ch >> FONTMAP_SHIFT; + if (subFontPtr->fontMap[row] == NULL) { + FontMapLoadPage(subFontPtr, row); + } + bitOffset = ch & (FONTMAP_BITSPERPAGE - 1); + subFontPtr->fontMap[row][bitOffset >> 3] |= 1 << (bitOffset & 7); +} + +/* + *------------------------------------------------------------------------- + * + * FontMapLoadPage -- + * + * Load information about all the characters on a given page. + * This information consists of one bit per character that indicates + * whether the associated screen font can (1) or cannot (0) display + * the characters on the page. + * + * Results: + * None. + * + * Side effects: + * Mempry allocated. + * + *------------------------------------------------------------------------- + */ +static void +FontMapLoadPage(subFontPtr, row) + SubFont *subFontPtr; /* Contains font mapping cache to be + * updated. */ + int row; /* Index of the page to be loaded into + * the cache. */ +{ + char src[TCL_UTF_MAX], buf[16]; + int minHi, maxHi, minLo, maxLo, scale, checkLo; + int i, end, bitOffset, isTwoByteFont, n; + Tcl_Encoding encoding; + XFontStruct *fontStructPtr; + XCharStruct *widths; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + subFontPtr->fontMap[row] = (char *) ckalloc(FONTMAP_BITSPERPAGE / 8); + memset(subFontPtr->fontMap[row], 0, FONTMAP_BITSPERPAGE / 8); + + if (subFontPtr->familyPtr == &tsdPtr->controlFamily) { + return; + } + + fontStructPtr = subFontPtr->fontStructPtr; + encoding = subFontPtr->familyPtr->encoding; + isTwoByteFont = subFontPtr->familyPtr->isTwoByteFont; + + widths = fontStructPtr->per_char; + minHi = fontStructPtr->min_byte1; + maxHi = fontStructPtr->max_byte1; + minLo = fontStructPtr->min_char_or_byte2; + maxLo = fontStructPtr->max_char_or_byte2; + scale = maxLo - minLo + 1; + checkLo = minLo; + + if (! isTwoByteFont) { + if (minLo < 32) { + checkLo = 32; + } + } + + end = (row + 1) << FONTMAP_SHIFT; + for (i = row << FONTMAP_SHIFT; i < end; i++) { + int hi, lo; + + if (Tcl_UtfToExternal(NULL, encoding, src, Tcl_UniCharToUtf(i, src), + TCL_ENCODING_STOPONERROR, NULL, buf, sizeof(buf), NULL, + NULL, NULL) != TCL_OK) { + continue; + } + if (isTwoByteFont) { + hi = ((unsigned char *) buf)[0]; + lo = ((unsigned char *) buf)[1]; + } else { + hi = 0; + lo = ((unsigned char *) buf)[0]; + } + if ((hi < minHi) || (hi > maxHi) || (lo < checkLo) || (lo > maxLo)) { + continue; + } + n = (hi - minHi) * scale + lo - minLo; + if ((widths == NULL) || ((widths[n].width + widths[n].rbearing) != 0)) { + bitOffset = i & (FONTMAP_BITSPERPAGE - 1); + subFontPtr->fontMap[row][bitOffset >> 3] |= 1 << (bitOffset & 7); + } + } +} + +/* + *--------------------------------------------------------------------------- + * + * CanUseFallbackWithAliases -- + * + * Helper function for FindSubFontForChar. Determine if the + * specified face name (or an alias of the specified face name) + * can be used to construct a screen font that can display the + * given character. + * + * Results: + * See CanUseFallback(). + * + * Side effects: + * If the name and/or one of its aliases was rejected, the + * rejected string is recorded in nameTriedPtr so that it won't + * be tried again. + * + *--------------------------------------------------------------------------- + */ + +static SubFont * +CanUseFallbackWithAliases(fontPtr, faceName, ch, nameTriedPtr) + UnixFont *fontPtr; /* The font object that will own the new + * screen font. */ + char *faceName; /* Desired face name for new screen font. */ + int ch; /* The Unicode character that the new + * screen font must be able to display. */ + Tcl_DString *nameTriedPtr; /* Records face names that have already + * been tried. It is possible for the same + * face name to be queried multiple times when + * trying to find a suitable screen font. */ +{ + SubFont *subFontPtr; + char **aliases; + int i; + + if (SeenName(faceName, nameTriedPtr) == 0) { + subFontPtr = CanUseFallback(fontPtr, faceName, ch); + if (subFontPtr != NULL) { + return subFontPtr; + } + } + aliases = TkFontGetAliasList(faceName); + if (aliases != NULL) { + for (i = 0; aliases[i] != NULL; i++) { + if (SeenName(aliases[i], nameTriedPtr) == 0) { + subFontPtr = CanUseFallback(fontPtr, aliases[i], ch); + if (subFontPtr != NULL) { + return subFontPtr; + } + } + } + } + return NULL; +} + +/* + *--------------------------------------------------------------------------- + * + * SeenName -- + * + * Used to determine we have already tried and rejected the given + * face name when looking for a screen font that can support some + * Unicode character. + * + * Results: + * The return value is 0 if this face name has not already been seen, + * non-zero otherwise. * * Side effects: * None. @@ -1009,19 +1921,663 @@ AllocFont(tkFontPtr, tkwin, fontStructPtr, fontName) */ 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. */ +SeenName(name, dsPtr) + CONST char *name; /* The name to check. */ + Tcl_DString *dsPtr; /* Contains names that have already been + * seen. */ +{ + CONST char *seen, *end; + + seen = Tcl_DStringValue(dsPtr); + end = seen + Tcl_DStringLength(dsPtr); + while (seen < end) { + if (strcasecmp(seen, name) == 0) { + return 1; + } + seen += strlen(seen) + 1; + } + Tcl_DStringAppend(dsPtr, (char *) name, (int) (strlen(name) + 1)); + return 0; +} + +/* + *------------------------------------------------------------------------- + * + * CanUseFallback -- + * + * If the specified screen font has not already been loaded + * into the font object, determine if the specified screen + * font can display the given character. + * + * Results: + * The return value is a pointer to a newly allocated SubFont, + * owned by the font object. This SubFont can be used to display + * the given character. The SubFont represents the screen font + * with the base set of font attributes from the font object, but + * using the specified face name. NULL is returned if the font + * object already holds a reference to the specified font or if + * the specified font doesn't exist or cannot display the given + * character. + * + * Side effects: + * The font object's subFontArray is updated to contain a reference + * to the newly allocated SubFont. + * + *------------------------------------------------------------------------- + */ + +static SubFont * +CanUseFallback(fontPtr, faceName, ch) + UnixFont *fontPtr; /* The font object that will own the new + * screen font. */ + char *faceName; /* Desired face name for new screen font. */ + int ch; /* The Unicode character that the new + * screen font must be able to display. */ { - buf[0] = '\\'; - if ((c < sizeof(mapChars)) && (mapChars[c] != 0)) { - buf[1] = mapChars[c]; - return 2; + int i, nameIdx, numNames, srcLen; + Tk_Uid hateFoundry; + int bestIdx[2]; + CONST char *charset, *hateCharset; + unsigned int bestScore[2]; + char **nameList, **nameListOrig; + FontAttributes want, got; + char src[TCL_UTF_MAX]; + Display *display; + SubFont subFont; + XFontStruct *fontStructPtr; + Tcl_DString dsEncodings; + int numEncodings; + Tcl_Encoding *encodingCachePtr; + + /* + * Assume: the face name is times. + * Assume: adobe:times:iso8859-1 has already been used. + * + * Are there any versions of times that can display this + * character (e.g., perhaps linotype:times:iso8859-2)? + * a. Get list of all times fonts. + * b1. Cross out all names whose encodings we've already used. + * b2. Cross out all names whose foundry & encoding we've already seen. + * c. Cross out all names whose encoding cannot handle the character. + * d. Rank each name and pick the best match. + * e. If that font cannot actually display the character, cross + * out all names with the same foundry and encoding and go + * back to (c). + */ + + display = fontPtr->display; + nameList = ListFonts(display, faceName, &numNames); + if (numNames == 0) { + return NULL; + } + nameListOrig = nameList; + + srcLen = Tcl_UniCharToUtf(ch, src); + + want.fa = fontPtr->font.fa; + want.xa = fontPtr->xa; + + want.fa.family = Tk_GetUid(faceName); + want.fa.size = -fontPtr->pixelSize; + + hateFoundry = NULL; + hateCharset = NULL; + numEncodings = 0; + Tcl_DStringInit(&dsEncodings); + + charset = NULL; /* lint, since numNames must be > 0 to get here. */ + + retry: + bestIdx[0] = -1; + bestIdx[1] = -1; + bestScore[0] = (unsigned int) -1; + bestScore[1] = (unsigned int) -1; + for (nameIdx = 0; nameIdx < numNames; nameIdx++) { + Tcl_Encoding encoding; + char dst[16]; + int scalable, srcRead, dstWrote; + unsigned int score; + + if (nameList[nameIdx] == NULL) { + continue; + } + if (TkFontParseXLFD(nameList[nameIdx], &got.fa, &got.xa) != TCL_OK) { + goto crossout; + } + IdentifySymbolEncodings(&got); + charset = GetEncodingAlias(got.xa.charset); + if (hateFoundry != NULL) { + /* + * E. If the font we picked cannot actually display the + * character, cross out all names with the same foundry and + * encoding. + */ + + if ((hateFoundry == got.xa.foundry) + && (strcmp(hateCharset, charset) == 0)) { + goto crossout; + } + } else { + /* + * B. Cross out all names whose encodings we've already used. + */ + + for (i = 0; i < fontPtr->numSubFonts; i++) { + encoding = fontPtr->subFontArray[i].familyPtr->encoding; + if (strcmp(charset, Tcl_GetEncodingName(encoding)) == 0) { + goto crossout; + } + } + } + + /* + * C. Cross out all names whose encoding cannot handle the character. + */ + + encodingCachePtr = (Tcl_Encoding *) Tcl_DStringValue(&dsEncodings); + for (i = numEncodings; --i >= 0; encodingCachePtr++) { + encoding = *encodingCachePtr; + if (strcmp(Tcl_GetEncodingName(encoding), charset) == 0) { + break; + } + } + if (i < 0) { + encoding = Tcl_GetEncoding(NULL, charset); + if (encoding == NULL) { + goto crossout; + } + + Tcl_DStringAppend(&dsEncodings, (char *) &encoding, + sizeof(encoding)); + numEncodings++; + } + Tcl_UtfToExternal(NULL, encoding, src, srcLen, + TCL_ENCODING_STOPONERROR, NULL, dst, sizeof(dst), &srcRead, + &dstWrote, NULL); + if (dstWrote == 0) { + goto crossout; + } + + /* + * D. Rank each name and pick the best match. + */ + + scalable = (got.fa.size == 0); + score = RankAttributes(&want, &got); + if (score <= bestScore[scalable]) { + bestIdx[scalable] = nameIdx; + bestScore[scalable] = score; + } + if (score == 0) { + break; + } + continue; + + crossout: + if (nameList == nameListOrig) { + /* + * Not allowed to change pointers to memory that X gives you, + * so make a copy. + */ + + nameList = (char **) ckalloc(numNames * sizeof(char *)); + memcpy(nameList, nameListOrig, numNames * sizeof(char *)); + } + nameList[nameIdx] = NULL; + } + + fontStructPtr = GetScreenFont(display, &want, nameList, bestIdx, bestScore); + + encodingCachePtr = (Tcl_Encoding *) Tcl_DStringValue(&dsEncodings); + for (i = numEncodings; --i >= 0; encodingCachePtr++) { + Tcl_FreeEncoding(*encodingCachePtr); + } + Tcl_DStringFree(&dsEncodings); + numEncodings = 0; + + if (fontStructPtr == NULL) { + if (nameList != nameListOrig) { + ckfree((char *) nameList); + } + XFreeFontNames(nameListOrig); + return NULL; + } + + InitSubFont(display, fontStructPtr, 0, &subFont); + if (FontMapLookup(&subFont, ch) == 0) { + /* + * E. If the font we picked cannot actually display the character, + * cross out all names with the same foundry and encoding and pick + * another font. + */ + + hateFoundry = got.xa.foundry; + hateCharset = charset; + ReleaseSubFont(display, &subFont); + goto retry; + } + if (nameList != nameListOrig) { + ckfree((char *) nameList); + } + XFreeFontNames(nameListOrig); + + if (fontPtr->numSubFonts >= SUBFONT_SPACE) { + SubFont *newPtr; + + newPtr = (SubFont *) ckalloc(sizeof(SubFont) * (fontPtr->numSubFonts + 1)); + memcpy((char *) newPtr, fontPtr->subFontArray, + fontPtr->numSubFonts * sizeof(SubFont)); + if (fontPtr->subFontArray != fontPtr->staticSubFonts) { + ckfree((char *) fontPtr->subFontArray); + } + fontPtr->subFontArray = newPtr; + } + fontPtr->subFontArray[fontPtr->numSubFonts] = subFont; + fontPtr->numSubFonts++; + return &fontPtr->subFontArray[fontPtr->numSubFonts - 1]; +} + +/* + *--------------------------------------------------------------------------- + * + * RankAttributes -- + * + * Determine how close the attributes of the font in question match + * the attributes that we want. + * + * Results: + * The return value is the score; lower numbers are better. + * *scalablePtr is set to 0 if the font was not scalable, 1 otherwise. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static unsigned int +RankAttributes(wantPtr, gotPtr) + FontAttributes *wantPtr; /* The desired attributes. */ + FontAttributes *gotPtr; /* The attributes we have to live with. */ +{ + unsigned int penalty; + + penalty = 0; + if (gotPtr->xa.foundry != wantPtr->xa.foundry) { + penalty += 4500; + } + if (gotPtr->fa.family != wantPtr->fa.family) { + penalty += 9000; + } + if (gotPtr->fa.weight != wantPtr->fa.weight) { + penalty += 90; + } + if (gotPtr->fa.slant != wantPtr->fa.slant) { + penalty += 60; + } + if (gotPtr->xa.slant != wantPtr->xa.slant) { + penalty += 10; + } + if (gotPtr->xa.setwidth != wantPtr->xa.setwidth) { + penalty += 1000; + } + + if (gotPtr->fa.size == 0) { + /* + * A scalable font is almost always acceptable, but the + * corresponding bitmapped font would be better. + */ + + penalty += 10; } else { - buf[1] = 'x'; - buf[2] = hexChars[(c >> 4) & 0xf]; - buf[3] = hexChars[c & 0xf]; - return 4; + int diff; + + /* + * It's worse to be too large than to be too small. + */ + + diff = (-gotPtr->fa.size - -wantPtr->fa.size); + if (diff > 0) { + penalty += 600; + } else if (diff < 0) { + penalty += 150; + diff = -diff; + } + penalty += 150 * diff; } + if (gotPtr->xa.charset != wantPtr->xa.charset) { + int i; + CONST char *gotAlias, *wantAlias; + + penalty += 65000; + gotAlias = GetEncodingAlias(gotPtr->xa.charset); + wantAlias = GetEncodingAlias(wantPtr->xa.charset); + if (strcmp(gotAlias, wantAlias) != 0) { + penalty += 30000; + for (i = 0; encodingList[i] != NULL; i++) { + if (strcmp(gotAlias, encodingList[i]) == 0) { + penalty -= 30000; + break; + } + penalty += 20000; + } + } + } + return penalty; } + +/* + *--------------------------------------------------------------------------- + * + * GetScreenFont -- + * + * Given the names for the best scalable and best bitmapped font, + * actually construct an XFontStruct based on the best XLFD. + * This is where all the alias and fallback substitution bottoms + * out. + * + * Results: + * The screen font that best corresponds to the set of attributes. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static XFontStruct * +GetScreenFont(display, wantPtr, nameList, bestIdx, bestScore) + Display *display; /* Display for new XFontStruct. */ + FontAttributes *wantPtr; /* Contains desired actual pixel-size if the + * best font was scalable. */ + char **nameList; /* Array of XLFDs. */ + int bestIdx[2]; /* Indices into above array for XLFD of + * best bitmapped and best scalable font. */ + unsigned int bestScore[2]; /* Scores of best bitmapped and best + * scalable font. XLFD corresponding to + * lowest score will be constructed. */ +{ + XFontStruct *fontStructPtr; + + if ((bestIdx[0] < 0) && (bestIdx[1] < 0)) { + return NULL; + } + + /* + * Now we know which is the closest matching scalable font and the + * closest matching bitmapped font. If the scalable font was a + * better match, try getting the scalable font; however, if the + * scalable font was not actually available in the desired + * pointsize, fall back to the closest bitmapped font. + */ + + fontStructPtr = NULL; + if (bestScore[1] < bestScore[0]) { + char *str, *rest; + char buf[256]; + int i; + + /* + * Fill in the desired pixel size for this font. + */ + + tryscale: + str = nameList[bestIdx[1]]; + for (i = 0; i < XLFD_PIXEL_SIZE; i++) { + str = strchr(str + 1, '-'); + } + rest = str; + for (i = XLFD_PIXEL_SIZE; i < XLFD_CHARSET; i++) { + rest = strchr(rest + 1, '-'); + } + *str = '\0'; + sprintf(buf, "%.200s-%d-*-*-*-*-*%s", nameList[bestIdx[1]], + -wantPtr->fa.size, rest); + *str = '-'; + fontStructPtr = XLoadQueryFont(display, buf); + bestScore[1] = INT_MAX; + } + if (fontStructPtr == NULL) { + fontStructPtr = XLoadQueryFont(display, nameList[bestIdx[0]]); + 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 (bestScore[1] < INT_MAX) { + goto tryscale; + } + return GetSystemFont(display); + } + } + return fontStructPtr; +} + +/* + *--------------------------------------------------------------------------- + * + * GetSystemFont -- + * + * Absolute fallback mechanism, called when we need a font and no + * other font can be found and/or instantiated. + * + * Results: + * A pointer to a font. Never NULL. + * + * Side effects: + * If there are NO fonts installed on the system, this call will + * panic, but how did you get X running in that case? + * + *--------------------------------------------------------------------------- + */ + +static XFontStruct * +GetSystemFont(display) + Display *display; /* Display for new XFontStruct. */ +{ + XFontStruct *fontStructPtr; + + fontStructPtr = XLoadQueryFont(display, "fixed"); + if (fontStructPtr == NULL) { + fontStructPtr = XLoadQueryFont(display, "*"); + if (fontStructPtr == NULL) { + panic("TkpGetFontFromAttributes: cannot get any font"); + } + } + return fontStructPtr; +} + +/* + *--------------------------------------------------------------------------- + * + * GetFontAttributes -- + * + * Given a screen font, determine its actual attributes, which are + * not necessarily the attributes that were used to construct it. + * + * Results: + * *faPtr is filled with the screen font's attributes. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static int +GetFontAttributes(display, fontStructPtr, faPtr) + Display *display; /* Display that owns the screen font. */ + XFontStruct *fontStructPtr; /* Screen font to query. */ + FontAttributes *faPtr; /* For storing attributes of screen font. */ +{ + unsigned long value; + char *p, *name; + + if ((XGetFontProperty(fontStructPtr, XA_FONT, &value) != False) && + (value != 0)) { + name = XGetAtomName(display, (Atom) value); + for (p = name; *p != '\0'; p++) { + if (isupper(UCHAR(*p))) { /* INTL: native text */ + *p = tolower(UCHAR(*p)); /* INTL: native text */ + } + } + if (TkFontParseXLFD(name, &faPtr->fa, &faPtr->xa) != TCL_OK) { + faPtr->fa.family = Tk_GetUid(name); + faPtr->xa.foundry = Tk_GetUid(""); + faPtr->xa.charset = Tk_GetUid(""); + } + XFree(name); + } else { + TkInitFontAttributes(&faPtr->fa); + TkInitXLFDAttributes(&faPtr->xa); + faPtr->fa.family = Tk_GetUid(""); + faPtr->xa.foundry = Tk_GetUid(""); + faPtr->xa.charset = Tk_GetUid(""); + } + return IdentifySymbolEncodings(faPtr); +} + +/* + *--------------------------------------------------------------------------- + * + * ListFonts -- + * + * Utility function to return the array of all XLFDs on the system + * with the specified face name. + * + * Results: + * The return value is an array of XLFDs, which should be freed with + * XFreeFontNames(), or NULL if no XLFDs matched the requested name. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static char ** +ListFonts(display, faceName, numNamesPtr) + Display *display; /* Display to query. */ + CONST char *faceName; /* Desired face name, or "*" for all. */ + int *numNamesPtr; /* Filled with length of returned array, or + * 0 if no names were found. */ +{ + char buf[256]; + + sprintf(buf, "-*-%.80s-*-*-*-*-*-*-*-*-*-*-*-*", faceName); + return XListFonts(display, buf, 10000, numNamesPtr); +} + +static char ** +ListFontOrAlias(display, faceName, numNamesPtr) + Display *display; /* Display to query. */ + CONST char *faceName; /* Desired face name, or "*" for all. */ + int *numNamesPtr; /* Filled with length of returned array, or + * 0 if no names were found. */ +{ + char **nameList, **aliases; + int i; + + nameList = ListFonts(display, faceName, numNamesPtr); + if (nameList != NULL) { + return nameList; + } + aliases = TkFontGetAliasList(faceName); + if (aliases != NULL) { + for (i = 0; aliases[i] != NULL; i++) { + nameList = ListFonts(display, aliases[i], numNamesPtr); + if (nameList != NULL) { + return nameList; + } + } + } + *numNamesPtr = 0; + return NULL; +} + +/* + *--------------------------------------------------------------------------- + * + * IdentifySymbolEncodings -- + * + * If the font attributes refer to a symbol font, update the + * charset field of the font attributes so that it reflects the + * encoding of that symbol font. In general, the raw value for + * the charset field parsed from an XLFD is meaningless for symbol + * fonts. + * + * Symbol fonts are all fonts whose name appears in the symbolClass. + * + * Results: + * The return value is non-zero if the font attributes specify a + * symbol font, or 0 otherwise. If a non-zero value is returned + * the charset field of the font attributes will be changed to + * the string that represents the actual encoding for the symbol font. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static int +IdentifySymbolEncodings(faPtr) + FontAttributes *faPtr; +{ + int i, j; + char **aliases, **symbolClass; + + symbolClass = TkFontGetSymbolClass(); + for (i = 0; symbolClass[i] != NULL; i++) { + if (strcasecmp(faPtr->fa.family, symbolClass[i]) == 0) { + faPtr->xa.charset = Tk_GetUid(GetEncodingAlias(symbolClass[i])); + return 1; + } + aliases = TkFontGetAliasList(symbolClass[i]); + for (j = 0; (aliases != NULL) && (aliases[j] != NULL); j++) { + if (strcasecmp(faPtr->fa.family, aliases[j]) == 0) { + faPtr->xa.charset = Tk_GetUid(GetEncodingAlias(aliases[j])); + return 1; + } + } + } + return 0; +} + +/* + *--------------------------------------------------------------------------- + * + * GetEncodingAlias -- + * + * Map the name of an encoding to another name that should be used + * when actually loading the encoding. For instance, the encodings + * "jisc6226.1978", "jisx0208.1983", "jisx0208.1990", and + * "jisx0208.1996" are well-known names for the same encoding and + * are represented by one encoding table: "jis0208". + * + * Results: + * As above. If the name has no alias, the original name is returned. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static CONST char * +GetEncodingAlias(name) + CONST char *name; /* The name to look up. */ +{ + EncodingAlias *aliasPtr; + + for (aliasPtr = encodingAliases; aliasPtr->aliasPattern != NULL; ) { + if (Tcl_StringMatch((char *) name, aliasPtr->aliasPattern)) { + return aliasPtr->realName; + } + aliasPtr++; + } + return name; +} + + |