summaryrefslogtreecommitdiffstats
path: root/tk8.6/win/tkWinFont.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2017-09-22 18:57:36 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2017-09-22 18:57:36 (GMT)
commit04d4f0f83ae01daebf6001f40d1261464d185809 (patch)
treed1e4a8b1c22c47d70a369143d3406c524b5c1834 /tk8.6/win/tkWinFont.c
parent2aff4a96fa0286d875bddec0019648e2c6431cbc (diff)
parent1005c7e630baa54e58ac331f48bf876011deb6b3 (diff)
downloadblt-04d4f0f83ae01daebf6001f40d1261464d185809.zip
blt-04d4f0f83ae01daebf6001f40d1261464d185809.tar.gz
blt-04d4f0f83ae01daebf6001f40d1261464d185809.tar.bz2
Merge commit '1005c7e630baa54e58ac331f48bf876011deb6b3' as 'tk8.6'
Diffstat (limited to 'tk8.6/win/tkWinFont.c')
-rw-r--r--tk8.6/win/tkWinFont.c2908
1 files changed, 2908 insertions, 0 deletions
diff --git a/tk8.6/win/tkWinFont.c b/tk8.6/win/tkWinFont.c
new file mode 100644
index 0000000..d67ea66
--- /dev/null
+++ b/tk8.6/win/tkWinFont.c
@@ -0,0 +1,2908 @@
+/*
+ * tkWinFont.c --
+ *
+ * Contains the Windows implementation of the platform-independant font
+ * package interface.
+ *
+ * Copyright (c) 1994 Software Research Associates, Inc.
+ * Copyright (c) 1995-1997 Sun Microsystems, Inc.
+ * Copyright (c) 1998-1999 by Scriptics Corporation.
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "tkWinInt.h"
+#include "tkFont.h"
+
+/*
+ * 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 Windows, a "font family" is uniquely identified by its face name.
+ */
+
+#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. */
+ size_t refCount; /* How many SubFonts are referring to this
+ * FontFamily. When the refCount drops to
+ * zero, this FontFamily may be freed. */
+ /*
+ * Key.
+ */
+
+ Tk_Uid faceName; /* Face name key for this FontFamily. */
+
+ /*
+ * Derived properties.
+ */
+
+ Tcl_Encoding encoding; /* Encoding for this font family. */
+ int isSymbolFont; /* Non-zero if this is a symbol font. */
+ int isWideFont; /* 1 if this is a double-byte font, 0
+ * otherwise. */
+ BOOL (WINAPI *textOutProc)(HDC hdc, int x, int y, TCHAR *str, int len);
+ /* The procedure to use to draw text after it
+ * has been converted from UTF-8 to the
+ * encoding of this font. */
+ BOOL (WINAPI *getTextExtentPoint32Proc)(HDC, TCHAR *, int, LPSIZE);
+ /* The procedure to use to measure text after
+ * it has been converted from UTF-8 to the
+ * encoding of this font. */
+
+ 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 added. 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. */
+
+ /*
+ * Cached Truetype font info.
+ */
+
+ int segCount; /* The length of the following arrays. */
+ USHORT *startCount; /* Truetype information about the font, */
+ USHORT *endCount; /* indicating which characters this font can
+ * display (malloced). The format of this
+ * information is (relatively) compact, but
+ * would take longer to search than indexing
+ * into the fontMap[][] table. */
+} 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. */
+ HFONT hFont0; /* The specific screen font that will be used
+ * when displaying/measuring chars belonging
+ * to the FontFamily. */
+ FontFamily *familyPtr; /* The FontFamily for this SubFont. */
+ HFONT hFontAngled;
+ double angle;
+} SubFont;
+
+/*
+ * The following structure represents Windows' implementation of a font
+ * object.
+ */
+
+#define SUBFONT_SPACE 3
+#define BASE_CHARS 128
+
+typedef struct WinFont {
+ 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. */
+ HWND hwnd; /* Toplevel window of application that owns
+ * this font, used for getting HDC for
+ * offscreen measurements. */
+ int pixelSize; /* Original pixel size used when font was
+ * constructed. */
+ int widths[BASE_CHARS]; /* Widths of first 128 chars in the base font,
+ * for handling common case. The base font is
+ * always used to draw characters between
+ * 0x0000 and 0x007f. */
+} WinFont;
+
+/*
+ * The following structure is passed as the LPARAM when calling the font
+ * enumeration procedure to determine if a font can support the given
+ * character.
+ */
+
+typedef struct CanUse {
+ HDC hdc;
+ WinFont *fontPtr;
+ Tcl_DString *nameTriedPtr;
+ int ch;
+ SubFont *subFontPtr;
+ SubFont **subFontPtrPtr;
+} CanUse;
+
+/*
+ * The following structure is used to map between the Tcl strings that
+ * represent the system fonts and the numbers used by Windows.
+ */
+
+static const TkStateMap systemMap[] = {
+ {ANSI_FIXED_FONT, "ansifixed"},
+ {ANSI_FIXED_FONT, "fixed"},
+ {ANSI_VAR_FONT, "ansi"},
+ {DEVICE_DEFAULT_FONT, "device"},
+ {DEFAULT_GUI_FONT, "defaultgui"},
+ {OEM_FIXED_FONT, "oemfixed"},
+ {SYSTEM_FIXED_FONT, "systemfixed"},
+ {SYSTEM_FONT, "system"},
+ {-1, NULL}
+};
+
+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. */
+ Tcl_HashTable uidTable;
+} ThreadSpecificData;
+static Tcl_ThreadDataKey dataKey;
+
+/*
+ * Information cached about the system at startup time.
+ */
+
+static Tcl_Encoding systemEncoding;
+
+/*
+ * Procedures used only in this file.
+ */
+
+static FontFamily * AllocFontFamily(HDC hdc, HFONT hFont, int base);
+static SubFont * CanUseFallback(HDC hdc, WinFont *fontPtr,
+ const char *fallbackName, int ch,
+ SubFont **subFontPtrPtr);
+static SubFont * CanUseFallbackWithAliases(HDC hdc, WinFont *fontPtr,
+ const char *faceName, int ch,
+ Tcl_DString *nameTriedPtr,
+ SubFont **subFontPtrPtr);
+static int FamilyExists(HDC hdc, const char *faceName);
+static const char * FamilyOrAliasExists(HDC hdc, const char *faceName);
+static SubFont * FindSubFontForChar(WinFont *fontPtr, int ch,
+ SubFont **subFontPtrPtr);
+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 *familyPtr);
+static HFONT GetScreenFont(const TkFontAttributes *faPtr,
+ const char *faceName, int pixelSize,
+ double angle);
+static void InitFont(Tk_Window tkwin, HFONT hFont,
+ int overstrike, WinFont *tkFontPtr);
+static inline void InitSubFont(HDC hdc, HFONT hFont, int base,
+ SubFont *subFontPtr);
+static int CreateNamedSystemLogFont(Tcl_Interp *interp,
+ Tk_Window tkwin, const char* name,
+ LOGFONT* logFontPtr);
+static int CreateNamedSystemFont(Tcl_Interp *interp,
+ Tk_Window tkwin, const char* name, HFONT hFont);
+static int LoadFontRanges(HDC hdc, HFONT hFont,
+ USHORT **startCount, USHORT **endCount,
+ int *symbolPtr);
+static void MultiFontTextOut(HDC hdc, WinFont *fontPtr,
+ const char *source, int numBytes, int x, int y,
+ double angle);
+static void ReleaseFont(WinFont *fontPtr);
+static inline void ReleaseSubFont(SubFont *subFontPtr);
+static int SeenName(const char *name, Tcl_DString *dsPtr);
+static inline HFONT SelectFont(HDC hdc, WinFont *fontPtr,
+ SubFont *subFontPtr, double angle);
+static inline void SwapLong(PULONG p);
+static inline void SwapShort(USHORT *p);
+static int CALLBACK WinFontCanUseProc(ENUMLOGFONT *lfPtr,
+ NEWTEXTMETRIC *tmPtr, int fontType,
+ LPARAM lParam);
+static int CALLBACK WinFontExistProc(ENUMLOGFONT *lfPtr,
+ NEWTEXTMETRIC *tmPtr, int fontType,
+ LPARAM lParam);
+static int CALLBACK WinFontFamilyEnumProc(ENUMLOGFONT *lfPtr,
+ NEWTEXTMETRIC *tmPtr, int fontType,
+ LPARAM lParam);
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * TkpFontPkgInit --
+ *
+ * This procedure is called when an application is created. It
+ * initializes all the structures that are used by the platform-dependent
+ * code on a per application basis.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+void
+TkpFontPkgInit(
+ TkMainInfo *mainPtr) /* The application being created. */
+{
+ systemEncoding = TkWinGetUnicodeEncoding();
+ TkWinSetupSystemFonts(mainPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkpGetNativeFont --
+ *
+ * Map a platform-specific native font name to a TkFont.
+ *
+ * Results:
+ * The return value is a pointer to a TkFont that represents the native
+ * font. If a native font by the given name could not be found, the
+ * return value is NULL.
+ *
+ * Every call to this procedure returns a new TkFont structure, even if
+ * the name has already been seen before. The caller should call
+ * TkpDeleteFont() when the font is no longer needed.
+ *
+ * The caller is responsible for initializing the memory associated with
+ * the generic TkFont when this function returns and releasing the
+ * contents of the generic TkFont before calling TkpDeleteFont().
+ *
+ * Side effects:
+ * Memory allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+TkFont *
+TkpGetNativeFont(
+ Tk_Window tkwin, /* For display where font will be used. */
+ const char *name) /* Platform-specific font name. */
+{
+ int object;
+ WinFont *fontPtr;
+
+ object = TkFindStateNum(NULL, NULL, systemMap, name);
+ if (object < 0) {
+ return NULL;
+ }
+
+ tkwin = (Tk_Window) ((TkWindow *) tkwin)->mainPtr->winPtr;
+ fontPtr = ckalloc(sizeof(WinFont));
+ InitFont(tkwin, GetStockObject(object), 0, fontPtr);
+
+ return (TkFont *) fontPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CreateNamedSystemFont --
+ *
+ * This function registers a Windows logical font description with the Tk
+ * named font mechanism.
+ *
+ * Side effects:
+ * A new named font is added to the Tk font registry.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static int
+CreateNamedSystemLogFont(
+ Tcl_Interp *interp,
+ Tk_Window tkwin,
+ const char* name,
+ LOGFONT* logFontPtr)
+{
+ HFONT hFont;
+ int r;
+
+ hFont = CreateFontIndirect(logFontPtr);
+ r = CreateNamedSystemFont(interp, tkwin, name, hFont);
+ DeleteObject((HGDIOBJ)hFont);
+ return r;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CreateNamedSystemFont --
+ *
+ * This function registers a Windows font with the Tk named font
+ * mechanism.
+ *
+ * Side effects:
+ * A new named font is added to the Tk font registry.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static int
+CreateNamedSystemFont(
+ Tcl_Interp *interp,
+ Tk_Window tkwin,
+ const char* name,
+ HFONT hFont)
+{
+ WinFont winfont;
+ int r;
+
+ TkDeleteNamedFont(NULL, tkwin, name);
+ InitFont(tkwin, hFont, 0, &winfont);
+ r = TkCreateNamedFont(interp, tkwin, name, &winfont.font.fa);
+ TkpDeleteFont((TkFont *)&winfont);
+ return r;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkWinSystemFonts --
+ *
+ * Create some platform specific named fonts that to give access to the
+ * system fonts. These are all defined for the Windows desktop
+ * parameters.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+TkWinSetupSystemFonts(
+ TkMainInfo *mainPtr)
+{
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ const TkStateMap *mapPtr;
+ NONCLIENTMETRICS ncMetrics;
+ ICONMETRICS iconMetrics;
+ HFONT hFont;
+
+ interp = (Tcl_Interp *) mainPtr->interp;
+ tkwin = (Tk_Window) mainPtr->winPtr;
+
+ /* force this for now */
+ if (((TkWindow *) tkwin)->mainPtr == NULL) {
+ ((TkWindow *) tkwin)->mainPtr = mainPtr;
+ }
+
+ /*
+ * If this API call fails then we will fallback to setting these named
+ * fonts from script in ttk/fonts.tcl. So far I've only seen it fail when
+ * WINVER has been defined for a higher platform than we are running on.
+ * (i.e. WINVER=0x0600 and running on XP).
+ */
+
+ ZeroMemory(&ncMetrics, sizeof(ncMetrics));
+ ncMetrics.cbSize = sizeof(ncMetrics);
+ if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
+ sizeof(ncMetrics), &ncMetrics, 0)) {
+ CreateNamedSystemLogFont(interp, tkwin, "TkDefaultFont",
+ &ncMetrics.lfMessageFont);
+ CreateNamedSystemLogFont(interp, tkwin, "TkHeadingFont",
+ &ncMetrics.lfMessageFont);
+ CreateNamedSystemLogFont(interp, tkwin, "TkTextFont",
+ &ncMetrics.lfMessageFont);
+ CreateNamedSystemLogFont(interp, tkwin, "TkMenuFont",
+ &ncMetrics.lfMenuFont);
+ CreateNamedSystemLogFont(interp, tkwin, "TkTooltipFont",
+ &ncMetrics.lfStatusFont);
+ CreateNamedSystemLogFont(interp, tkwin, "TkCaptionFont",
+ &ncMetrics.lfCaptionFont);
+ CreateNamedSystemLogFont(interp, tkwin, "TkSmallCaptionFont",
+ &ncMetrics.lfSmCaptionFont);
+ }
+
+ iconMetrics.cbSize = sizeof(iconMetrics);
+ if (SystemParametersInfo(SPI_GETICONMETRICS, sizeof(iconMetrics),
+ &iconMetrics, 0)) {
+ CreateNamedSystemLogFont(interp, tkwin, "TkIconFont",
+ &iconMetrics.lfFont);
+ }
+
+ /*
+ * Identify an available fixed font. Equivalent to ANSI_FIXED_FONT but
+ * more reliable on Russian Windows.
+ */
+
+ {
+ LOGFONT lfFixed = {
+ 0, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
+ 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, TEXT("")
+ };
+ long pointSize, dpi;
+ HDC hdc = GetDC(NULL);
+ dpi = GetDeviceCaps(hdc, LOGPIXELSY);
+ pointSize = -MulDiv(ncMetrics.lfMessageFont.lfHeight, 72, dpi);
+ lfFixed.lfHeight = -MulDiv(pointSize+1, dpi, 72);
+ ReleaseDC(NULL, hdc);
+ CreateNamedSystemLogFont(interp, tkwin, "TkFixedFont", &lfFixed);
+ }
+
+ /*
+ * Setup the remaining standard Tk font names as named fonts.
+ */
+
+ for (mapPtr = systemMap; mapPtr->strKey != NULL; mapPtr++) {
+ hFont = (HFONT) GetStockObject(mapPtr->numKey);
+ CreateNamedSystemFont(interp, tkwin, mapPtr->strKey, hFont);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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. NULL is never returned.
+ *
+ * Every call to this procedure returns a new TkFont structure, even if
+ * the specified attributes have already been seen before. The caller
+ * should call TkpDeleteFont() to free the platform- specific data when
+ * the font is no longer needed.
+ *
+ * The caller is responsible for initializing the memory associated with
+ * the generic TkFont when this function returns and releasing the
+ * contents of the generic TkFont before calling TkpDeleteFont().
+ *
+ * Side effects:
+ * 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. */
+{
+ int i, j;
+ HDC hdc;
+ HWND hwnd;
+ HFONT hFont;
+ Window window;
+ WinFont *fontPtr;
+ const char *const *const *fontFallbacks;
+ Tk_Uid faceName, fallback, actualName;
+
+ tkwin = (Tk_Window) ((TkWindow *) tkwin)->mainPtr->winPtr;
+ window = Tk_WindowId(tkwin);
+ hwnd = (window == None) ? NULL : TkWinGetHWND(window);
+ hdc = GetDC(hwnd);
+
+ /*
+ * Algorithm to get the closest font name to the one requested.
+ *
+ * try fontname
+ * try all aliases for fontname
+ * foreach fallback for fontname
+ * try the fallback
+ * try all aliases for the fallback
+ */
+
+ faceName = faPtr->family;
+ if (faceName != NULL) {
+ actualName = FamilyOrAliasExists(hdc, faceName);
+ if (actualName != NULL) {
+ faceName = actualName;
+ goto found;
+ }
+ fontFallbacks = TkFontGetFallbacks();
+ for (i = 0; fontFallbacks[i] != NULL; i++) {
+ for (j = 0; (fallback = fontFallbacks[i][j]) != NULL; j++) {
+ if (strcasecmp(faceName, fallback) == 0) {
+ break;
+ }
+ }
+ if (fallback != NULL) {
+ for (j = 0; (fallback = fontFallbacks[i][j]) != NULL; j++) {
+ actualName = FamilyOrAliasExists(hdc, fallback);
+ if (actualName != NULL) {
+ faceName = actualName;
+ goto found;
+ }
+ }
+ }
+ }
+ }
+
+ found:
+ ReleaseDC(hwnd, hdc);
+
+ hFont = GetScreenFont(faPtr, faceName,
+ (int)(TkFontGetPixels(tkwin, faPtr->size) + 0.5), 0.0);
+ if (tkFontPtr == NULL) {
+ fontPtr = ckalloc(sizeof(WinFont));
+ } else {
+ fontPtr = (WinFont *) tkFontPtr;
+ ReleaseFont(fontPtr);
+ }
+ InitFont(tkwin, hFont, faPtr->overstrike, fontPtr);
+
+ 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. */
+{
+ WinFont *fontPtr;
+
+ fontPtr = (WinFont *) tkFontPtr;
+ ReleaseFont(fontPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkpGetFontFamilies, WinFontFamilyEnumProc --
+ *
+ * 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. */
+{
+ HDC hdc;
+ HWND hwnd;
+ Window window;
+ Tcl_Obj *resultObj;
+
+ window = Tk_WindowId(tkwin);
+ hwnd = (window == None) ? NULL : TkWinGetHWND(window);
+ hdc = GetDC(hwnd);
+ resultObj = Tcl_NewObj();
+
+ /*
+ * On any version NT, there may fonts with international names. Use the
+ * NT-only Unicode version of EnumFontFamilies to get the font names. If
+ * we used the ANSI version on a non-internationalized version of NT, we
+ * would get font names with '?' replacing all the international
+ * characters.
+ *
+ * On a non-internationalized verson of 95, fonts with international names
+ * are not allowed, so the ANSI version of EnumFontFamilies will work. On
+ * an internationalized version of 95, there may be fonts with
+ * international names; the ANSI version will work, fetching the name in
+ * the system code page. Can't use the Unicode version of EnumFontFamilies
+ * because it only exists under NT.
+ */
+
+ EnumFontFamilies(hdc, NULL, (FONTENUMPROC) WinFontFamilyEnumProc,
+ (LPARAM) resultObj);
+ ReleaseDC(hwnd, hdc);
+ Tcl_SetObjResult(interp, resultObj);
+}
+
+static int CALLBACK
+WinFontFamilyEnumProc(
+ ENUMLOGFONT *lfPtr, /* Logical-font data. */
+ NEWTEXTMETRIC *tmPtr, /* Physical-font data (not used). */
+ int fontType, /* Type of font (not used). */
+ LPARAM lParam) /* Result object to hold result. */
+{
+ char *faceName = (char *) lfPtr->elfLogFont.lfFaceName;
+ Tcl_Obj *resultObj = (Tcl_Obj *) lParam;
+ Tcl_DString faceString;
+
+ Tcl_ExternalToUtfDString(systemEncoding, faceName, -1, &faceString);
+ Tcl_ListObjAppendElement(NULL, resultObj, Tcl_NewStringObj(
+ Tcl_DStringValue(&faceString), Tcl_DStringLength(&faceString)));
+ Tcl_DStringFree(&faceString);
+ return 1;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * 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, /* Interp to hold result. */
+ Tk_Font tkfont) /* Font object to query. */
+{
+ int i;
+ WinFont *fontPtr;
+ FontFamily *familyPtr;
+ Tcl_Obj *resultPtr, *strPtr;
+
+ resultPtr = Tcl_NewObj();
+ fontPtr = (WinFont *) tkfont;
+ for (i = 0; i < fontPtr->numSubFonts; i++) {
+ familyPtr = fontPtr->subFontArray[i].familyPtr;
+ strPtr = Tcl_NewStringObj(familyPtr->faceName, -1);
+ Tcl_ListObjAppendElement(NULL, resultPtr, strPtr);
+ }
+ 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 */
+{
+ WinFont *fontPtr = (WinFont *) tkfont;
+ /* Structure describing the logical font */
+ HDC hdc = GetDC(fontPtr->hwnd);
+ /* GDI device context */
+ 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 */
+ FontFamily *familyPtr = thisSubFontPtr->familyPtr;
+ HFONT oldfont; /* Saved font from the device context */
+ TEXTMETRIC tm; /* Font metrics of the selected subfont */
+
+ /*
+ * Get the font attributes.
+ */
+
+ oldfont = SelectObject(hdc, thisSubFontPtr->hFont0);
+ GetTextMetrics(hdc, &tm);
+ SelectObject(hdc, oldfont);
+ ReleaseDC(fontPtr->hwnd, hdc);
+ faPtr->family = familyPtr->faceName;
+ faPtr->size = TkFontGetPoints(tkwin,
+ (double)(tm.tmInternalLeading - tm.tmHeight));
+ faPtr->weight = (tm.tmWeight > FW_MEDIUM) ? TK_FW_BOLD : TK_FW_NORMAL;
+ faPtr->slant = tm.tmItalic ? TK_FS_ITALIC : TK_FS_ROMAN;
+ faPtr->underline = (tm.tmUnderlined != 0);
+ faPtr->overstrike = fontPtr->font.fa.overstrike;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_MeasureChars --
+ *
+ * Determine the number of bytes 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 fits on this line.
+ * TK_WHOLE_WORDS means stop on a word
+ * boundary, if possible. TK_AT_LEAST_ONE
+ * means return at least one character (or at
+ * least the first partial word in case
+ * TK_WHOLE_WORDS is also set) even if no
+ * characters (words) fit. */
+ int *lengthPtr) /* Filled with x-location just after the
+ * terminating character. */
+{
+ HDC hdc;
+ HFONT oldFont;
+ WinFont *fontPtr;
+ int curX, moretomeasure;
+ int ch;
+ SIZE size;
+ FontFamily *familyPtr;
+ Tcl_DString runString;
+ SubFont *thisSubFontPtr, *lastSubFontPtr;
+ const char *p, *end, *next = NULL, *start;
+
+ if (numBytes == 0) {
+ *lengthPtr = 0;
+ return 0;
+ }
+
+ fontPtr = (WinFont *) tkfont;
+
+ hdc = GetDC(fontPtr->hwnd);
+ lastSubFontPtr = &fontPtr->subFontArray[0];
+ oldFont = SelectObject(hdc, lastSubFontPtr->hFont0);
+
+ /*
+ * 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.
+ */
+
+ moretomeasure = 0;
+ curX = 0;
+ start = source;
+ end = start + numBytes;
+ for (p = start; p < end; ) {
+ next = p + TkUtfToUniChar(p, &ch);
+ thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr);
+ if (thisSubFontPtr != lastSubFontPtr) {
+ familyPtr = lastSubFontPtr->familyPtr;
+ Tcl_UtfToExternalDString(familyPtr->encoding, start,
+ (int) (p - start), &runString);
+ size.cx = 0;
+ familyPtr->getTextExtentPoint32Proc(hdc,
+ (TCHAR *)Tcl_DStringValue(&runString),
+ Tcl_DStringLength(&runString) >> familyPtr->isWideFont,
+ &size);
+ Tcl_DStringFree(&runString);
+ if (maxLength >= 0 && (curX+size.cx) > maxLength) {
+ moretomeasure = 1;
+ break;
+ }
+ curX += size.cx;
+ lastSubFontPtr = thisSubFontPtr;
+ start = p;
+
+ SelectObject(hdc, lastSubFontPtr->hFont0);
+ }
+ p = next;
+ }
+
+ if (!moretomeasure) {
+ /*
+ * We get here if the previous loop was just finished normally,
+ * without a break. Just measure the last run and that's it.
+ */
+
+ familyPtr = lastSubFontPtr->familyPtr;
+ Tcl_UtfToExternalDString(familyPtr->encoding, start,
+ (int) (p - start), &runString);
+ size.cx = 0;
+ familyPtr->getTextExtentPoint32Proc(hdc, (TCHAR *) Tcl_DStringValue(&runString),
+ Tcl_DStringLength(&runString) >> familyPtr->isWideFont,
+ &size);
+ Tcl_DStringFree(&runString);
+ if (maxLength >= 0 && (curX+size.cx) > maxLength) {
+ moretomeasure = 1;
+ } else {
+ curX += size.cx;
+ p = end;
+ }
+ }
+
+ if (moretomeasure) {
+ /*
+ * We get here if the measurement of the last run was over the
+ * maxLength limit. We need to restart this run and do it char by
+ * char, but always in context with the previous text to account for
+ * kerning (especially italics).
+ */
+
+ char buf[16];
+ int dstWrote;
+ int lastSize = 0;
+
+ familyPtr = lastSubFontPtr->familyPtr;
+ Tcl_DStringInit(&runString);
+ for (p = start; p < end; ) {
+ next = p + TkUtfToUniChar(p, &ch);
+ Tcl_UtfToExternal(NULL, familyPtr->encoding, p,
+ (int) (next - p), 0, NULL, buf, sizeof(buf), NULL,
+ &dstWrote, NULL);
+ Tcl_DStringAppend(&runString,buf,dstWrote);
+ size.cx = 0;
+ familyPtr->getTextExtentPoint32Proc(hdc,
+ (TCHAR *) Tcl_DStringValue(&runString),
+ Tcl_DStringLength(&runString) >> familyPtr->isWideFont,
+ &size);
+ if ((curX+size.cx) > maxLength) {
+ break;
+ }
+ lastSize = size.cx;
+ p = next;
+ }
+ Tcl_DStringFree(&runString);
+
+ /*
+ * "p" points to the first character that doesn't fit in the desired
+ * span. Look at the flags to figure out whether to include this next
+ * character.
+ */
+
+ if ((p < end) && (((flags & TK_PARTIAL_OK) && (curX != maxLength))
+ || ((p==source) && (flags&TK_AT_LEAST_ONE) && (curX==0)))) {
+ /*
+ * Include the first character that didn't quite fit in the
+ * desired span. The width returned will include the width of that
+ * extra character.
+ */
+
+ p = next;
+ curX += size.cx;
+ } else {
+ curX += lastSize;
+ }
+ }
+
+ SelectObject(hdc, oldFont);
+ ReleaseDC(fontPtr->hwnd, hdc);
+
+ if ((flags & TK_WHOLE_WORDS) && (p < end)) {
+ /*
+ * Scan the string for the last word break and than repeat the whole
+ * procedure without the maxLength limit or any flags.
+ */
+
+ const char *lastWordBreak = NULL;
+ int ch2;
+
+ end = p;
+ p = source;
+ ch = ' ';
+ while (p < end) {
+ next = p + TkUtfToUniChar(p, &ch2);
+ if ((ch != ' ') && (ch2 == ' ')) {
+ lastWordBreak = p;
+ }
+ p = next;
+ ch = ch2;
+ }
+
+ if (lastWordBreak != NULL) {
+ return Tk_MeasureChars(tkfont, source, lastWordBreak-source,
+ -1, 0, lengthPtr);
+ }
+ if (flags & TK_AT_LEAST_ONE) {
+ p = end;
+ } else {
+ p = source;
+ curX = 0;
+ }
+ }
+
+ *lengthPtr = curX;
+ return p - source;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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 Windows 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.
+ *
+ * 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. */
+{
+ HDC dc;
+ WinFont *fontPtr;
+ TkWinDCState state;
+
+ fontPtr = (WinFont *) gc->font;
+ display->request++;
+
+ if (drawable == None) {
+ return;
+ }
+
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+
+ if ((gc->clip_mask != None) &&
+ ((TkpClipMask *) gc->clip_mask)->type == TKP_CLIP_REGION) {
+ SelectClipRgn(dc, (HRGN)((TkpClipMask *)gc->clip_mask)->value.region);
+ }
+
+ if ((gc->fill_style == FillStippled
+ || gc->fill_style == FillOpaqueStippled)
+ && gc->stipple != None) {
+ TkWinDrawable *twdPtr = (TkWinDrawable *) gc->stipple;
+ HBRUSH oldBrush, stipple;
+ HBITMAP oldBitmap, bitmap;
+ HDC dcMem;
+ TEXTMETRIC tm;
+ SIZE size;
+
+ if (twdPtr->type != TWD_BITMAP) {
+ Tcl_Panic("unexpected drawable type in stipple");
+ }
+
+ /*
+ * Select stipple pattern into destination dc.
+ */
+
+ dcMem = CreateCompatibleDC(dc);
+
+ stipple = CreatePatternBrush(twdPtr->bitmap.handle);
+ SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL);
+ oldBrush = SelectObject(dc, stipple);
+
+ SetTextAlign(dcMem, TA_LEFT | TA_BASELINE);
+ SetTextColor(dcMem, gc->foreground);
+ SetBkMode(dcMem, TRANSPARENT);
+ SetBkColor(dcMem, RGB(0, 0, 0));
+
+ /*
+ * Compute the bounding box and create a compatible bitmap.
+ */
+
+ GetTextExtentPointA(dcMem, source, numBytes, &size);
+ GetTextMetrics(dcMem, &tm);
+ size.cx -= tm.tmOverhang;
+ bitmap = CreateCompatibleBitmap(dc, size.cx, size.cy);
+ oldBitmap = SelectObject(dcMem, bitmap);
+
+ /*
+ * The following code is tricky because fonts are rendered in multiple
+ * colors. First we draw onto a black background and copy the white
+ * bits. Then we draw onto a white background and copy the black bits.
+ * Both the foreground and background bits of the font are ANDed with
+ * the stipple pattern as they are copied.
+ */
+
+ PatBlt(dcMem, 0, 0, size.cx, size.cy, BLACKNESS);
+ MultiFontTextOut(dc, fontPtr, source, numBytes, x, y, 0.0);
+ BitBlt(dc, x, y - tm.tmAscent, size.cx, size.cy, dcMem,
+ 0, 0, 0xEA02E9);
+ PatBlt(dcMem, 0, 0, size.cx, size.cy, WHITENESS);
+ MultiFontTextOut(dc, fontPtr, source, numBytes, x, y, 0.0);
+ BitBlt(dc, x, y - tm.tmAscent, size.cx, size.cy, dcMem,
+ 0, 0, 0x8A0E06);
+
+ /*
+ * Destroy the temporary bitmap and restore the device context.
+ */
+
+ SelectObject(dcMem, oldBitmap);
+ DeleteObject(bitmap);
+ DeleteDC(dcMem);
+ SelectObject(dc, oldBrush);
+ DeleteObject(stipple);
+ } else if (gc->function == GXcopy) {
+ SetTextAlign(dc, TA_LEFT | TA_BASELINE);
+ SetTextColor(dc, gc->foreground);
+ SetBkMode(dc, TRANSPARENT);
+ MultiFontTextOut(dc, fontPtr, source, numBytes, x, y, 0.0);
+ } else {
+ HBITMAP oldBitmap, bitmap;
+ HDC dcMem;
+ TEXTMETRIC tm;
+ SIZE size;
+
+ dcMem = CreateCompatibleDC(dc);
+
+ SetTextAlign(dcMem, TA_LEFT | TA_BASELINE);
+ SetTextColor(dcMem, gc->foreground);
+ SetBkMode(dcMem, TRANSPARENT);
+ SetBkColor(dcMem, RGB(0, 0, 0));
+
+ /*
+ * Compute the bounding box and create a compatible bitmap.
+ */
+
+ GetTextExtentPointA(dcMem, source, numBytes, &size);
+ GetTextMetrics(dcMem, &tm);
+ size.cx -= tm.tmOverhang;
+ bitmap = CreateCompatibleBitmap(dc, size.cx, size.cy);
+ oldBitmap = SelectObject(dcMem, bitmap);
+
+ MultiFontTextOut(dcMem, fontPtr, source, numBytes, 0, tm.tmAscent,
+ 0.0);
+ BitBlt(dc, x, y - tm.tmAscent, size.cx, size.cy, dcMem,
+ 0, 0, (DWORD) tkpWinBltModes[gc->function]);
+
+ /*
+ * Destroy the temporary bitmap and restore the device context.
+ */
+
+ SelectObject(dcMem, oldBitmap);
+ DeleteObject(bitmap);
+ DeleteDC(dcMem);
+ }
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+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, /* Coordinates at which to place origin of
+ * string when drawing. */
+ double angle)
+{
+ HDC dc;
+ WinFont *fontPtr;
+ TkWinDCState state;
+
+ fontPtr = (WinFont *) gc->font;
+ display->request++;
+
+ if (drawable == None) {
+ return;
+ }
+
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+
+ if ((gc->clip_mask != None) &&
+ ((TkpClipMask *) gc->clip_mask)->type == TKP_CLIP_REGION) {
+ SelectClipRgn(dc, (HRGN)((TkpClipMask *)gc->clip_mask)->value.region);
+ }
+
+ if ((gc->fill_style == FillStippled
+ || gc->fill_style == FillOpaqueStippled)
+ && gc->stipple != None) {
+ TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple;
+ HBRUSH oldBrush, stipple;
+ HBITMAP oldBitmap, bitmap;
+ HDC dcMem;
+ TEXTMETRIC tm;
+ SIZE size;
+
+ if (twdPtr->type != TWD_BITMAP) {
+ Tcl_Panic("unexpected drawable type in stipple");
+ }
+
+ /*
+ * Select stipple pattern into destination dc.
+ */
+
+ dcMem = CreateCompatibleDC(dc);
+
+ stipple = CreatePatternBrush(twdPtr->bitmap.handle);
+ SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL);
+ oldBrush = SelectObject(dc, stipple);
+
+ SetTextAlign(dcMem, TA_LEFT | TA_BASELINE);
+ SetTextColor(dcMem, gc->foreground);
+ SetBkMode(dcMem, TRANSPARENT);
+ SetBkColor(dcMem, RGB(0, 0, 0));
+
+ /*
+ * Compute the bounding box and create a compatible bitmap.
+ */
+
+ GetTextExtentPointA(dcMem, source, numBytes, &size);
+ GetTextMetrics(dcMem, &tm);
+ size.cx -= tm.tmOverhang;
+ bitmap = CreateCompatibleBitmap(dc, size.cx, size.cy);
+ oldBitmap = SelectObject(dcMem, bitmap);
+
+ /*
+ * The following code is tricky because fonts are rendered in multiple
+ * colors. First we draw onto a black background and copy the white
+ * bits. Then we draw onto a white background and copy the black bits.
+ * Both the foreground and background bits of the font are ANDed with
+ * the stipple pattern as they are copied.
+ */
+
+ PatBlt(dcMem, 0, 0, size.cx, size.cy, BLACKNESS);
+ MultiFontTextOut(dc, fontPtr, source, numBytes, (int)x, (int)y, angle);
+ BitBlt(dc, (int)x, (int)y - tm.tmAscent, size.cx, size.cy, dcMem,
+ 0, 0, 0xEA02E9);
+ PatBlt(dcMem, 0, 0, size.cx, size.cy, WHITENESS);
+ MultiFontTextOut(dc, fontPtr, source, numBytes, (int)x, (int)y, angle);
+ BitBlt(dc, (int)x, (int)y - tm.tmAscent, size.cx, size.cy, dcMem,
+ 0, 0, 0x8A0E06);
+
+ /*
+ * Destroy the temporary bitmap and restore the device context.
+ */
+
+ SelectObject(dcMem, oldBitmap);
+ DeleteObject(bitmap);
+ DeleteDC(dcMem);
+ SelectObject(dc, oldBrush);
+ DeleteObject(stipple);
+ } else if (gc->function == GXcopy) {
+ SetTextAlign(dc, TA_LEFT | TA_BASELINE);
+ SetTextColor(dc, gc->foreground);
+ SetBkMode(dc, TRANSPARENT);
+ MultiFontTextOut(dc, fontPtr, source, numBytes, (int)x, (int)y, angle);
+ } else {
+ HBITMAP oldBitmap, bitmap;
+ HDC dcMem;
+ TEXTMETRIC tm;
+ SIZE size;
+
+ dcMem = CreateCompatibleDC(dc);
+
+ SetTextAlign(dcMem, TA_LEFT | TA_BASELINE);
+ SetTextColor(dcMem, gc->foreground);
+ SetBkMode(dcMem, TRANSPARENT);
+ SetBkColor(dcMem, RGB(0, 0, 0));
+
+ /*
+ * Compute the bounding box and create a compatible bitmap.
+ */
+
+ GetTextExtentPointA(dcMem, source, numBytes, &size);
+ GetTextMetrics(dcMem, &tm);
+ size.cx -= tm.tmOverhang;
+ bitmap = CreateCompatibleBitmap(dc, size.cx, size.cy);
+ oldBitmap = SelectObject(dcMem, bitmap);
+
+ MultiFontTextOut(dcMem, fontPtr, source, numBytes, 0, tm.tmAscent,
+ angle);
+ BitBlt(dc, (int)x, (int)y - tm.tmAscent, size.cx, size.cy, dcMem,
+ 0, 0, (DWORD) tkpWinBltModes[gc->function]);
+
+ /*
+ * Destroy the temporary bitmap and restore the device context.
+ */
+
+ SelectObject(dcMem, oldBitmap);
+ DeleteObject(bitmap);
+ DeleteDC(dcMem);
+ }
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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 Windows
+ * 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);
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * MultiFontTextOut --
+ *
+ * Helper function for Tk_DrawChars. Draws characters, using the various
+ * screen fonts in fontPtr to draw multilingual characters. Note: No
+ * bidirectional support.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets drawn on the screen. Contents of fontPtr may be
+ * modified if more subfonts were loaded in order to draw all the
+ * multilingual characters in the given string.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static void
+MultiFontTextOut(
+ HDC hdc, /* HDC to draw into. */
+ WinFont *fontPtr, /* Contains set of fonts to use when drawing
+ * following string. */
+ const char *source, /* Potentially multilingual UTF-8 string. */
+ int numBytes, /* Length of string in bytes. */
+ int x, int y, /* Coordinates at which to place origin of
+ * string when drawing. */
+ double angle)
+{
+ int ch;
+ SIZE size;
+ HFONT oldFont;
+ FontFamily *familyPtr;
+ Tcl_DString runString;
+ const char *p, *end, *next;
+ SubFont *lastSubFontPtr, *thisSubFontPtr;
+ TEXTMETRIC tm;
+
+ lastSubFontPtr = &fontPtr->subFontArray[0];
+ oldFont = SelectFont(hdc, fontPtr, lastSubFontPtr, angle);
+ GetTextMetrics(hdc, &tm);
+
+ end = source + numBytes;
+ for (p = source; p < end; ) {
+ next = p + TkUtfToUniChar(p, &ch);
+ thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr);
+
+ /*
+ * The drawing API has a limit of 32767 pixels in one go.
+ * To avoid spending time on a rare case we do not measure each char,
+ * instead we limit to drawing chunks of 200 bytes since that works
+ * well in practice.
+ */
+
+ if ((thisSubFontPtr != lastSubFontPtr) || (p-source > 200)) {
+ if (p > source) {
+ familyPtr = lastSubFontPtr->familyPtr;
+ Tcl_UtfToExternalDString(familyPtr->encoding, source,
+ (int) (p - source), &runString);
+ familyPtr->textOutProc(hdc, x-(tm.tmOverhang/2), y,
+ (TCHAR *)Tcl_DStringValue(&runString),
+ Tcl_DStringLength(&runString)>>familyPtr->isWideFont);
+ familyPtr->getTextExtentPoint32Proc(hdc,
+ (TCHAR *)Tcl_DStringValue(&runString),
+ Tcl_DStringLength(&runString) >> familyPtr->isWideFont,
+ &size);
+ x += size.cx;
+ Tcl_DStringFree(&runString);
+ }
+ lastSubFontPtr = thisSubFontPtr;
+ source = p;
+ SelectFont(hdc, fontPtr, lastSubFontPtr, angle);
+ GetTextMetrics(hdc, &tm);
+ }
+ p = next;
+ }
+ if (p > source) {
+ familyPtr = lastSubFontPtr->familyPtr;
+ Tcl_UtfToExternalDString(familyPtr->encoding, source,
+ (int) (p - source), &runString);
+ familyPtr->textOutProc(hdc, x-(tm.tmOverhang/2), y,
+ (TCHAR *)Tcl_DStringValue(&runString),
+ Tcl_DStringLength(&runString) >> familyPtr->isWideFont);
+ Tcl_DStringFree(&runString);
+ }
+ SelectObject(hdc, oldFont);
+}
+
+static inline HFONT
+SelectFont(
+ HDC hdc,
+ WinFont *fontPtr,
+ SubFont *subFontPtr,
+ double angle)
+{
+ if (angle == 0.0) {
+ return SelectObject(hdc, subFontPtr->hFont0);
+ } else if (angle == subFontPtr->angle) {
+ return SelectObject(hdc, subFontPtr->hFontAngled);
+ } else {
+ if (subFontPtr->hFontAngled) {
+ DeleteObject(subFontPtr->hFontAngled);
+ }
+ subFontPtr->hFontAngled = GetScreenFont(&fontPtr->font.fa,
+ subFontPtr->familyPtr->faceName, fontPtr->pixelSize, angle);
+ if (subFontPtr->hFontAngled == NULL) {
+ return SelectObject(hdc, subFontPtr->hFont0);
+ }
+ subFontPtr->angle = angle;
+ return SelectObject(hdc, subFontPtr->hFontAngled);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * InitFont --
+ *
+ * Helper for TkpGetNativeFont() and TkpGetFontFromAttributes().
+ * Initializes the memory for a new WinFont that wraps the
+ * platform-specific data.
+ *
+ * The caller is responsible for initializing the fields of the WinFont
+ * 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, /* Main window of interp in which font will be
+ * used, for getting HDC. */
+ HFONT hFont, /* Windows token for font. */
+ int overstrike, /* The overstrike attribute of logfont used to
+ * allocate this font. For some reason, the
+ * TEXTMETRICs may contain incorrect info in
+ * the tmStruckOut field. */
+ WinFont *fontPtr) /* Filled with information constructed from
+ * the above arguments. */
+{
+ HDC hdc;
+ HWND hwnd;
+ HFONT oldFont;
+ TEXTMETRIC tm;
+ Window window;
+ TkFontMetrics *fmPtr;
+ Tcl_Encoding encoding;
+ Tcl_DString faceString;
+ TkFontAttributes *faPtr;
+ TCHAR buf[LF_FACESIZE];
+
+ window = Tk_WindowId(tkwin);
+ hwnd = (window == None) ? NULL : TkWinGetHWND(window);
+ hdc = GetDC(hwnd);
+ oldFont = SelectObject(hdc, hFont);
+
+ GetTextMetrics(hdc, &tm);
+
+ /*
+ * On any version NT, there may fonts with international names. Use the
+ * NT-only Unicode version of GetTextFace to get the font's name. If we
+ * used the ANSI version on a non-internationalized version of NT, we
+ * would get a font name with '?' replacing all the international
+ * characters.
+ *
+ * On a non-internationalized verson of 95, fonts with international names
+ * are not allowed, so the ANSI version of GetTextFace will work. On an
+ * internationalized version of 95, there may be fonts with international
+ * names; the ANSI version will work, fetching the name in the
+ * international system code page. Can't use the Unicode version of
+ * GetTextFace because it only exists under NT.
+ */
+
+ GetTextFace(hdc, LF_FACESIZE, buf);
+ Tcl_ExternalToUtfDString(systemEncoding, (char *) buf, -1, &faceString);
+
+ fontPtr->font.fid = (Font) fontPtr;
+ fontPtr->hwnd = hwnd;
+ fontPtr->pixelSize = tm.tmHeight - tm.tmInternalLeading;
+
+ faPtr = &fontPtr->font.fa;
+ faPtr->family = Tk_GetUid(Tcl_DStringValue(&faceString));
+
+ faPtr->size =
+ TkFontGetPoints(tkwin, (double)-(fontPtr->pixelSize));
+ faPtr->weight =
+ (tm.tmWeight > FW_MEDIUM) ? TK_FW_BOLD : TK_FW_NORMAL;
+ faPtr->slant = (tm.tmItalic != 0) ? TK_FS_ITALIC : TK_FS_ROMAN;
+ faPtr->underline = (tm.tmUnderlined != 0) ? 1 : 0;
+ faPtr->overstrike = overstrike;
+
+ fmPtr = &fontPtr->font.fm;
+ fmPtr->ascent = tm.tmAscent;
+ fmPtr->descent = tm.tmDescent;
+ fmPtr->maxWidth = tm.tmMaxCharWidth;
+ fmPtr->fixed = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH);
+
+ fontPtr->numSubFonts = 1;
+ fontPtr->subFontArray = fontPtr->staticSubFonts;
+ InitSubFont(hdc, hFont, 1, &fontPtr->subFontArray[0]);
+
+ encoding = fontPtr->subFontArray[0].familyPtr->encoding;
+ if (encoding == TkWinGetUnicodeEncoding()) {
+ GetCharWidth(hdc, 0, BASE_CHARS - 1, fontPtr->widths);
+ } else {
+ GetCharWidthA(hdc, 0, BASE_CHARS - 1, fontPtr->widths);
+ }
+ Tcl_DStringFree(&faceString);
+
+ SelectObject(hdc, oldFont);
+ ReleaseDC(hwnd, hdc);
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * ReleaseFont --
+ *
+ * Called to release the windows-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(
+ WinFont *fontPtr) /* The font to delete. */
+{
+ int i;
+
+ for (i = 0; i < fontPtr->numSubFonts; i++) {
+ ReleaseSubFont(&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 inline void
+InitSubFont(
+ HDC hdc, /* HDC in which font can be selected. */
+ HFONT hFont, /* 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->hFont0 = hFont;
+ subFontPtr->familyPtr = AllocFontFamily(hdc, hFont, base);
+ subFontPtr->fontMap = subFontPtr->familyPtr->fontMap;
+ subFontPtr->hFontAngled = NULL;
+ subFontPtr->angle = 0.0;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * 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 inline void
+ReleaseSubFont(
+ SubFont *subFontPtr) /* The SubFont to delete. */
+{
+ DeleteObject(subFontPtr->hFont0);
+ if (subFontPtr->hFontAngled) {
+ DeleteObject(subFontPtr->hFontAngled);
+ }
+ 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(
+ HDC hdc, /* HDC in which font can be selected. */
+ HFONT hFont, /* 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. */
+{
+ Tk_Uid faceName;
+ FontFamily *familyPtr;
+ Tcl_DString faceString;
+ Tcl_Encoding encoding;
+ TCHAR buf[LF_FACESIZE];
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ hFont = SelectObject(hdc, hFont);
+ GetTextFace(hdc, LF_FACESIZE, buf);
+ Tcl_ExternalToUtfDString(systemEncoding, (char *) buf, -1, &faceString);
+ faceName = Tk_GetUid(Tcl_DStringValue(&faceString));
+ Tcl_DStringFree(&faceString);
+ hFont = SelectObject(hdc, hFont);
+
+ familyPtr = tsdPtr->fontFamilyList;
+ for ( ; familyPtr != NULL; familyPtr = familyPtr->nextPtr) {
+ if (familyPtr->faceName == faceName) {
+ 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->faceName = faceName;
+
+ /*
+ * An initial refCount of 2 means that FontFamily information will persist
+ * even when the SubFont that loaded the FontFamily is released. Change it
+ * to 1 to cause FontFamilies to be unloaded when not in use.
+ */
+
+ familyPtr->refCount = 2;
+
+ familyPtr->segCount = LoadFontRanges(hdc, hFont, &familyPtr->startCount,
+ &familyPtr->endCount, &familyPtr->isSymbolFont);
+
+ encoding = NULL;
+ if (familyPtr->isSymbolFont != 0) {
+ /*
+ * Symbol fonts are handled specially. For instance, Unicode 0393
+ * (GREEK CAPITAL GAMMA) must be mapped to Symbol character 0047
+ * (GREEK CAPITAL GAMMA), because the Symbol font doesn't have a GREEK
+ * CAPITAL GAMMA at location 0393. If Tk interpreted the Symbol font
+ * using the Unicode encoding, it would decide that the Symbol font
+ * has no GREEK CAPITAL GAMMA, because the Symbol encoding (of course)
+ * reports that character 0393 doesn't exist.
+ *
+ * With non-symbol Windows fonts, such as Times New Roman, if the font
+ * has a GREEK CAPITAL GAMMA, it will be found in the correct Unicode
+ * location (0393); the GREEK CAPITAL GAMMA will not be off hiding at
+ * some other location.
+ */
+
+ encoding = Tcl_GetEncoding(NULL, faceName);
+ }
+
+ if (encoding == NULL) {
+ encoding = Tcl_GetEncoding(NULL, "unicode");
+ familyPtr->textOutProc =
+ (BOOL (WINAPI *)(HDC, int, int, TCHAR *, int)) TextOutW;
+ familyPtr->getTextExtentPoint32Proc =
+ (BOOL (WINAPI *)(HDC, TCHAR *, int, LPSIZE)) GetTextExtentPoint32W;
+ familyPtr->isWideFont = 1;
+ } else {
+ familyPtr->textOutProc =
+ (BOOL (WINAPI *)(HDC, int, int, TCHAR *, int)) TextOutA;
+ familyPtr->getTextExtentPoint32Proc =
+ (BOOL (WINAPI *)(HDC, TCHAR *, int, LPSIZE)) GetTextExtentPoint32A;
+ familyPtr->isWideFont = 0;
+ }
+
+ familyPtr->encoding = encoding;
+
+ return familyPtr;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * FreeFontFamily --
+ *
+ * Called to free a 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. */
+{
+ int i;
+ FontFamily **familyPtrPtr;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ if (familyPtr == NULL) {
+ return;
+ }
+ if (familyPtr->refCount-- > 1) {
+ return;
+ }
+ for (i = 0; i < FONTMAP_PAGES; i++) {
+ if (familyPtr->fontMap[i] != NULL) {
+ ckfree(familyPtr->fontMap[i]);
+ }
+ }
+ if (familyPtr->startCount != NULL) {
+ ckfree(familyPtr->startCount);
+ }
+ if (familyPtr->endCount != NULL) {
+ ckfree(familyPtr->endCount);
+ }
+ if (familyPtr->encoding != TkWinGetUnicodeEncoding()) {
+ Tcl_FreeEncoding(familyPtr->encoding);
+ }
+
+ /*
+ * 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.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static SubFont *
+FindSubFontForChar(
+ WinFont *fontPtr, /* The font object with which the character
+ * will be displayed. */
+ int ch, /* The Unicode character to be displayed. */
+ SubFont **subFontPtrPtr) /* Pointer to var to be fixed up if we
+ * reallocate the subfont table. */
+{
+ HDC hdc;
+ int i, j, k;
+ CanUse canUse;
+ const char *const *aliases;
+ const char *const *anyFallbacks;
+ const char *const *const *fontFallbacks;
+ const char *fallbackName;
+ SubFont *subFontPtr;
+ Tcl_DString ds;
+
+
+ if ((ch < BASE_CHARS) || (ch >= 0x10000)) {
+ return &fontPtr->subFontArray[0];
+ }
+
+ for (i = 0; i < fontPtr->numSubFonts; i++) {
+ if (FontMapLookup(&fontPtr->subFontArray[i], ch)) {
+ return &fontPtr->subFontArray[i];
+ }
+ }
+
+ /*
+ * 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);
+ hdc = GetDC(fontPtr->hwnd);
+
+ aliases = TkFontGetAliasList(fontPtr->font.fa.family);
+
+ fontFallbacks = TkFontGetFallbacks();
+ for (i = 0; fontFallbacks[i] != NULL; i++) {
+ for (j = 0; fontFallbacks[i][j] != NULL; j++) {
+ fallbackName = fontFallbacks[i][j];
+ if (strcasecmp(fallbackName, fontPtr->font.fa.family) == 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(aliases[k], fallbackName) == 0) {
+ goto tryfallbacks;
+ }
+ }
+ }
+ }
+ continue;
+
+ /*
+ * ...then see if we can use one of the fallbacks, or an alias for one
+ * of the fallbacks.
+ */
+
+ tryfallbacks:
+ for (j = 0; fontFallbacks[i][j] != NULL; j++) {
+ fallbackName = fontFallbacks[i][j];
+ subFontPtr = CanUseFallbackWithAliases(hdc, fontPtr, fallbackName,
+ ch, &ds, subFontPtrPtr);
+ if (subFontPtr != NULL) {
+ goto end;
+ }
+ }
+ }
+
+ /*
+ * See if we can use something from the global fallback list.
+ */
+
+ anyFallbacks = TkFontGetGlobalClass();
+ for (i = 0; anyFallbacks[i] != NULL; i++) {
+ fallbackName = anyFallbacks[i];
+ subFontPtr = CanUseFallbackWithAliases(hdc, fontPtr, fallbackName,
+ ch, &ds, subFontPtrPtr);
+ if (subFontPtr != NULL) {
+ goto end;
+ }
+ }
+
+ /*
+ * Try all face names available in the whole system until we find one that
+ * can be used.
+ */
+
+ canUse.hdc = hdc;
+ canUse.fontPtr = fontPtr;
+ canUse.nameTriedPtr = &ds;
+ canUse.ch = ch;
+ canUse.subFontPtr = NULL;
+ canUse.subFontPtrPtr = subFontPtrPtr;
+ EnumFontFamilies(hdc, NULL, (FONTENUMPROC) WinFontCanUseProc,
+ (LPARAM) &canUse);
+ subFontPtr = canUse.subFontPtr;
+
+ end:
+ Tcl_DStringFree(&ds);
+
+ if (subFontPtr == NULL) {
+ /*
+ * No font can display this character. We will use the base font and
+ * have it display the "unknown" character.
+ */
+
+ subFontPtr = &fontPtr->subFontArray[0];
+ FontMapInsert(subFontPtr, ch);
+ }
+ ReleaseDC(fontPtr->hwnd, hdc);
+ return subFontPtr;
+}
+
+static int CALLBACK
+WinFontCanUseProc(
+ ENUMLOGFONT *lfPtr, /* Logical-font data. */
+ NEWTEXTMETRIC *tmPtr, /* Physical-font data (not used). */
+ int fontType, /* Type of font (not used). */
+ LPARAM lParam) /* Result object to hold result. */
+{
+ int ch;
+ HDC hdc;
+ WinFont *fontPtr;
+ CanUse *canUsePtr;
+ char *fallbackName;
+ SubFont *subFontPtr;
+ Tcl_DString faceString;
+ Tcl_DString *nameTriedPtr;
+
+ canUsePtr = (CanUse *) lParam;
+ ch = canUsePtr->ch;
+ hdc = canUsePtr->hdc;
+ fontPtr = canUsePtr->fontPtr;
+ nameTriedPtr = canUsePtr->nameTriedPtr;
+
+ fallbackName = (char *) lfPtr->elfLogFont.lfFaceName;
+ Tcl_ExternalToUtfDString(systemEncoding, fallbackName, -1, &faceString);
+ fallbackName = Tcl_DStringValue(&faceString);
+
+ if (SeenName(fallbackName, nameTriedPtr) == 0) {
+ subFontPtr = CanUseFallback(hdc, fontPtr, fallbackName, ch,
+ canUsePtr->subFontPtrPtr);
+ if (subFontPtr != NULL) {
+ canUsePtr->subFontPtr = subFontPtr;
+ Tcl_DStringFree(&faceString);
+ return 0;
+ }
+ }
+ Tcl_DStringFree(&faceString);
+ return 1;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * 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 HFONT can (1) or cannot (0) display the characters on
+ * the page.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Mempry 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. */
+{
+ FontFamily *familyPtr;
+ Tcl_Encoding encoding;
+ char src[XMaxTransChars], buf[16];
+ USHORT *startCount, *endCount;
+ int i, j, bitOffset, end, segCount;
+
+ subFontPtr->fontMap[row] = ckalloc(FONTMAP_BITSPERPAGE / 8);
+ memset(subFontPtr->fontMap[row], 0, FONTMAP_BITSPERPAGE / 8);
+
+ familyPtr = subFontPtr->familyPtr;
+ encoding = familyPtr->encoding;
+
+ if (familyPtr->encoding == TkWinGetUnicodeEncoding()) {
+ /*
+ * Font is Unicode. Few fonts are going to have all characters, so
+ * examine the TrueType character existence metrics to determine what
+ * characters actually exist in this font.
+ */
+
+ segCount = familyPtr->segCount;
+ startCount = familyPtr->startCount;
+ endCount = familyPtr->endCount;
+
+ j = 0;
+ end = (row + 1) << FONTMAP_SHIFT;
+ for (i = row << FONTMAP_SHIFT; i < end; i++) {
+ for ( ; j < segCount; j++) {
+ if (endCount[j] >= i) {
+ if (startCount[j] <= i) {
+ bitOffset = i & (FONTMAP_BITSPERPAGE - 1);
+ subFontPtr->fontMap[row][bitOffset >> 3] |=
+ 1 << (bitOffset & 7);
+ }
+ break;
+ }
+ }
+ }
+ } else if (familyPtr->isSymbolFont) {
+ /*
+ * Assume that a symbol font with a known encoding has all the
+ * characters that its encoding claims it supports.
+ *
+ * The test for "encoding == unicodeEncoding" must occur before this
+ * case, to catch all symbol fonts (such as {Comic Sans MS} or
+ * Wingdings) for which we don't have encoding information; those
+ * symbol fonts are treated as if they were in the Unicode encoding
+ * and their symbolic character existence metrics are treated as if
+ * they were Unicode character existence metrics. This way, although
+ * we don't know the proper Unicode -> symbol font mapping, we can
+ * install the symbol font as the base font and access its glyphs.
+ */
+
+ end = (row + 1) << FONTMAP_SHIFT;
+ for (i = row << FONTMAP_SHIFT; i < end; i++) {
+ if (Tcl_UtfToExternal(NULL, encoding, src,
+ Tcl_UniCharToUtf(i, src), TCL_ENCODING_STOPONERROR, NULL,
+ buf, sizeof(buf), NULL, NULL, NULL) != TCL_OK) {
+ continue;
+ }
+ bitOffset = i & (FONTMAP_BITSPERPAGE - 1);
+ subFontPtr->fontMap[row][bitOffset >> 3] |= 1 << (bitOffset & 7);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CanUseFallbackWithAliases --
+ *
+ * Helper function for FindSubFontForChar. Determine if the specified
+ * face name (or an alias of the specified face name) can be used to
+ * construct a screen font that can display the given character.
+ *
+ * Results:
+ * See CanUseFallback().
+ *
+ * Side effects:
+ * If the name and/or one of its aliases was rejected, the rejected
+ * string is recorded in nameTriedPtr so that it won't be tried again.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static SubFont *
+CanUseFallbackWithAliases(
+ HDC hdc, /* HDC in which font can be selected. */
+ WinFont *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 **subFontPtrPtr) /* Variable to fixup if we reallocate the
+ * array of subfonts. */
+{
+ int i;
+ const char *const *aliases;
+ SubFont *subFontPtr;
+
+ if (SeenName(faceName, nameTriedPtr) == 0) {
+ subFontPtr = CanUseFallback(hdc, fontPtr, faceName, ch, subFontPtrPtr);
+ 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(hdc, fontPtr, aliases[i], ch,
+ subFontPtrPtr);
+ 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 it 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 font
+ * name. NULL is returned if the font object already holds a reference to
+ * the specified physical font or if the specified physical font cannot
+ * display the given character.
+ *
+ * Side effects:
+ * The font object's subFontArray is updated to contain a reference to
+ * the newly allocated SubFont.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static SubFont *
+CanUseFallback(
+ HDC hdc, /* HDC in which font can be selected. */
+ WinFont *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 **subFontPtrPtr) /* Variable to fix-up if we realloc the array
+ * of subfonts. */
+{
+ int i;
+ HFONT hFont;
+ SubFont subFont;
+
+ if (FamilyExists(hdc, faceName) == 0) {
+ return NULL;
+ }
+
+ /*
+ * Skip all fonts we've already used.
+ */
+
+ for (i = 0; i < fontPtr->numSubFonts; i++) {
+ if (faceName == fontPtr->subFontArray[i].familyPtr->faceName) {
+ return NULL;
+ }
+ }
+
+ /*
+ * Load this font and see if it has the desired character.
+ */
+
+ hFont = GetScreenFont(&fontPtr->font.fa, faceName, fontPtr->pixelSize,
+ 0.0);
+ InitSubFont(hdc, hFont, 0, &subFont);
+ if (((ch < 256) && (subFont.familyPtr->isSymbolFont))
+ || (FontMapLookup(&subFont, ch) == 0)) {
+ /*
+ * Don't use a symbol font as a fallback font for characters below
+ * 256.
+ */
+
+ ReleaseSubFont(&subFont);
+ return NULL;
+ }
+
+ if (fontPtr->numSubFonts >= SUBFONT_SPACE) {
+ SubFont *newPtr;
+
+ newPtr = ckalloc(sizeof(SubFont) * (fontPtr->numSubFonts + 1));
+ memcpy(newPtr, fontPtr->subFontArray,
+ fontPtr->numSubFonts * sizeof(SubFont));
+ if (fontPtr->subFontArray != fontPtr->staticSubFonts) {
+ ckfree(fontPtr->subFontArray);
+ }
+
+ /*
+ * Fix up the variable pointed to by subFontPtrPtr so it still points
+ * into the live array. [Bug 618872]
+ */
+
+ *subFontPtrPtr = newPtr + (*subFontPtrPtr - fontPtr->subFontArray);
+ fontPtr->subFontArray = newPtr;
+ }
+ fontPtr->subFontArray[fontPtr->numSubFonts] = subFont;
+ fontPtr->numSubFonts++;
+ return &fontPtr->subFontArray[fontPtr->numSubFonts - 1];
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetScreenFont --
+ *
+ * Given the name and other attributes, construct an HFONT. This is where
+ * all the alias and fallback substitution bottoms out.
+ *
+ * Results:
+ * The screen font that corresponds to the attributes.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static HFONT
+GetScreenFont(
+ const TkFontAttributes *faPtr,
+ /* Desired font attributes for new HFONT. */
+ const char *faceName, /* Overrides font family specified in font
+ * attributes. */
+ int pixelSize, /* Overrides size specified in font
+ * attributes. */
+ double angle) /* What is the desired orientation of the
+ * font. */
+{
+ Tcl_DString ds;
+ HFONT hFont;
+ LOGFONT lf;
+
+ memset(&lf, 0, sizeof(lf));
+ lf.lfHeight = -pixelSize;
+ lf.lfWidth = 0;
+ lf.lfEscapement = ROUND16(angle * 10);
+ lf.lfOrientation = ROUND16(angle * 10);
+ lf.lfWeight = (faPtr->weight == TK_FW_NORMAL) ? FW_NORMAL : FW_BOLD;
+ lf.lfItalic = faPtr->slant;
+ lf.lfUnderline = faPtr->underline;
+ lf.lfStrikeOut = faPtr->overstrike;
+ lf.lfCharSet = DEFAULT_CHARSET;
+ lf.lfOutPrecision = OUT_TT_PRECIS;
+ lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf.lfQuality = DEFAULT_QUALITY;
+ lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+
+ Tcl_UtfToExternalDString(systemEncoding, faceName, -1, &ds);
+ _tcsncpy(lf.lfFaceName, (TCHAR *)Tcl_DStringValue(&ds), LF_FACESIZE-1);
+ Tcl_DStringFree(&ds);
+ lf.lfFaceName[LF_FACESIZE-1] = 0;
+ hFont = CreateFontIndirect(&lf);
+ return hFont;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * FamilyExists, FamilyOrAliasExists, WinFontExistsProc --
+ *
+ * Determines if any physical screen font exists on the system with the
+ * given family name. If the family exists, then it should be possible to
+ * construct some physical screen font with that family name.
+ *
+ * Results:
+ * The return value is 0 if the specified font family does not exist,
+ * non-zero otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static int
+FamilyExists(
+ HDC hdc, /* HDC in which font family will be used. */
+ const char *faceName) /* Font family to query. */
+{
+ int result;
+ Tcl_DString faceString;
+
+ /*
+ * Just immediately rule out the following fonts, because they look so
+ * ugly on windows. The caller's fallback mechanism will cause the
+ * corresponding appropriate TrueType fonts to be selected.
+ */
+
+ if (strcasecmp(faceName, "Courier") == 0) {
+ return 0;
+ }
+ if (strcasecmp(faceName, "Times") == 0) {
+ return 0;
+ }
+ if (strcasecmp(faceName, "Helvetica") == 0) {
+ return 0;
+ }
+
+ Tcl_UtfToExternalDString(systemEncoding, faceName, -1, &faceString);
+
+ /*
+ * If the family exists, WinFontExistProc() will be called and
+ * EnumFontFamilies() will return whatever WinFontExistProc() returns. If
+ * the family doesn't exist, EnumFontFamilies() will just return a
+ * non-zero value.
+ */
+
+ result = EnumFontFamilies(hdc, (TCHAR*) Tcl_DStringValue(&faceString),
+ (FONTENUMPROC) WinFontExistProc, 0);
+ Tcl_DStringFree(&faceString);
+ return (result == 0);
+}
+
+static const char *
+FamilyOrAliasExists(
+ HDC hdc,
+ const char *faceName)
+{
+ const char *const *aliases;
+ int i;
+
+ if (FamilyExists(hdc, faceName) != 0) {
+ return faceName;
+ }
+ aliases = TkFontGetAliasList(faceName);
+ if (aliases != NULL) {
+ for (i = 0; aliases[i] != NULL; i++) {
+ if (FamilyExists(hdc, aliases[i]) != 0) {
+ return aliases[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+static int CALLBACK
+WinFontExistProc(
+ ENUMLOGFONT *lfPtr, /* Logical-font data. */
+ NEWTEXTMETRIC *tmPtr, /* Physical-font data (not used). */
+ int fontType, /* Type of font (not used). */
+ LPARAM lParam) /* EnumFontData to hold result. */
+{
+ return 0;
+}
+
+/*
+ * The following data structures are used when querying a TrueType font file
+ * to determine which characters the font supports.
+ */
+
+#pragma pack(1) /* Structures are byte aligned in file. */
+
+#define CMAPHEX 0x636d6170 /* Key for character map resource. */
+
+typedef struct CMAPTABLE {
+ USHORT version; /* Table version number (0). */
+ USHORT numTables; /* Number of encoding tables following. */
+} CMAPTABLE;
+
+typedef struct ENCODINGTABLE {
+ USHORT platform; /* Platform for which data is targeted. 3
+ * means data is for Windows. */
+ USHORT encoding; /* How characters in font are encoded. 1 means
+ * that the following subtable is keyed based
+ * on Unicode. */
+ ULONG offset; /* Byte offset from beginning of CMAPTABLE to
+ * the subtable for this encoding. */
+} ENCODINGTABLE;
+
+typedef struct ANYTABLE {
+ USHORT format; /* Format number. */
+ USHORT length; /* The actual length in bytes of this
+ * subtable. */
+ USHORT version; /* Version number (starts at 0). */
+} ANYTABLE;
+
+typedef struct BYTETABLE {
+ USHORT format; /* Format number is set to 0. */
+ USHORT length; /* The actual length in bytes of this
+ * subtable. */
+ USHORT version; /* Version number (starts at 0). */
+ BYTE glyphIdArray[256]; /* Array that maps up to 256 single-byte char
+ * codes to glyph indices. */
+} BYTETABLE;
+
+typedef struct SUBHEADER {
+ USHORT firstCode; /* First valid low byte for subHeader. */
+ USHORT entryCount; /* Number valid low bytes for subHeader. */
+ SHORT idDelta; /* Constant adder to get base glyph index. */
+ USHORT idRangeOffset; /* Byte offset from here to appropriate
+ * glyphIndexArray. */
+} SUBHEADER;
+
+typedef struct HIBYTETABLE {
+ USHORT format; /* Format number is set to 2. */
+ USHORT length; /* The actual length in bytes of this
+ * subtable. */
+ USHORT version; /* Version number (starts at 0). */
+ USHORT subHeaderKeys[256]; /* Maps high bytes to subHeaders: value is
+ * subHeader index * 8. */
+#if 0
+ SUBHEADER subHeaders[]; /* Variable-length array of SUBHEADERs. */
+ USHORT glyphIndexArray[]; /* Variable-length array containing subarrays
+ * used for mapping the low byte of 2-byte
+ * characters. */
+#endif
+} HIBYTETABLE;
+
+typedef struct SEGMENTTABLE {
+ USHORT format; /* Format number is set to 4. */
+ USHORT length; /* The actual length in bytes of this
+ * subtable. */
+ USHORT version; /* Version number (starts at 0). */
+ USHORT segCountX2; /* 2 x segCount. */
+ USHORT searchRange; /* 2 x (2**floor(log2(segCount))). */
+ USHORT entrySelector; /* log2(searchRange/2). */
+ USHORT rangeShift; /* 2 x segCount - searchRange. */
+#if 0
+ USHORT endCount[segCount] /* End characterCode for each segment. */
+ USHORT reservedPad; /* Set to 0. */
+ USHORT startCount[segCount];/* Start character code for each segment. */
+ USHORT idDelta[segCount]; /* Delta for all character in segment. */
+ USHORT idRangeOffset[segCount]; /* Offsets into glyphIdArray or 0. */
+ USHORT glyphIdArray[] /* Glyph index array. */
+#endif
+} SEGMENTTABLE;
+
+typedef struct TRIMMEDTABLE {
+ USHORT format; /* Format number is set to 6. */
+ USHORT length; /* The actual length in bytes of this
+ * subtable. */
+ USHORT version; /* Version number (starts at 0). */
+ USHORT firstCode; /* First character code of subrange. */
+ USHORT entryCount; /* Number of character codes in subrange. */
+#if 0
+ USHORT glyphIdArray[]; /* Array of glyph index values for
+ * character codes in the range. */
+#endif
+} TRIMMEDTABLE;
+
+typedef union SUBTABLE {
+ ANYTABLE any;
+ BYTETABLE byte;
+ HIBYTETABLE hiByte;
+ SEGMENTTABLE segment;
+ TRIMMEDTABLE trimmed;
+} SUBTABLE;
+
+#pragma pack()
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * LoadFontRanges --
+ *
+ * Given an HFONT, get the information about the characters that this
+ * font can display.
+ *
+ * Results:
+ * If the font has no Unicode character information, the return value is
+ * 0 and *startCountPtr and *endCountPtr are filled with NULL. Otherwise,
+ * *startCountPtr and *endCountPtr are set to pointers to arrays of
+ * TrueType character existence information and the return value is the
+ * length of the arrays (the two arrays are always the same length as
+ * each other).
+ *
+ * Side effects:
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static int
+LoadFontRanges(
+ HDC hdc, /* HDC into which font can be selected. */
+ HFONT hFont, /* HFONT to query. */
+ USHORT **startCountPtr, /* Filled with malloced pointer to character
+ * range information. */
+ USHORT **endCountPtr, /* Filled with malloced pointer to character
+ * range information. */
+ int *symbolPtr)
+ {
+ int n, i, swapped, offset, cbData, segCount;
+ DWORD cmapKey;
+ USHORT *startCount, *endCount;
+ CMAPTABLE cmapTable;
+ ENCODINGTABLE encTable;
+ SUBTABLE subTable;
+ char *s;
+
+ segCount = 0;
+ startCount = NULL;
+ endCount = NULL;
+ *symbolPtr = 0;
+
+ hFont = SelectObject(hdc, hFont);
+
+ i = 0;
+ s = (char *) &i;
+ *s = '\1';
+ swapped = 0;
+
+ if (i == 1) {
+ swapped = 1;
+ }
+
+ cmapKey = CMAPHEX;
+ if (swapped) {
+ SwapLong(&cmapKey);
+ }
+
+ n = GetFontData(hdc, cmapKey, 0, &cmapTable, sizeof(cmapTable));
+ if (n != (int) GDI_ERROR) {
+ if (swapped) {
+ SwapShort(&cmapTable.numTables);
+ }
+ for (i = 0; i < cmapTable.numTables; i++) {
+ offset = sizeof(cmapTable) + i * sizeof(encTable);
+ GetFontData(hdc, cmapKey, (DWORD) offset, &encTable,
+ sizeof(encTable));
+ if (swapped) {
+ SwapShort(&encTable.platform);
+ SwapShort(&encTable.encoding);
+ SwapLong(&encTable.offset);
+ }
+ if (encTable.platform != 3) {
+ /*
+ * Not Microsoft encoding.
+ */
+
+ continue;
+ }
+ if (encTable.encoding == 0) {
+ *symbolPtr = 1;
+ } else if (encTable.encoding != 1) {
+ continue;
+ }
+
+ GetFontData(hdc, cmapKey, (DWORD) encTable.offset, &subTable,
+ sizeof(subTable));
+ if (swapped) {
+ SwapShort(&subTable.any.format);
+ }
+ if (subTable.any.format == 4) {
+ if (swapped) {
+ SwapShort(&subTable.segment.segCountX2);
+ }
+ segCount = subTable.segment.segCountX2 / 2;
+ cbData = segCount * sizeof(USHORT);
+
+ startCount = ckalloc(cbData);
+ endCount = ckalloc(cbData);
+
+ offset = encTable.offset + sizeof(subTable.segment);
+ GetFontData(hdc, cmapKey, (DWORD) offset, endCount, cbData);
+ offset += cbData + sizeof(USHORT);
+ GetFontData(hdc, cmapKey, (DWORD) offset, startCount, cbData);
+ if (swapped) {
+ for (i = 0; i < segCount; i++) {
+ SwapShort(&endCount[i]);
+ SwapShort(&startCount[i]);
+ }
+ }
+ if (*symbolPtr != 0) {
+ /*
+ * Empirically determined: When a symbol font is loaded,
+ * the character existence metrics obtained from the
+ * system are mildly wrong. If the real range of the
+ * symbol font is from 0020 to 00FE, then the metrics are
+ * reported as F020 to F0FE. When we load a symbol font,
+ * we must fix the character existence metrics.
+ *
+ * Symbol fonts should only use the symbol encoding for
+ * 8-bit characters [note Bug: 2406]
+ */
+
+ for (i = 0; i < segCount; i++) {
+ if (((startCount[i] & 0xff00) == 0xf000)
+ && ((endCount[i] & 0xff00) == 0xf000)) {
+ startCount[i] &= 0xff;
+ endCount[i] &= 0xff;
+ }
+ }
+ }
+ }
+ }
+ } else if (GetTextCharset(hdc) == ANSI_CHARSET) {
+ /*
+ * Bitmap font. We should also support ranges for the other *_CHARSET
+ * values.
+ */
+
+ segCount = 1;
+ cbData = segCount * sizeof(USHORT);
+ startCount = ckalloc(cbData);
+ endCount = ckalloc(cbData);
+ startCount[0] = 0x0000;
+ endCount[0] = 0x00ff;
+ }
+ SelectObject(hdc, hFont);
+
+ *startCountPtr = startCount;
+ *endCountPtr = endCount;
+ return segCount;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * SwapShort, SwapLong --
+ *
+ * Helper functions to convert the data loaded from TrueType font files
+ * to Intel byte ordering.
+ *
+ * Results:
+ * Bytes of input value are swapped and stored back in argument.
+ *
+ * Side effects:
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static inline void
+SwapShort(
+ PUSHORT p)
+{
+ *p = (SHORT)(HIBYTE(*p) + (LOBYTE(*p) << 8));
+}
+
+static inline void
+SwapLong(
+ PULONG p)
+{
+ ULONG temp;
+
+ temp = (LONG) ((BYTE) *p);
+ temp <<= 8;
+ *p >>=8;
+
+ temp += (LONG) ((BYTE) *p);
+ temp <<= 8;
+ *p >>=8;
+
+ temp += (LONG) ((BYTE) *p);
+ temp <<= 8;
+ *p >>=8;
+
+ temp += (LONG) ((BYTE) *p);
+ *p = temp;
+}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */