diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2018-11-26 20:08:41 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2018-11-26 20:08:41 (GMT) |
commit | 95844816a714456156ed31854b004d29c3e29dbe (patch) | |
tree | 337e2d0ee4f2fb1a31ffb141eccbb1cdf6d71a04 /tk8.6/unix/tkUnixFont.c | |
parent | 3dcee315fb784599a02aaafe3a83cfea0c1d1fe9 (diff) | |
download | blt-95844816a714456156ed31854b004d29c3e29dbe.zip blt-95844816a714456156ed31854b004d29c3e29dbe.tar.gz blt-95844816a714456156ed31854b004d29c3e29dbe.tar.bz2 |
update tcl/tk
Diffstat (limited to 'tk8.6/unix/tkUnixFont.c')
-rw-r--r-- | tk8.6/unix/tkUnixFont.c | 3334 |
1 files changed, 3334 insertions, 0 deletions
diff --git a/tk8.6/unix/tkUnixFont.c b/tk8.6/unix/tkUnixFont.c new file mode 100644 index 0000000..96635b4 --- /dev/null +++ b/tk8.6/unix/tkUnixFont.c @@ -0,0 +1,3334 @@ +/* + * tkUnixFont.c -- + * + * Contains the Unix implementation of the platform-independent font + * package interface. + * + * 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. + */ + +#include "tkUnixInt.h" +#include "tkFont.h" +#include <netinet/in.h> /* for htons() prototype */ +#include <arpa/inet.h> /* inet_ntoa() */ + +/* + * The preferred font encodings. + */ + +static const char *const 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 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. */ + 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 when drawing underlined font) + * (pixels). */ + int barHeight; /* Height of underline or overstrike bar (used + * when drawing underlined or strikeout font) + * (pixels). */ +} UnixFont; + +/* + * 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 { + const char *realName; /* The real name of the encoding to load if + * the provided name matched the pattern. */ + const char *aliasPattern; /* Pattern for encoding name, of the form that + * is acceptable to Tcl_StringMatch. */ +} EncodingAlias; + +/* + * Just some utility structures used for passing around values in helper + * functions. + */ + +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; + +/* + * 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-raw", "gb2312*"}, + {"big5", "big5*"}, + {"cns11643-1", "cns11643*-1"}, + {"cns11643-1", "cns11643*.1-0"}, + {"cns11643-2", "cns11643*-2"}, + {"cns11643-2", "cns11643*.2-0"}, + {"jis0201", "jisx0201*"}, + {"jis0201", "jisx0202*"}, + {"jis0208", "jisc6226*"}, + {"jis0208", "jisx0208*"}, + {"jis0212", "jisx0212*"}, + {"tis620", "tis620*"}, + {"ksc5601", "ksc5601*"}, + {"dingbats", "*dingbats"}, +#ifdef WORDS_BIGENDIAN + {"unicode", "iso10646-1"}, +#else + /* + * ucs-2be is needed if native order isn't BE. + */ + {"ucs-2be", "iso10646-1"}, +#endif + {NULL, NULL} +}; + +/* + * Functions used only in this file. + */ + +static void FontPkgCleanup(ClientData clientData); +static FontFamily * AllocFontFamily(Display *display, + XFontStruct *fontStructPtr, int base); +static SubFont * CanUseFallback(UnixFont *fontPtr, + const char *fallbackName, int ch, + SubFont **fixSubFontPtrPtr); +static SubFont * CanUseFallbackWithAliases(UnixFont *fontPtr, + const char *fallbackName, int ch, + Tcl_DString *nameTriedPtr, + SubFont **fixSubFontPtrPtr); +static int ControlUtfProc(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(Tk_Window tkwin, + const TkFontAttributes *faPtr, + const TkXLFDAttributes *xaPtr); +static SubFont * FindSubFontForChar(UnixFont *fontPtr, int ch, + SubFont **fixSubFontPtrPtr); +static void FontMapInsert(SubFont *subFontPtr, int ch); +static void FontMapLoadPage(SubFont *subFontPtr, int row); +static int FontMapLookup(SubFont *subFontPtr, int ch); +static void FreeFontFamily(FontFamily *afPtr); +static const char * GetEncodingAlias(const char *name); +static int GetFontAttributes(Display *display, + XFontStruct *fontStructPtr, FontAttributes *faPtr); +static XFontStruct * GetScreenFont(Display *display, + FontAttributes *wantPtr, char **nameList, + int bestIdx[], unsigned bestScore[]); +static XFontStruct * GetSystemFont(Display *display); +static int IdentifySymbolEncodings(FontAttributes *faPtr); +static void InitFont(Tk_Window tkwin, XFontStruct *fontStructPtr, + UnixFont *fontPtr); +static void InitSubFont(Display *display, + XFontStruct *fontStructPtr, int base, + SubFont *subFontPtr); +static char ** ListFonts(Display *display, const char *faceName, + int *numNamesPtr); +static char ** ListFontOrAlias(Display *display, const char*faceName, + int *numNamesPtr); +static unsigned RankAttributes(FontAttributes *wantPtr, + FontAttributes *gotPtr); +static void ReleaseFont(UnixFont *fontPtr); +static void ReleaseSubFont(Display *display, SubFont *subFontPtr); +static int SeenName(const char *name, Tcl_DString *dsPtr); +#ifndef WORDS_BIGENDIAN +static int Ucs2beToUtfProc(ClientData clientData, const char*src, + int srcLen, int flags, Tcl_EncodingState*statePtr, + char *dst, int dstLen, int *srcReadPtr, + int *dstWrotePtr, int *dstCharsPtr); +static int UtfToUcs2beProc(ClientData clientData, const char*src, + int srcLen, int flags, Tcl_EncodingState*statePtr, + char *dst, int dstLen, int *srcReadPtr, + int *dstWrotePtr, int *dstCharsPtr); +#endif + +/* + *------------------------------------------------------------------------- + * + * FontPkgCleanup -- + * + * This function 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: + * Releases thread-specific resources used by font pkg. + * + *------------------------------------------------------------------------- + */ + +static void +FontPkgCleanup( + ClientData clientData) +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (tsdPtr->controlFamily.encoding != NULL) { + FontFamily *familyPtr = &tsdPtr->controlFamily; + int i; + + Tcl_FreeEncoding(familyPtr->encoding); + for (i = 0; i < FONTMAP_PAGES; i++) { + if (familyPtr->fontMap[i] != NULL) { + ckfree(familyPtr->fontMap[i]); + } + } + tsdPtr->controlFamily.encoding = NULL; + } +} + +/* + *------------------------------------------------------------------------- + * + * TkpFontPkgInit -- + * + * This function 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. + * + *------------------------------------------------------------------------- + */ + +void +TkpFontPkgInit( + TkMainInfo *mainPtr) /* The application being created. */ +{ + ThreadSpecificData *tsdPtr = + 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); + } + +#ifndef WORDS_BIGENDIAN + /* + * UCS-2BE is unicode (UCS-2) in big-endian format. Define this if + * native order isn't BE. It is used in iso10646 fonts. + */ + + type.encodingName = "ucs-2be"; + type.toUtfProc = Ucs2beToUtfProc; + type.fromUtfProc = UtfToUcs2beProc; + type.freeProc = NULL; + type.clientData = NULL; + type.nullSize = 2; + Tcl_CreateEncoding(&type); +#endif + Tcl_CreateThreadExitHandler(FontPkgCleanup, NULL); + } +} + +/* + *------------------------------------------------------------------------- + * + * 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 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; + int ch, 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 += TkUtfToUniChar(src, &ch); + dst[0] = '\\'; + if (((size_t) 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 if (ch < 0x10000) { + 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; + } else { + /* TODO we can do better here */ + dst[1] = 'u'; + dst[2] = 'f'; + dst[3] = 'f'; + dst[4] = 'f'; + dst[5] = 'd'; + dst += 6; + } + } + *srcReadPtr = src - srcStart; + *dstWrotePtr = dst - dstStart; + *dstCharsPtr = dst - dstStart; + return result; +} + +#ifndef WORDS_BIGENDIAN +/* + *------------------------------------------------------------------------- + * + * Ucs2beToUtfProc -- + * + * Convert from UCS-2BE (big-endian 16-bit Unicode) to UTF-8. + * This is only defined on LE machines. + * + * Results: + * Returns TCL_OK if conversion was successful. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ + +static int +Ucs2beToUtfProc( + ClientData clientData, /* Not used. */ + const char *src, /* Source string in Unicode. */ + 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 *dstEnd, *dstStart; + int result, numChars; + + result = TCL_OK; + + /* check alignment with ucs-2 (2 == sizeof(UCS-2)) */ + if ((srcLen % 2) != 0) { + result = TCL_CONVERT_MULTIBYTE; + srcLen--; + } + + srcStart = src; + srcEnd = src + srcLen; + + dstStart = dst; + dstEnd = dst + dstLen - TCL_UTF_MAX; + + for (numChars = 0; src < srcEnd; numChars++) { + if (dst > dstEnd) { + result = TCL_CONVERT_NOSPACE; + break; + } + + /* + * Need to swap byte-order on little-endian machines (x86) for + * UCS-2BE. We know this is an LE->BE swap. + */ + + dst += Tcl_UniCharToUtf(htons(*((short *)src)), dst); + src += 2 /* sizeof(UCS-2) */; + } + + *srcReadPtr = src - srcStart; + *dstWrotePtr = dst - dstStart; + *dstCharsPtr = numChars; + return result; +} + +/* + *------------------------------------------------------------------------- + * + * UtfToUcs2beProc -- + * + * Convert from UTF-8 to UCS-2BE (fixed 2-byte encoding). + * + * Results: + * Returns TCL_OK if conversion was successful. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ + +static int +UtfToUcs2beProc( + ClientData clientData, /* TableEncodingData that specifies + * encoding. */ + 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, *srcClose, *dstStart, *dstEnd; + int result, numChars; + Tcl_UniChar ch; + + srcStart = src; + srcEnd = src + srcLen; + srcClose = srcEnd; + if (!(flags & TCL_ENCODING_END)) { + srcClose -= TCL_UTF_MAX; + } + + dstStart = dst; + dstEnd = dst + dstLen - 2 /* sizeof(UCS-2) */; + + result = TCL_OK; + for (numChars = 0; src < srcEnd; numChars++) { + if ((src > srcClose) && (!Tcl_UtfCharComplete(src, srcEnd - src))) { + /* + * If there is more string to follow, this will ensure that the + * last UTF-8 character in the source buffer hasn't been cut off. + */ + + result = TCL_CONVERT_MULTIBYTE; + break; + } + if (dst > dstEnd) { + result = TCL_CONVERT_NOSPACE; + break; + } + src += Tcl_UtfToUniChar(src, &ch); + + /* + * Ensure big-endianness (store big bits first). + * XXX: This hard-codes the assumed size of Tcl_UniChar as 2. Make + * sure to work in char* for Tcl_UtfToUniChar alignment. [Bug 1122671] + */ + + *dst++ = (ch >> 8); + *dst++ = (ch & 0xFF); + } + *srcReadPtr = src - srcStart; + *dstWrotePtr = dst - dstStart; + *dstCharsPtr = numChars; + return result; +} +#endif /* WORDS_BIGENDIAN */ + +/* + *--------------------------------------------------------------------------- + * + * 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 function 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: + * Memory allocated. + * + *--------------------------------------------------------------------------- + */ + +TkFont * +TkpGetNativeFont( + Tk_Window tkwin, /* For display where font will be used. */ + const char *name) /* Platform-specific font name. */ +{ + UnixFont *fontPtr; + XFontStruct *fontStructPtr; + FontAttributes fa; + const char *p; + int hasSpace, dashes, hasWild; + + /* + * The behavior of X when given a name that isn't an XLFD is unspecified. + * For example, Exceed 6 returns a valid font for any random string. This + * is awkward since system names have higher priority than the other Tk + * font syntaxes. So, we need to perform a quick sanity check on the name + * and fail if it looks suspicious. We fail if the name: + * - contains a space immediately before a dash + * - contains a space, but no '*' characters and fewer than 14 dashes + */ + + hasSpace = dashes = hasWild = 0; + for (p = name; *p != '\0'; p++) { + if (*p == ' ') { + if (p[1] == '-') { + return NULL; + } + hasSpace = 1; + } else if (*p == '-') { + dashes++; + } else if (*p == '*') { + hasWild = 1; + } + } + if ((dashes < 14) && !hasWild && hasSpace) { + return NULL; + } + + fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), name); + if (fontStructPtr == 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 = ckalloc(sizeof(UnixFont)); + InitFont(tkwin, fontStructPtr, fontPtr); + + return (TkFont *) fontPtr; +} + +/* + *--------------------------------------------------------------------------- + * + * 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 function 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: + * Memory allocated. + * + *--------------------------------------------------------------------------- + */ + +TkFont * +TkpGetFontFromAttributes( + 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. */ +{ + UnixFont *fontPtr; + TkXLFDAttributes xa; + XFontStruct *fontStructPtr; + + TkInitXLFDAttributes(&xa); + fontStructPtr = CreateClosestFont(tkwin, faPtr, &xa); + + fontPtr = (UnixFont *) tkFontPtr; + if (fontPtr == NULL) { + fontPtr = ckalloc(sizeof(UnixFont)); + } else { + ReleaseFont(fontPtr); + } + InitFont(tkwin, fontStructPtr, fontPtr); + + 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( + TkFont *tkFontPtr) /* Token of font to be deleted. */ +{ + UnixFont *fontPtr = (UnixFont *) tkFontPtr; + + ReleaseFont(fontPtr); +} + +/* + *--------------------------------------------------------------------------- + * + * TkpGetFontFamilies -- + * + * Return information about the font families that are available on the + * display of the given window. + * + * Results: + * Modifies interp's result object to hold a list of all the available + * font families. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +void +TkpGetFontFamilies( + Tcl_Interp *interp, /* Interp to hold result. */ + Tk_Window tkwin) /* For display to query. */ +{ + int i, new, numNames; + char *family, **nameList; + Tcl_HashTable familyTable; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + Tcl_Obj *resultPtr, *strPtr; + + Tcl_InitHashTable(&familyTable, TCL_STRING_KEYS); + nameList = ListFonts(Tk_Display(tkwin), "*", &numNames); + for (i = 0; i < numNames; i++) { + char *familyEnd; + + family = strchr(nameList[i] + 1, '-'); + if (family == NULL) { + /* + * Apparently, sometimes ListFonts() can return a font name with + * zero or one '-' character in it. This is probably indicative of + * a server misconfiguration, but crashing because of it is a very + * bad idea anyway. [Bug 1475865] + */ + + continue; + } + family++; /* Advance to char after '-'. */ + familyEnd = strchr(family, '-'); + if (familyEnd == NULL) { + continue; /* See comment above. */ + } + *familyEnd = '\0'; + Tcl_CreateHashEntry(&familyTable, family, &new); + } + XFreeFontNames(nameList); + + hPtr = Tcl_FirstHashEntry(&familyTable, &search); + resultPtr = Tcl_NewObj(); + while (hPtr != NULL) { + strPtr = Tcl_NewStringObj(Tcl_GetHashKey(&familyTable, hPtr), -1); + Tcl_ListObjAppendElement(NULL, resultPtr, strPtr); + hPtr = Tcl_NextHashEntry(&search); + } + Tcl_SetObjResult(interp, resultPtr); + + Tcl_DeleteHashTable(&familyTable); +} + +/* + *------------------------------------------------------------------------- + * + * 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( + Tcl_Interp *interp, + Tk_Font tkfont) +{ + int i; + Tcl_Obj *objv[3], *resultPtr, *listPtr; + UnixFont *fontPtr; + FontFamily *familyPtr; + + resultPtr = Tcl_NewObj(); + 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); + } + Tcl_SetObjResult(interp, resultPtr); +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetFontAttrsForChar -- + * + * Retrieve the font attributes of the actual font used to render a given + * character. + * + * Results: + * None. + * + * Side effects: + * The font attributes are stored in *faPtr. + * + *---------------------------------------------------------------------- + */ + +void +TkpGetFontAttrsForChar( + Tk_Window tkwin, /* Window on the font's display */ + Tk_Font tkfont, /* Font to query */ + int c, /* Character of interest */ + TkFontAttributes *faPtr) /* Output: Font attributes */ +{ + FontAttributes atts; + UnixFont *fontPtr = (UnixFont *) tkfont; + /* Structure describing the logical font */ + SubFont *lastSubFontPtr = &fontPtr->subFontArray[0]; + /* Pointer to subfont array in case + * FindSubFontForChar needs to fix up the + * memory allocation */ + SubFont *thisSubFontPtr = FindSubFontForChar(fontPtr, c, &lastSubFontPtr); + /* Pointer to the subfont to use for the given + * character */ + + GetFontAttributes(Tk_Display(tkwin), thisSubFontPtr->fontStructPtr, &atts); + *faPtr = atts.fa; +} + +/* + *--------------------------------------------------------------------------- + * + * 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 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. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +Tk_MeasureChars( + Tk_Font tkfont, /* Font in which characters will be drawn. */ + const char *source, /* UTF-8 string to be displayed. Need not be + * '\0' terminated. */ + int numBytes, /* Maximum number of bytes to consider from + * source string. */ + 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 + * 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; + SubFont *lastSubFontPtr; + int curX, curByte, ch; + + /* + * 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. + */ + + fontPtr = (UnixFont *) tkfont; + + lastSubFontPtr = &fontPtr->subFontArray[0]; + + if (numBytes == 0) { + curX = 0; + curByte = 0; + } else if (maxLength < 0) { + const char *p, *end, *next; + SubFont *thisSubFontPtr; + FontFamily *familyPtr; + Tcl_DString runString; + + /* + * 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. + */ + + curX = 0; + end = source + numBytes; + for (p = source; p < end; ) { + next = p + TkUtfToUniChar(p, &ch); + thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); + 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; + } + 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; + FontFamily *familyPtr; + XChar2b buf[8]; + + /* + * How many chars will fit in the space allotted? This first version + * may be inefficient because it measures every character + * individually. + */ + + next = source + TkUtfToUniChar(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, NULL); + familyPtr = lastSubFontPtr->familyPtr; + Tcl_UtfToExternal(NULL, familyPtr->encoding, p, next - p, 0, NULL, + (char *)&buf[0].byte1, sizeof(buf), NULL, &dstWrote, NULL); + if (familyPtr->isTwoByteFont) { + newX += XTextWidth16(lastSubFontPtr->fontStructPtr, + buf, dstWrote >> 1); + } else { + newX += XTextWidth(lastSubFontPtr->fontStructPtr, + (char *)&buf[0].byte1, dstWrote); + } + } + if (newX > maxLength) { + break; + } + curX = newX; + p = next; + if (p >= end) { + term = end; + termX = curX; + break; + } + + next += TkUtfToUniChar(next, &ch); + if ((ch < 256) && isspace(ch)) { + 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) && (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 += TkUtfToUniChar(p, &ch); + } + if ((flags & TK_AT_LEAST_ONE) && (term == source) && (p < end)) { + term = p; + termX = curX; + if (term == source) { + term += TkUtfToUniChar(term, &ch); + termX = newX; + } + } else if ((p >= end) || !(flags & TK_WHOLE_WORDS)) { + term = p; + termX = curX; + } + + curX = termX; + curByte = term - source; + } + + *lengthPtr = curX; + return curByte; +} + +/* + *--------------------------------------------------------------------------- + * + * TkpMeasureCharsInContext -- + * + * Determine the number of bytes from the string that will fit in the + * given horizontal span. The measurement is done under the assumption + * that TkpDrawCharsInContext() will be used to actually display the + * characters. + * + * This one is almost the same as Tk_MeasureChars(), but with access to + * all the characters on the line for context. On X11 this context isn't + * consulted, so we just call Tk_MeasureChars(). + * + * Results: + * 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. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +TkpMeasureCharsInContext( + Tk_Font tkfont, /* Font in which characters will be drawn. */ + const char *source, /* UTF-8 string to be displayed. Need not be + * '\0' terminated. */ + int numBytes, /* Maximum number of bytes to consider from + * source string in all. */ + int rangeStart, /* Index of first byte to measure. */ + int rangeLength, /* Length of range to measure in bytes. */ + 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. TK_ISOLATE_END means + * that the last character should not be + * considered in context with the rest of the + * string (used for breaking lines). */ + int *lengthPtr) /* Filled with x-location just after the + * terminating character. */ +{ + (void) numBytes; /*unused*/ + return Tk_MeasureChars(tkfont, source + rangeStart, rangeLength, + maxLength, flags, lengthPtr); +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_DrawChars -- + * + * Draw a string of characters on the screen. Tk_DrawChars() expands + * control characters that occur in the string to \xNN sequences. + * + * Results: + * None. + * + * Side effects: + * Information gets drawn on the screen. + * + *--------------------------------------------------------------------------- + */ + +void +Tk_DrawChars( + 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, /* 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 numBytes, /* Number of bytes in string. */ + int x, int y) /* Coordinates at which to place origin of + * string when drawing. */ +{ + UnixFont *fontPtr = (UnixFont *) tkfont; + SubFont *thisSubFontPtr, *lastSubFontPtr; + Tcl_DString runString; + const char *p, *end, *next; + int xStart, needWidth, window_width, do_width, ch; + FontFamily *familyPtr; +#ifdef TK_DRAW_CHAR_XWINDOW_CHECK + int rx, ry; + unsigned width, height, border_width, depth; + Drawable root; +#endif + + lastSubFontPtr = &fontPtr->subFontArray[0]; + xStart = x; + +#ifdef TK_DRAW_CHAR_XWINDOW_CHECK + /* + * Get the window width so we can abort drawing outside of the window + */ + + if (XGetGeometry(display, drawable, &root, &rx, &ry, &width, &height, + &border_width, &depth) == False) { + window_width = INT_MAX; + } else { + window_width = width; + } +#else + /* + * This is used by default until we find a solution that doesn't do a + * round-trip to the X server (needed to get Tk cached window width). + */ + + window_width = 32768; +#endif + + end = source + numBytes; + needWidth = fontPtr->font.fa.underline + fontPtr->font.fa.overstrike; + for (p = source; p <= end; ) { + if (p < end) { + next = p + TkUtfToUniChar(p, &ch); + thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); + } else { + next = p + 1; + thisSubFontPtr = lastSubFontPtr; + } + if ((thisSubFontPtr != lastSubFontPtr) + || (p == end) || (p-source > 200)) { + if (p > source) { + do_width = (needWidth || (p != end)) ? 1 : 0; + 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); + if (do_width) { + 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)); + if (do_width) { + x += XTextWidth(lastSubFontPtr->fontStructPtr, + Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString)); + } + } + Tcl_DStringFree(&runString); + } + lastSubFontPtr = thisSubFontPtr; + source = p; + XSetFont(display, gc, lastSubFontPtr->fontStructPtr->fid); + if (x > window_width) { + break; + } + } + p = next; + } + + if (lastSubFontPtr != &fontPtr->subFontArray[0]) { + XSetFont(display, gc, fontPtr->subFontArray[0].fontStructPtr->fid); + } + + if (fontPtr->font.fa.underline != 0) { + XFillRectangle(display, drawable, gc, xStart, + y + fontPtr->underlinePos, + (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, xStart, y, + (unsigned) (x - xStart), (unsigned) fontPtr->barHeight); + } +} + +/* + *--------------------------------------------------------------------------- + * + * TkpDrawCharsInContext -- + * + * Draw a string of characters on the screen like Tk_DrawChars(), but + * with access to all the characters on the line for context. On X11 this + * context isn't consulted, so we just call Tk_DrawChars(). + * + * Results: + * None. + * + * Side effects: + * Information gets drawn on the screen. + * + *--------------------------------------------------------------------------- + */ + +void +TkpDrawCharsInContext( + 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, /* 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 numBytes, /* Number of bytes in string. */ + int rangeStart, /* Index of first byte to draw. */ + int rangeLength, /* Length of range to draw in bytes. */ + int x, int y) /* Coordinates at which to place origin of the + * whole (not just the range) string when + * drawing. */ +{ + int widthUntilStart; + + (void) numBytes; /*unused*/ + + Tk_MeasureChars(tkfont, source, rangeStart, -1, 0, &widthUntilStart); + Tk_DrawChars(display, drawable, gc, tkfont, source + rangeStart, + rangeLength, x+widthUntilStart, y); +} + +/* + *------------------------------------------------------------------------- + * + * CreateClosestFont -- + * + * Helper for TkpGetNativeFont() and TkpGetFontFromAttributes(). 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: + * 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: + * None. + * + *------------------------------------------------------------------------- + */ + +static XFontStruct * +CreateClosestFont( + Tk_Window tkwin, /* For display where font will be used. */ + const TkFontAttributes *faPtr, + /* Set of generic attributes to match. */ + const TkXLFDAttributes *xaPtr) + /* Set of X-specific attributes to match. */ +{ + FontAttributes want; + char **nameList; + int numNames, nameIdx, bestIdx[2]; + Display *display; + XFontStruct *fontStructPtr; + unsigned 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); + + /* + * 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 + */ + + nameList = ListFontOrAlias(display, want.fa.family, &numNames); + if (numNames == 0) { + const char *const *const *fontFallbacks; + int i, j; + const 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); + } + } + + found: + bestIdx[0] = -1; + bestIdx[1] = -1; + bestScore[0] = (unsigned) -1; + bestScore[1] = (unsigned) -1; + for (nameIdx = 0; nameIdx < numNames; nameIdx++) { + FontAttributes got; + int scalable; + unsigned score; + + if (TkFontParseXLFD(nameList[nameIdx], &got.fa, &got.xa) != TCL_OK) { + continue; + } + IdentifySymbolEncodings(&got); + scalable = (got.fa.size == 0.0); + score = RankAttributes(&want, &got); + if (score < bestScore[scalable]) { + bestIdx[scalable] = nameIdx; + bestScore[scalable] = score; + } + if (score == 0) { + break; + } + } + + fontStructPtr = GetScreenFont(display, &want, nameList, bestIdx, + bestScore); + XFreeFontNames(nameList); + + if (fontStructPtr == NULL) { + return GetSystemFont(display); + } + 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( + 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 = + 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; + + /* + * Get all font attributes and metrics. + */ + + 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; + } + } + } + } + + 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 = (int)(TkFontGetPixels(tkwin, fa.fa.size) + 0.5); + 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', NULL); + 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 ((minHi > 0) || (i < minLo) || (i > maxLo) + || !((pageMap[i>>3] >> (i&7)) & 1)) { + n = 0; + } else if (fontStructPtr->per_char == NULL) { + n = fontStructPtr->max_bounds.width; + } else { + n = fontStructPtr->per_char[i - minLo].width; + } + fontPtr->widths[i] = n; + } + + 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)) { + 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; + } + } +} + +/* + *------------------------------------------------------------------------- + * + * 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( + UnixFont *fontPtr) /* The font to delete. */ +{ + int i; + + for (i = 0; i < fontPtr->numSubFonts; i++) { + ReleaseSubFont(fontPtr->display, &fontPtr->subFontArray[i]); + } + if (fontPtr->subFontArray != fontPtr->staticSubFonts) { + ckfree(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 *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 *display, /* Display which owns screen font. */ + SubFont *subFontPtr) /* The SubFont to delete. */ +{ + XFreeFont(display, subFontPtr->fontStructPtr); + FreeFontFamily(subFontPtr->familyPtr); +} + +/* + *------------------------------------------------------------------------- + * + * AllocFontFamily -- + * + * 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. + * + * 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: + * 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 *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 = + 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)) { + if (encoding) { + Tcl_FreeEncoding(encoding); + } + familyPtr->refCount++; + return familyPtr; + } + } + + familyPtr = 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; + + /* + * One byte/character fonts have both min_byte1 and max_byte1 0, and + * max_char_or_byte2 <= 255. Anything else specifies a two byte/character + * font. + */ + + familyPtr->isTwoByteFont = !( + (fontStructPtr->min_byte1 == 0) && + (fontStructPtr->max_byte1 == 0) && + (fontStructPtr->max_char_or_byte2 < 256)); + 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( + FontFamily *familyPtr) /* The FontFamily to delete. */ +{ + FontFamily **familyPtrPtr; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + int i; + + if (familyPtr == NULL) { + return; + } + familyPtr->refCount--; + if (familyPtr->refCount > 0) { + return; + } + if (familyPtr->encoding) { + 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(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. The + * table of SubFonts might be extended, and if a non-NULL reference to a + * subfont pointer is available, it is updated if it previously pointed + * into the old subfont table. + * + *------------------------------------------------------------------------- + */ + +static SubFont * +FindSubFontForChar( + UnixFont *fontPtr, /* The font object with which the character + * will be displayed. */ + int ch, /* The Unicode character to be displayed. */ + SubFont **fixSubFontPtrPtr) /* Subfont reference to fix up if we + * reallocate our subfont table. */ +{ + int i, j, k, numNames; + Tk_Uid faceName; + const char *fallback; + const char *const *aliases; + char **nameList; + const char *const *anyFallbacks; + const char *const *const *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, fixSubFontPtrPtr); + 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, + fixSubFontPtrPtr); + 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, + fixSubFontPtrPtr); + 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, + fixSubFontPtrPtr); + 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( + 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( + 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: + * Memory allocated. + * + *------------------------------------------------------------------------- + */ +static void +FontMapLoadPage( + SubFont *subFontPtr, /* Contains font mapping cache to be + * updated. */ + int row) /* Index of the page to be loaded into the + * cache. */ +{ + char buf[16], src[6]; + int minHi, maxHi, minLo, maxLo, scale, checkLo; + int i, end, bitOffset, isTwoByteFont, n; + Tcl_Encoding encoding; + XFontStruct *fontStructPtr; + XCharStruct *widths; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + subFontPtr->fontMap[row] = 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, TkUniCharToUtf(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. + * The table of SubFonts might be extended, and if a non-NULL reference + * to a subfont pointer is available, it is updated if it previously + * pointed into the old subfont table. + * + *--------------------------------------------------------------------------- + */ + +static SubFont * +CanUseFallbackWithAliases( + UnixFont *fontPtr, /* The font object that will own the new + * screen font. */ + const 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 **fixSubFontPtrPtr) /* Subfont reference to fix up if we + * reallocate our subfont table. */ +{ + SubFont *subFontPtr; + const char *const *aliases; + int i; + + if (SeenName(faceName, nameTriedPtr) == 0) { + subFontPtr = CanUseFallback(fontPtr, faceName, ch, fixSubFontPtrPtr); + 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, + fixSubFontPtrPtr); + 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. + * + *--------------------------------------------------------------------------- + */ + +static int +SeenName( + 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, 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. The table of SubFonts might be extended, + * and if a non-NULL reference to a subfont pointer is available, it is + * updated if it previously pointed into the old subfont table. + * + *------------------------------------------------------------------------- + */ + +static SubFont * +CanUseFallback( + UnixFont *fontPtr, /* The font object that will own the new + * screen font. */ + const char *faceName, /* Desired face name for new screen font. */ + int ch, /* The Unicode character that the new screen + * font must be able to display. */ + SubFont **fixSubFontPtrPtr) /* Subfont reference to fix up if we + * reallocate our subfont table. */ +{ + int i, nameIdx, numNames, srcLen, numEncodings, bestIdx[2]; + Tk_Uid hateFoundry; + const char *charset, *hateCharset; + unsigned bestScore[2]; + char **nameList; + char **nameListOrig; + char src[6]; + FontAttributes want, got; + Display *display; + SubFont subFont; + XFontStruct *fontStructPtr; + Tcl_DString dsEncodings; + 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 = TkUniCharToUtf(ch, src); + + want.fa = fontPtr->font.fa; + want.xa = fontPtr->xa; + + want.fa.family = Tk_GetUid(faceName); + want.fa.size = (double)-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) -1; + bestScore[1] = (unsigned) -1; + for (nameIdx = 0; nameIdx < numNames; nameIdx++) { + Tcl_Encoding encoding; + char dst[16]; + int scalable, srcRead, dstWrote; + unsigned 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.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 = 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(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(nameList); + } + XFreeFontNames(nameListOrig); + + if (fontPtr->numSubFonts >= SUBFONT_SPACE) { + SubFont *newPtr; + + newPtr = ckalloc(sizeof(SubFont) * (fontPtr->numSubFonts + 1)); + memcpy(newPtr, fontPtr->subFontArray, + fontPtr->numSubFonts * sizeof(SubFont)); + if (fixSubFontPtrPtr != NULL) { + register SubFont *fixSubFontPtr = *fixSubFontPtrPtr; + + if (fixSubFontPtr != &fontPtr->controlSubFont) { + *fixSubFontPtrPtr = + newPtr + (fixSubFontPtr - fontPtr->subFontArray); + } + } + if (fontPtr->subFontArray != fontPtr->staticSubFonts) { + ckfree(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 +RankAttributes( + FontAttributes *wantPtr, /* The desired attributes. */ + FontAttributes *gotPtr) /* The attributes we have to live with. */ +{ + unsigned 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.0) { + /* + * A scalable font is almost always acceptable, but the corresponding + * bitmapped font would be better. + */ + + penalty += 10; + } else { + int diff; + + /* + * It's worse to be too large than to be too small. + */ + + diff = (int) (150 * (-gotPtr->fa.size - -wantPtr->fa.size)); + if (diff > 0) { + penalty += 600; + } else if (diff < 0) { + penalty += 150; + diff = -diff; + } + penalty += 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 *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 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, 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]], + (int)(-wantPtr->fa.size+0.5), 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 for new XFontStruct. */ +{ + XFontStruct *fontStructPtr; + + fontStructPtr = XLoadQueryFont(display, "fixed"); + if (fontStructPtr == NULL) { + fontStructPtr = XLoadQueryFont(display, "*"); + if (fontStructPtr == NULL) { + Tcl_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 *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 *name; + + if ((XGetFontProperty(fontStructPtr, XA_FONT, &value) != False) && + (value != 0)) { + name = XGetAtomName(display, (Atom) value); + 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); + } + + /* + * Do last ditch check for family. It seems that some X servers can fail + * on the X font calls above, slipping through earlier checks. X-Win32 5.4 + * is one of these. + */ + + if (faPtr->fa.family == NULL) { + 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 *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 *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; + const char *const *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( + FontAttributes *faPtr) +{ + int i, j; + const char *const *aliases; + const char *const *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( + const char *name) /* The name to look up. */ +{ + EncodingAlias *aliasPtr; + + for (aliasPtr = encodingAliases; aliasPtr->aliasPattern != NULL; ) { + if (Tcl_StringMatch(name, aliasPtr->aliasPattern)) { + return aliasPtr->realName; + } + aliasPtr++; + } + return name; +} + +/* + *--------------------------------------------------------------------------- + * + * TkDrawAngledChars -- + * + * Draw some characters at an angle. This is awkward here because we have + * no reliable way of drawing any characters at an angle in classic X11; + * we have to draw on a Pixmap which is converted to an XImage (from + * helper function GetImageOfText), rotate the image (hokey code!) onto + * another XImage (from helper function InitDestImage), and then use the + * rotated image as a mask when drawing. This is pretty awful; improved + * versions are welcomed! + * + * Results: + * None. + * + * Side effects: + * Target drawable is updated. + * + *--------------------------------------------------------------------------- + */ + +static inline XImage * +GetImageOfText( + Display *display, /* Display on which to draw. */ + Drawable drawable, /* Window or pixmap in which to draw. */ + Tk_Font tkfont, /* Font in which characters will be drawn. */ + 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 numBytes, /* Number of bytes in string. */ + int *realWidthPtr, int *realHeightPtr) +{ + int width, height; + TkFont *fontPtr = (TkFont *) tkfont; + Pixmap bitmap; + GC bitmapGC; + XGCValues values; + XImage *image; + + (void) Tk_MeasureChars(tkfont, source, numBytes, -1, 0, &width); + height = fontPtr->fm.ascent + fontPtr->fm.descent; + + bitmap = Tk_GetPixmap(display, drawable, width, height, 1); + values.graphics_exposures = False; + values.foreground = BlackPixel(display, DefaultScreen(display)); + bitmapGC = XCreateGC(display, bitmap, GCGraphicsExposures|GCForeground, + &values); + XFillRectangle(display, bitmap, bitmapGC, 0, 0, width, height); + + values.font = Tk_FontId(tkfont); + values.foreground = WhitePixel(display, DefaultScreen(display)); + values.background = BlackPixel(display, DefaultScreen(display)); + XChangeGC(display, bitmapGC, GCFont|GCForeground|GCBackground, &values); + Tk_DrawChars(display, bitmap, bitmapGC, tkfont, source, numBytes, 0, + fontPtr->fm.ascent); + XFreeGC(display, bitmapGC); + + image = XGetImage(display, bitmap, 0, 0, width, height, AllPlanes, + ZPixmap); + Tk_FreePixmap(display, bitmap); + + *realWidthPtr = width; + *realHeightPtr = height; + return image; +} + +static inline XImage * +InitDestImage( + Display *display, + Drawable drawable, + int width, + int height, + Pixmap *bitmapPtr) +{ + Pixmap bitmap; + XImage *image; + GC bitmapGC; + XGCValues values; + + bitmap = Tk_GetPixmap(display, drawable, width, height, 1); + values.graphics_exposures = False; + values.foreground = BlackPixel(display, DefaultScreen(display)); + bitmapGC = XCreateGC(display, bitmap, GCGraphicsExposures|GCForeground, + &values); + XFillRectangle(display, bitmap, bitmapGC, 0, 0, width, height); + XFreeGC(display, bitmapGC); + + image = XGetImage(display, bitmap, 0, 0, width, height, AllPlanes, + ZPixmap); + *bitmapPtr = bitmap; + return image; +} + +void +TkDrawAngledChars( + 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, /* 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 numBytes, /* Number of bytes in string. */ + double x, double y, + double angle) +{ + if (angle == 0.0) { + Tk_DrawChars(display, drawable, gc, tkfont, source, numBytes, x, y); + } else { + double sinA = sin(angle * PI/180.0), cosA = cos(angle * PI/180.0); + int bufHeight, bufWidth, srcWidth, srcHeight, i, j, dx, dy; + Pixmap buf; + XImage *srcImage = GetImageOfText(display, drawable, tkfont, source, + numBytes, &srcWidth, &srcHeight); + XImage *dstImage; + enum {Q0=1,R1,Q1,R2,Q2,R3,Q3} quadrant; + GC bwgc, cpgc; + XGCValues values; + int ascent = ((TkFont *) tkfont)->fm.ascent; + + /* + * First, work out what quadrant we are operating in. We also handle + * the rectilinear rotations as special cases. Conceptually, there's + * also R0 (angle == 0.0) but that has been already handled as a + * special case above. + * + * R1 + * Q1 | Q0 + * | + * R2 ----+---- R0 + * | + * Q2 | Q3 + * R3 + */ + + if (angle < 90.0) { + quadrant = Q0; + } else if (angle == 90.0) { + quadrant = R1; + } else if (angle < 180.0) { + quadrant = Q1; + } else if (angle == 180.0) { + quadrant = R2; + } else if (angle < 270.0) { + quadrant = Q2; + } else if (angle == 270.0) { + quadrant = R3; + } else { + quadrant = Q3; + } + + if (srcImage == NULL) { + return; + } + bufWidth = srcWidth*fabs(cosA) + srcHeight*fabs(sinA); + bufHeight = srcHeight*fabs(cosA) + srcWidth*fabs(sinA); + dstImage = InitDestImage(display, drawable, bufWidth,bufHeight, &buf); + if (dstImage == NULL) { + Tk_FreePixmap(display, buf); + XDestroyImage(srcImage); + return; + } + + /* + * Do the rotation, setting or resetting pixels in the destination + * image dependent on whether the corresponding pixel (after rotation + * to source image space) is set. + */ + + for (i=0 ; i<srcWidth ; i++) { + for (j=0 ; j<srcHeight ; j++) { + switch (quadrant) { + case Q0: + dx = ROUND16(i*cosA + j*sinA); + dy = ROUND16(j*cosA + (srcWidth - i)*sinA); + break; + case R1: + dx = j; + dy = srcWidth - i; + break; + case Q1: + dx = ROUND16((i - srcWidth)*cosA + j*sinA); + dy = ROUND16((srcWidth-i)*sinA + (j-srcHeight)*cosA); + break; + case R2: + dx = srcWidth - i; + dy = srcHeight - j; + break; + case Q2: + dx = ROUND16((i-srcWidth)*cosA + (j-srcHeight)*sinA); + dy = ROUND16((j - srcHeight)*cosA - i*sinA); + break; + case R3: + dx = srcHeight - j; + dy = i; + break; + default: + dx = ROUND16(i*cosA + (j - srcHeight)*sinA); + dy = ROUND16(j*cosA - i*sinA); + } + + if (dx < 0 || dy < 0 || dx >= bufWidth || dy >= bufHeight) { + continue; + } + XPutPixel(dstImage, dx, dy, + XGetPixel(dstImage,dx,dy) | XGetPixel(srcImage,i,j)); + } + } + XDestroyImage(srcImage); + + /* + * Schlep the data back to the Xserver. + */ + + values.function = GXcopy; + values.foreground = WhitePixel(display, DefaultScreen(display)); + values.background = BlackPixel(display, DefaultScreen(display)); + bwgc = XCreateGC(display, buf, GCFunction|GCForeground|GCBackground, + &values); + XPutImage(display, buf, bwgc, dstImage, 0,0, 0,0, bufWidth,bufHeight); + XFreeGC(display, bwgc); + XDestroyImage(dstImage); + + /* + * Calculate where we want to draw the text. + */ + + switch (quadrant) { + case Q0: + dx = x; + dy = y - srcWidth*sinA; + break; + case R1: + dx = x; + dy = y - srcWidth; + break; + case Q1: + dx = x + srcWidth*cosA; + dy = y + srcHeight*cosA - srcWidth*sinA; + break; + case R2: + dx = x - srcWidth; + dy = y - srcHeight; + break; + case Q2: + dx = x + srcWidth*cosA + srcHeight*sinA; + dy = y + srcHeight*cosA; + break; + case R3: + dx = x - srcHeight; + dy = y; + break; + default: + dx = x + srcHeight*sinA; + dy = y; + } + + /* + * Apply a correction to deal with the fact that we aren't told to + * draw from our top-left corner but rather from the left-end of our + * baseline. + */ + + dx -= ascent*sinA; + dy -= ascent*cosA; + + /* + * Transfer the text to the screen. This is done by using it as a mask + * and then drawing through that mask with the original drawing color. + */ + + values.function = GXcopy; + values.fill_style = FillSolid; + values.clip_mask = buf; + values.clip_x_origin = dx; + values.clip_y_origin = dy; + cpgc = XCreateGC(display, drawable, + GCFunction|GCFillStyle|GCClipMask|GCClipXOrigin|GCClipYOrigin, + &values); + XCopyGC(display, gc, GCForeground, cpgc); + XFillRectangle(display, drawable, cpgc, dx, dy, bufWidth, + bufHeight); + XFreeGC(display, cpgc); + + Tk_FreePixmap(display, buf); + return; + } +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |