diff options
Diffstat (limited to 'win/tkWinFont.c')
-rw-r--r-- | win/tkWinFont.c | 643 |
1 files changed, 643 insertions, 0 deletions
diff --git a/win/tkWinFont.c b/win/tkWinFont.c new file mode 100644 index 0000000..c1d5161 --- /dev/null +++ b/win/tkWinFont.c @@ -0,0 +1,643 @@ +/* + * tkWinFont.c -- + * + * Contains the Windows implementation of the platform-independant + * font package interface. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * Copyright (c) 1994 Software Research Associates, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * SCCS: @(#) tkWinFont.c 1.20 97/05/14 15:45:30 + */ + +#include "tkWinInt.h" +#include "tkFont.h" + +/* + * The following structure represents Windows' implementation of a font. + */ + +typedef struct WinFont { + TkFont font; /* Stuff used by generic font package. Must + * be first in structure. */ + HFONT hFont; /* Windows information about font. */ + HWND hwnd; /* Toplevel window of application that owns + * this font, used for getting HDC. */ + int widths[256]; /* Widths of first 256 chars in this font. */ +} WinFont; + +/* + * The following structure is used as to map between the Tcl strings + * that represent the system fonts and the numbers used by Windows. + */ + +static TkStateMap systemMap[] = { + {ANSI_FIXED_FONT, "ansifixed"}, + {ANSI_VAR_FONT, "ansi"}, + {DEVICE_DEFAULT_FONT, "device"}, + {OEM_FIXED_FONT, "oemfixed"}, + {SYSTEM_FIXED_FONT, "systemfixed"}, + {SYSTEM_FONT, "system"}, + {-1, NULL} +}; + +#define ABS(x) (((x) < 0) ? -(x) : (x)) + +static TkFont * AllocFont _ANSI_ARGS_((TkFont *tkFontPtr, + Tk_Window tkwin, HFONT hFont)); +static char * GetProperty _ANSI_ARGS_((CONST TkFontAttributes *faPtr, + CONST char *option)); +static int CALLBACK WinFontFamilyEnumProc _ANSI_ARGS_((ENUMLOGFONT *elfPtr, + NEWTEXTMETRIC *ntmPtr, int fontType, + LPARAM lParam)); + + +/* + *--------------------------------------------------------------------------- + * + * TkpGetNativeFont -- + * + * Map a platform-specific native font name to a TkFont. + * + * Results: + * The return value is a pointer to a TkFont that represents the + * native font. If a native font by the given name could not be + * found, the return value is NULL. + * + * Every call to this procedure returns a new TkFont structure, + * even if the name has already been seen before. The caller should + * call TkpDeleteFont() when the font is no longer needed. + * + * The caller is responsible for initializing the memory associated + * with the generic TkFont when this function returns and releasing + * the contents of the generic TkFont before calling TkpDeleteFont(). + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +TkFont * +TkpGetNativeFont(tkwin, name) + Tk_Window tkwin; /* For display where font will be used. */ + CONST char *name; /* Platform-specific font name. */ +{ + int object; + HFONT hFont; + + object = TkFindStateNum(NULL, NULL, systemMap, name); + if (object < 0) { + return NULL; + } + hFont = GetStockObject(object); + if (hFont == NULL) { + panic("TkpGetNativeFont: can't allocate stock font"); + } + + return AllocFont(NULL, tkwin, 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: + * None. + * + *--------------------------------------------------------------------------- + */ +TkFont * +TkpGetFontFromAttributes(tkFontPtr, tkwin, faPtr) + TkFont *tkFontPtr; /* If non-NULL, store the information in + * this existing TkFont structure, rather than + * allocating a new structure to hold the + * font; the existing contents of the font + * will be released. If NULL, a new TkFont + * structure is allocated. */ + Tk_Window tkwin; /* For display where font will be used. */ + CONST TkFontAttributes *faPtr; /* Set of attributes to match. */ +{ + LOGFONT lf; + HFONT hFont; + Window window; + HWND hwnd; + HDC hdc; + + window = Tk_WindowId(((TkWindow *) tkwin)->mainPtr->winPtr); + hwnd = (window == None) ? NULL : TkWinGetHWND(window); + + hdc = GetDC(hwnd); + lf.lfHeight = -faPtr->pointsize; + if (lf.lfHeight < 0) { + lf.lfHeight = MulDiv(lf.lfHeight, + 254 * WidthOfScreen(Tk_Screen(tkwin)), + 720 * WidthMMOfScreen(Tk_Screen(tkwin))); + } + lf.lfWidth = 0; + lf.lfEscapement = 0; + lf.lfOrientation = 0; + 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_DEFAULT_PRECIS; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfQuality = DEFAULT_QUALITY; + lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + if (faPtr->family == NULL) { + lf.lfFaceName[0] = '\0'; + } else { + lstrcpyn(lf.lfFaceName, faPtr->family, sizeof(lf.lfFaceName)); + } + ReleaseDC(hwnd, hdc); + + /* + * Replace the standard X and Mac family names with the names that + * Windows likes. + */ + + if ((stricmp(lf.lfFaceName, "Times") == 0) + || (stricmp(lf.lfFaceName, "New York") == 0)) { + strcpy(lf.lfFaceName, "Times New Roman"); + } else if ((stricmp(lf.lfFaceName, "Courier") == 0) + || (stricmp(lf.lfFaceName, "Monaco") == 0)) { + strcpy(lf.lfFaceName, "Courier New"); + } else if ((stricmp(lf.lfFaceName, "Helvetica") == 0) + || (stricmp(lf.lfFaceName, "Geneva") == 0)) { + strcpy(lf.lfFaceName, "Arial"); + } + + hFont = CreateFontIndirect(&lf); + if (hFont == NULL) { + hFont = GetStockObject(SYSTEM_FONT); + if (hFont == NULL) { + panic("TkpGetFontFromAttributes: cannot get system font"); + } + } + return AllocFont(tkFontPtr, tkwin, hFont); +} + +/* + *--------------------------------------------------------------------------- + * + * TkpDeleteFont -- + * + * Called to release a font allocated by TkpGetNativeFont() or + * TkpGetFontFromAttributes(). The caller should have already + * released the fields of the TkFont that are used exclusively by + * the generic TkFont code. + * + * Results: + * None. + * + * Side effects: + * TkFont is deallocated. + * + *--------------------------------------------------------------------------- + */ + +void +TkpDeleteFont(tkFontPtr) + TkFont *tkFontPtr; /* Token of font to be deleted. */ +{ + WinFont *fontPtr; + + fontPtr = (WinFont *) tkFontPtr; + DeleteObject(fontPtr->hFont); + ckfree((char *) fontPtr); +} + +/* + *--------------------------------------------------------------------------- + * + * TkpGetFontFamilies, WinFontEnumFamilyProc -- + * + * Return information about the font families that are available + * on the display of the given window. + * + * Results: + * interp->result is modified to hold a list of all the available + * font families. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +void +TkpGetFontFamilies(interp, tkwin) + Tcl_Interp *interp; /* Interp to hold result. */ + Tk_Window tkwin; /* For display to query. */ +{ + Window window; + HWND hwnd; + HDC hdc; + + window = Tk_WindowId(tkwin); + hwnd = (window == (Window) NULL) ? NULL : TkWinGetHWND(window); + + hdc = GetDC(hwnd); + EnumFontFamilies(hdc, NULL, (FONTENUMPROC) WinFontFamilyEnumProc, + (LPARAM) interp); + ReleaseDC(hwnd, hdc); +} + +/* ARGSUSED */ + +static int CALLBACK +WinFontFamilyEnumProc(elfPtr, ntmPtr, fontType, lParam) + ENUMLOGFONT *elfPtr; /* Logical-font data. */ + NEWTEXTMETRIC *ntmPtr; /* Physical-font data (not used). */ + int fontType; /* Type of font (not used). */ + LPARAM lParam; /* Interp to hold result. */ +{ + Tcl_Interp *interp; + + interp = (Tcl_Interp *) lParam; + Tcl_AppendElement(interp, elfPtr->elfLogFont.lfFaceName); + return 1; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_MeasureChars -- + * + * Determine the number of characters from the string that will fit + * in the given horizontal span. The measurement is done under the + * assumption that Tk_DrawChars() will be used to actually display + * the characters. + * + * Results: + * The return value is the number of characters from source that + * fit into the span that extends from 0 to maxLength. *lengthPtr is + * filled with the x-coordinate of the right edge of the last + * character that did fit. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ +int +Tk_MeasureChars(tkfont, source, numChars, maxLength, flags, lengthPtr) + Tk_Font tkfont; /* Font in which characters will be drawn. */ + CONST char *source; /* Characters to be displayed. Need not be + * '\0' terminated. */ + int numChars; /* Maximum number of characters to consider + * from source string. */ + int maxLength; /* If > 0, maxLength specifies the longest + * permissible line length; don't consider any + * character that would cross this + * x-position. If <= 0, then line length is + * unbounded and the flags argument is + * ignored. */ + int flags; /* Various flag bits OR-ed together: + * TK_PARTIAL_OK means include the last char + * which only partially fit on this line. + * TK_WHOLE_WORDS means stop on a word + * boundary, if possible. + * TK_AT_LEAST_ONE means return at least one + * character even if no characters fit. */ + int *lengthPtr; /* Filled with x-location just after the + * terminating character. */ +{ + WinFont *fontPtr; + HDC hdc; + HFONT hFont; + int curX, curIdx; + + /* + * On the authority of the Gates Empire, Windows 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 = (WinFont *) tkfont; + + hdc = GetDC(fontPtr->hwnd); + hFont = SelectObject(hdc, fontPtr->hFont); + + if (numChars == 0) { + curX = 0; + curIdx = 0; + } else if (maxLength <= 0) { + SIZE size; + + GetTextExtentPoint(hdc, source, numChars, &size); + curX = size.cx; + curIdx = numChars; + } else { + int newX, termX, sawNonSpace; + CONST char *term, *end, *p; + int ch; + + ch = UCHAR(*source); + newX = curX = termX = 0; + + term = source; + end = source + numChars; + + sawNonSpace = !isspace(ch); + for (p = source; ; ) { + newX += fontPtr->widths[ch]; + if (newX > maxLength) { + break; + } + curX = newX; + p++; + if (p >= end) { + term = end; + termX = curX; + break; + } + + ch = UCHAR(*p); + if (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++; + } + if ((flags & TK_AT_LEAST_ONE) && (term == source) && (p < end)) { + term = p; + termX = curX; + if (term == source) { + term++; + termX = newX; + } + } else if ((p >= end) || !(flags & TK_WHOLE_WORDS)) { + term = p; + termX = curX; + } + + curX = termX; + curIdx = term - source; + } + + SelectObject(hdc, hFont); + ReleaseDC(fontPtr->hwnd, hdc); + + *lengthPtr = curX; + return curIdx; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_DrawChars -- + * + * Draw a string of characters on the screen. + * + * Results: + * None. + * + * Side effects: + * Information gets drawn on the screen. + * + *--------------------------------------------------------------------------- + */ + +void +Tk_DrawChars(display, drawable, gc, tkfont, source, numChars, x, y) + Display *display; /* Display on which to draw. */ + Drawable drawable; /* Window or pixmap in which to draw. */ + GC gc; /* Graphics context for drawing characters. */ + Tk_Font tkfont; /* Font in which characters will be drawn; + * must be the same as font used in GC. */ + CONST char *source; /* Characters to be displayed. Need not be + * '\0' terminated. All Tk meta-characters + * (tabs, control characters, and newlines) + * should be stripped out of the string that + * is passed to this function. If they are + * not stripped out, they will be displayed as + * regular printing characters. */ + int numChars; /* Number of characters in string. */ + int x, y; /* Coordinates at which to place origin of + * string when drawing. */ +{ + HDC dc; + HFONT hFont; + TkWinDCState state; + WinFont *fontPtr; + + fontPtr = (WinFont *) gc->font; + display->request++; + + if (drawable == None) { + return; + } + + dc = TkWinGetDrawableDC(display, drawable, &state); + + SetROP2(dc, tkpWinRopModes[gc->function]); + + 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) { + 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_TOP); + SetTextColor(dcMem, gc->foreground); + SetBkMode(dcMem, TRANSPARENT); + SetBkColor(dcMem, RGB(0, 0, 0)); + + hFont = SelectObject(dcMem, fontPtr->hFont); + + /* + * Compute the bounding box and create a compatible bitmap. + */ + + GetTextExtentPoint(dcMem, source, numChars, &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); + TextOut(dcMem, 0, 0, source, numChars); + BitBlt(dc, x, y - tm.tmAscent, size.cx, size.cy, dcMem, + 0, 0, 0xEA02E9); + PatBlt(dcMem, 0, 0, size.cx, size.cy, WHITENESS); + TextOut(dcMem, 0, 0, source, numChars); + 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, hFont); + SelectObject(dcMem, oldBitmap); + DeleteObject(bitmap); + DeleteDC(dcMem); + SelectObject(dc, oldBrush); + DeleteObject(stipple); + } else { + SetTextAlign(dc, TA_LEFT | TA_BASELINE); + SetTextColor(dc, gc->foreground); + SetBkMode(dc, TRANSPARENT); + hFont = SelectObject(dc, fontPtr->hFont); + TextOut(dc, x, y, source, numChars); + SelectObject(dc, hFont); + } + TkWinReleaseDrawableDC(drawable, dc, &state); +} + +/* + *--------------------------------------------------------------------------- + * + * AllocFont -- + * + * Helper for TkpGetNativeFont() and TkpGetFontFromAttributes(). + * Allocates and intializes the memory for a new TkFont that + * wraps the platform-specific data. + * + * Results: + * Returns pointer to newly constructed TkFont. + * + * The caller is responsible for initializing the fields of the + * TkFont that are used exclusively by the generic TkFont code, and + * for releasing those fields before calling TkpDeleteFont(). + * + * Side effects: + * Memory allocated. + * + *--------------------------------------------------------------------------- + */ + +static TkFont * +AllocFont(tkFontPtr, tkwin, hFont) + 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. */ + HFONT hFont; /* Windows information about font. */ +{ + HWND hwnd; + WinFont *fontPtr; + HDC hdc; + TEXTMETRIC tm; + Window window; + char buf[LF_FACESIZE]; + TkFontAttributes *faPtr; + + if (tkFontPtr != NULL) { + fontPtr = (WinFont *) tkFontPtr; + DeleteObject(fontPtr->hFont); + } else { + fontPtr = (WinFont *) ckalloc(sizeof(WinFont)); + } + + window = Tk_WindowId(((TkWindow *) tkwin)->mainPtr->winPtr); + hwnd = (window == None) ? NULL : TkWinGetHWND(window); + + hdc = GetDC(hwnd); + hFont = SelectObject(hdc, hFont); + GetTextFace(hdc, sizeof(buf), buf); + GetTextMetrics(hdc, &tm); + GetCharWidth(hdc, 0, 255, fontPtr->widths); + + fontPtr->font.fid = (Font) fontPtr; + + faPtr = &fontPtr->font.fa; + faPtr->family = Tk_GetUid(buf); + faPtr->pointsize = MulDiv(tm.tmHeight - tm.tmInternalLeading, + 720 * WidthMMOfScreen(Tk_Screen(tkwin)), + 254 * WidthOfScreen(Tk_Screen(tkwin))); + 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 = (tm.tmStruckOut != 0) ? 1 : 0; + + fontPtr->font.fm.ascent = tm.tmAscent; + fontPtr->font.fm.descent = tm.tmDescent; + fontPtr->font.fm.maxWidth = tm.tmMaxCharWidth; + fontPtr->font.fm.fixed = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); + + hFont = SelectObject(hdc, hFont); + ReleaseDC(hwnd, hdc); + + fontPtr->hFont = hFont; + fontPtr->hwnd = hwnd; + + return (TkFont *) fontPtr; +} + |