/* * tkFont.c -- * * This file maintains a database of fonts for the Tk toolkit. * It also provides several utility procedures for measuring and * displaying text. * * Copyright (c) 1990-1994 The Regents of the University of California. * Copyright (c) 1994-1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tkFont.c 1.72 97/07/22 15:33:45 */ #include "tkInt.h" #include "tkFont.h" /* * The following structure is used to keep track of all the fonts that * exist in the current application. It must be stored in the * TkMainInfo for the application. */ typedef struct TkFontInfo { Tcl_HashTable fontCache; /* Map a string to an existing Tk_Font. * Keys are CachedFontKey structs, values are * TkFont structs. */ Tcl_HashTable namedTable; /* Map a name to a set of attributes for a * font, used when constructing a Tk_Font from * a named font description. Keys are * Tk_Uids, values are NamedFont structs. */ TkMainInfo *mainPtr; /* Application that owns this structure. */ int updatePending; } TkFontInfo; /* * The following structure is used as a key in the fontCache. */ typedef struct CachedFontKey { Display *display; /* Display for which font was constructed. */ Tk_Uid string; /* String that describes font. */ } CachedFontKey; /* * The following data structure is used to keep track of the font attributes * for each named font that has been defined. The named font is only deleted * when the last reference to it goes away. */ typedef struct NamedFont { int refCount; /* Number of users of named font. */ int deletePending; /* Non-zero if font should be deleted when * last reference goes away. */ TkFontAttributes fa; /* Desired attributes for named font. */ } NamedFont; /* * The following two structures are used to keep track of string * measurement information when using the text layout facilities. * * A LayoutChunk represents a contiguous range of text that can be measured * and displayed by low-level text calls. In general, chunks will be * delimited by newlines and tabs. Low-level, platform-specific things * like kerning and non-integer character widths may occur between the * characters in a single chunk, but not between characters in different * chunks. * * A TextLayout is a collection of LayoutChunks. It can be displayed with * respect to any origin. It is the implementation of the Tk_TextLayout * opaque token. */ typedef struct LayoutChunk { CONST char *start; /* Pointer to simple string to be displayed. * This is a pointer into the TkTextLayout's * string. */ int numChars; /* The number of characters in this chunk. */ int numDisplayChars; /* The number of characters to display when * this chunk is displayed. Can be less than * numChars if extra space characters were * absorbed by the end of the chunk. This * will be < 0 if this is a chunk that is * holding a tab or newline. */ int x, y; /* The origin of the first character in this * chunk with respect to the upper-left hand * corner of the TextLayout. */ int totalWidth; /* Width in pixels of this chunk. Used * when hit testing the invisible spaces at * the end of a chunk. */ int displayWidth; /* Width in pixels of the displayable * characters in this chunk. Can be less than * width if extra space characters were * absorbed by the end of the chunk. */ } LayoutChunk; typedef struct TextLayout { Tk_Font tkfont; /* The font used when laying out the text. */ CONST char *string; /* The string that was layed out. */ int width; /* The maximum width of all lines in the * text layout. */ int numChunks; /* Number of chunks actually used in * following array. */ LayoutChunk chunks[1]; /* Array of chunks. The actual size will * be maxChunks. THIS FIELD MUST BE THE LAST * IN THE STRUCTURE. */ } TextLayout; /* * The following structures are used as two-way maps between the values for * the fields in the TkFontAttributes structure and the strings used in * Tcl, when parsing both option-value format and style-list format font * name strings. */ static TkStateMap weightMap[] = { {TK_FW_NORMAL, "normal"}, {TK_FW_BOLD, "bold"}, {TK_FW_UNKNOWN, NULL} }; static TkStateMap slantMap[] = { {TK_FS_ROMAN, "roman"}, {TK_FS_ITALIC, "italic"}, {TK_FS_UNKNOWN, NULL} }; static TkStateMap underlineMap[] = { {1, "underline"}, {0, NULL} }; static TkStateMap overstrikeMap[] = { {1, "overstrike"}, {0, NULL} }; /* * The following structures are used when parsing XLFD's into a set of * TkFontAttributes. */ static TkStateMap xlfdWeightMap[] = { {TK_FW_NORMAL, "normal"}, {TK_FW_NORMAL, "medium"}, {TK_FW_NORMAL, "book"}, {TK_FW_NORMAL, "light"}, {TK_FW_BOLD, "bold"}, {TK_FW_BOLD, "demi"}, {TK_FW_BOLD, "demibold"}, {TK_FW_NORMAL, NULL} /* Assume anything else is "normal". */ }; static TkStateMap xlfdSlantMap[] = { {TK_FS_ROMAN, "r"}, {TK_FS_ITALIC, "i"}, {TK_FS_OBLIQUE, "o"}, {TK_FS_ROMAN, NULL} /* Assume anything else is "roman". */ }; static TkStateMap xlfdSetwidthMap[] = { {TK_SW_NORMAL, "normal"}, {TK_SW_CONDENSE, "narrow"}, {TK_SW_CONDENSE, "semicondensed"}, {TK_SW_CONDENSE, "condensed"}, {TK_SW_UNKNOWN, NULL} }; static TkStateMap xlfdCharsetMap[] = { {TK_CS_NORMAL, "iso8859"}, {TK_CS_SYMBOL, "adobe"}, {TK_CS_SYMBOL, "sun"}, {TK_CS_OTHER, NULL} }; /* * The following structure and defines specify the valid builtin options * when configuring a set of font attributes. */ static char *fontOpt[] = { "-family", "-size", "-weight", "-slant", "-underline", "-overstrike", NULL }; #define FONT_FAMILY 0 #define FONT_SIZE 1 #define FONT_WEIGHT 2 #define FONT_SLANT 3 #define FONT_UNDERLINE 4 #define FONT_OVERSTRIKE 5 #define FONT_NUMFIELDS 6 /* Length of fontOpt array. */ #define GetFontAttributes(tkfont) \ ((CONST TkFontAttributes *) &((TkFont *) (tkfont))->fa) #define GetFontMetrics(tkfont) \ ((CONST TkFontMetrics *) &((TkFont *) (tkfont))->fm) static int ConfigAttributesObj _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin, int objc, Tcl_Obj *CONST objv[], TkFontAttributes *faPtr)); static int FieldSpecified _ANSI_ARGS_((CONST char *field)); static void GetAttributeInfoObj _ANSI_ARGS_((Tcl_Interp *interp, CONST TkFontAttributes *faPtr, Tcl_Obj *objPtr)); static LayoutChunk * NewChunk _ANSI_ARGS_((TextLayout **layoutPtrPtr, int *maxPtr, CONST char *start, int numChars, int curX, int newX, int y)); static int ParseFontNameObj _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj *objPtr, TkFontAttributes *faPtr)); static void RecomputeWidgets _ANSI_ARGS_((TkWindow *winPtr)); static void TheWorldHasChanged _ANSI_ARGS_(( ClientData clientData)); static void UpdateDependantFonts _ANSI_ARGS_((TkFontInfo *fiPtr, Tk_Window tkwin, Tcl_HashEntry *namedHashPtr)); /* *--------------------------------------------------------------------------- * * TkFontPkgInit -- * * This procedure is called when an application is created. It * initializes all the structures that are used by the font * package on a per application basis. * * Results: * Returns a token that must be stored in the TkMainInfo for this * application. * * Side effects: * Memory allocated. * *--------------------------------------------------------------------------- */ void TkFontPkgInit(mainPtr) TkMainInfo *mainPtr; /* The application being created. */ { TkFontInfo *fiPtr; fiPtr = (TkFontInfo *) ckalloc(sizeof(TkFontInfo)); Tcl_InitHashTable(&fiPtr->fontCache, sizeof(CachedFontKey) / sizeof(int)); Tcl_InitHashTable(&fiPtr->namedTable, TCL_ONE_WORD_KEYS); fiPtr->mainPtr = mainPtr; fiPtr->updatePending = 0; mainPtr->fontInfoPtr = fiPtr; } /* *--------------------------------------------------------------------------- * * TkFontPkgFree -- * * This procedure is called when an application is deleted. It * deletes all the structures that were used by the font package * for this application. * * Results: * None. * * Side effects: * Memory freed. * *--------------------------------------------------------------------------- */ void TkFontPkgFree(mainPtr) TkMainInfo *mainPtr; /* The application being deleted. */ { TkFontInfo *fiPtr; Tcl_HashEntry *hPtr; Tcl_HashSearch search; fiPtr = mainPtr->fontInfoPtr; if (fiPtr->fontCache.numEntries != 0) { panic("TkFontPkgFree: all fonts should have been freed already"); } Tcl_DeleteHashTable(&fiPtr->fontCache); hPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search); while (hPtr != NULL) { ckfree((char *) Tcl_GetHashValue(hPtr)); hPtr = Tcl_NextHashEntry(&search); } Tcl_DeleteHashTable(&fiPtr->namedTable); if (fiPtr->updatePending != 0) { Tcl_CancelIdleCall(TheWorldHasChanged, (ClientData) fiPtr); } ckfree((char *) fiPtr); } /* *--------------------------------------------------------------------------- * * Tk_FontObjCmd -- * * This procedure is implemented to process the "font" Tcl command. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ int Tk_FontObjCmd(clientData, interp, objc, objv) ClientData clientData; /* Main window associated with interpreter. */ Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments. */ Tcl_Obj *CONST objv[]; /* Argument objects. */ { int index; Tk_Window tkwin; TkFontInfo *fiPtr; static char *optionStrings[] = { "actual", "configure", "create", "delete", "families", "measure", "metrics", "names", NULL }; enum options { FONT_ACTUAL, FONT_CONFIGURE, FONT_CREATE, FONT_DELETE, FONT_FAMILIES, FONT_MEASURE, FONT_METRICS, FONT_NAMES }; tkwin = (Tk_Window) clientData; fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } switch ((enum options) index) { case FONT_ACTUAL: { int skip; Tk_Font tkfont; Tcl_Obj *objPtr; CONST TkFontAttributes *faPtr; skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin); if (skip < 0) { return TCL_ERROR; } if ((objc < 3) || (objc - skip > 4)) { Tcl_WrongNumArgs(interp, 2, objv, #ifdef STk_CODE "font ?:displayof window? ?option?"); #else "font ?-displayof window? ?option?"); #endif return TCL_ERROR; } tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]); if (tkfont == NULL) { return TCL_ERROR; } faPtr = GetFontAttributes(tkfont); objPtr = NULL; if (objc > 3) { objPtr = objv[3 + skip]; } GetAttributeInfoObj(interp, faPtr, objPtr); Tk_FreeFont(tkfont); break; } case FONT_CONFIGURE: { int result; char *string; Tcl_Obj *objPtr; NamedFont *nfPtr; Tcl_HashEntry *namedHashPtr; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "fontname ?options?"); return TCL_ERROR; } string = Tk_GetUid(Tcl_GetStringFromObj(objv[2], NULL)); namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string); nfPtr = NULL; /* lint. */ if (namedHashPtr != NULL) { nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr); } if ((namedHashPtr == NULL) || (nfPtr->deletePending != 0)) { Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "named font \"", string, "\" doesn't exist", NULL); return TCL_ERROR; } if (objc == 3) { objPtr = NULL; } else if (objc == 4) { objPtr = objv[3]; } else { result = ConfigAttributesObj(interp, tkwin, objc - 3, objv + 3, &nfPtr->fa); UpdateDependantFonts(fiPtr, tkwin, namedHashPtr); return result; } GetAttributeInfoObj(interp, &nfPtr->fa, objPtr); break; } case FONT_CREATE: { int skip, i; char *name; char buf[32]; TkFontAttributes fa; Tcl_HashEntry *namedHashPtr; skip = 3; if (objc < 3) { name = NULL; } else { name = Tcl_GetStringFromObj(objv[2], NULL); if (name[0] == '-') { name = NULL; } } if (name == NULL) { /* * No font name specified. Generate one of the form "fontX". */ for (i = 1; ; i++) { sprintf(buf, "font%d", i); namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, Tk_GetUid(buf)); if (namedHashPtr == NULL) { break; } } name = buf; skip = 2; } TkInitFontAttributes(&fa); if (ConfigAttributesObj(interp, tkwin, objc - skip, objv + skip, &fa) != TCL_OK) { return TCL_ERROR; } if (TkCreateNamedFont(interp, tkwin, name, &fa) != TCL_OK) { return TCL_ERROR; } Tcl_SetStringObj(Tcl_GetObjResult(interp), name, -1); break; } case FONT_DELETE: { int i; char *string; NamedFont *nfPtr; Tcl_HashEntry *namedHashPtr; /* * Delete the named font. If there are still widgets using this * font, then it isn't deleted right away. */ if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "fontname ?fontname ...?"); return TCL_ERROR; } for (i = 2; i < objc; i++) { string = Tk_GetUid(Tcl_GetStringFromObj(objv[i], NULL)); namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string); if (namedHashPtr == NULL) { Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "named font \"", string, "\" doesn't exist", (char *) NULL); return TCL_ERROR; } nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr); if (nfPtr->refCount != 0) { nfPtr->deletePending = 1; } else { Tcl_DeleteHashEntry(namedHashPtr); ckfree((char *) nfPtr); } } break; } case FONT_FAMILIES: { int skip; skip = TkGetDisplayOf(interp, objc - 2, objv + 2, &tkwin); if (skip < 0) { return TCL_ERROR; } if (objc - skip != 2) { #ifdef STk_CODE Tcl_WrongNumArgs(interp, 2, objv, "?:displayof window?"); #else Tcl_WrongNumArgs(interp, 2, objv, "?-displayof window?"); #endif return TCL_ERROR; } TkpGetFontFamilies(interp, tkwin); #ifdef STk_CODE Tcl_SetObjResult(interp, STk_create_tcl_object( STk_convert_Tcl_string2list(interp->result))); #endif break; } case FONT_MEASURE: { char *string; Tk_Font tkfont; int length, skip; skip = TkGetDisplayOf(interp, objc - 2, objv + 2, &tkwin); if (skip < 0) { return TCL_ERROR; } if (objc - skip != 4) { Tcl_WrongNumArgs(interp, 2, objv, #ifdef STk_CODE "font ?:displayof window? text"); #else "font ?-displayof window? text"); #endif return TCL_ERROR; } tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]); if (tkfont == NULL) { return TCL_ERROR; } string = Tcl_GetStringFromObj(objv[3 + skip], &length); Tcl_SetIntObj(Tcl_GetObjResult(interp), Tk_TextWidth(tkfont, string, length)); Tk_FreeFont(tkfont); break; } case FONT_METRICS: { char buf[64]; Tk_Font tkfont; int skip, index, i; CONST TkFontMetrics *fmPtr; static char *switches[] = { "-ascent", "-descent", "-linespace", "-fixed", NULL }; skip = TkGetDisplayOf(interp, objc - 2, objv + 2, &tkwin); if (skip < 0) { return TCL_ERROR; } if ((objc < 3) || ((objc - skip) > 4)) { Tcl_WrongNumArgs(interp, 2, objv, #ifdef STk_CODE "font ?:displayof window? ?option?"); #else "font ?-displayof window? ?option?"); #endif return TCL_ERROR; } tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]); if (tkfont == NULL) { return TCL_ERROR; } fmPtr = GetFontMetrics(tkfont); if (objc == 3) { #ifdef STk_CODE sprintf(buf, ":ascent %d :descent %d :linespace %d :fixed #%c", fmPtr->ascent, fmPtr->descent, fmPtr->ascent + fmPtr->descent, fmPtr->fixed? 't' : 'f'); Tcl_SetObjResult(interp, STk_create_tcl_object( STk_convert_Tcl_string2list(buf))); #else sprintf(buf, "-ascent %d -descent %d -linespace %d -fixed %d", fmPtr->ascent, fmPtr->descent, fmPtr->ascent + fmPtr->descent, fmPtr->fixed); Tcl_SetStringObj(Tcl_GetObjResult(interp), buf, -1); #endif } else { if (Tcl_GetIndexFromObj(interp, objv[3 + skip], switches, "metric", 0, &index) != TCL_OK) { Tk_FreeFont(tkfont); return TCL_ERROR; } i = 0; /* Needed only to prevent compiler * warning. */ switch (index) { case 0: i = fmPtr->ascent; break; case 1: i = fmPtr->descent; break; case 2: i = fmPtr->ascent + fmPtr->descent; break; case 3: i = fmPtr->fixed; break; } #ifdef STk_CODE if (index == 3) Tcl_SetBooleanObj(Tcl_GetObjResult(interp), i); else #endif Tcl_SetIntObj(Tcl_GetObjResult(interp), i); } Tk_FreeFont(tkfont); break; } case FONT_NAMES: { char *string; Tcl_Obj *strPtr; NamedFont *nfPtr; Tcl_HashSearch search; Tcl_HashEntry *namedHashPtr; if (objc != 2) { #ifdef STk_CODE Tcl_WrongNumArgs(interp, 1, objv, "'names"); #else Tcl_WrongNumArgs(interp, 1, objv, "names"); #endif return TCL_ERROR; } namedHashPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search); while (namedHashPtr != NULL) { nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr); if (nfPtr->deletePending == 0) { string = Tcl_GetHashKey(&fiPtr->namedTable, namedHashPtr); strPtr = Tcl_NewStringObj(string, -1); Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), strPtr); } namedHashPtr = Tcl_NextHashEntry(&search); } break; } } return TCL_OK; } /* *--------------------------------------------------------------------------- * * UpdateDependantFonts, TheWorldHasChanged, RecomputeWidgets -- * * Called when the attributes of a named font changes. Updates all * the instantiated fonts that depend on that named font and then * uses the brute force approach and prepares every widget to * recompute its geometry. * * Results: * None. * * Side effects: * Things get queued for redisplay. * *--------------------------------------------------------------------------- */ static void UpdateDependantFonts(fiPtr, tkwin, namedHashPtr) TkFontInfo *fiPtr; /* Info about application's fonts. */ Tk_Window tkwin; /* A window in the application. */ Tcl_HashEntry *namedHashPtr;/* The named font that is changing. */ { Tcl_HashEntry *cacheHashPtr; Tcl_HashSearch search; TkFont *fontPtr; NamedFont *nfPtr; nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr); if (nfPtr->refCount == 0) { /* * Well nobody's using this named font, so don't have to tell * any widgets to recompute themselves. */ return; } cacheHashPtr = Tcl_FirstHashEntry(&fiPtr->fontCache, &search); while (cacheHashPtr != NULL) { fontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr); if (fontPtr->namedHashPtr == namedHashPtr) { TkpGetFontFromAttributes(fontPtr, tkwin, &nfPtr->fa); if (fiPtr->updatePending == 0) { fiPtr->updatePending = 1; Tcl_DoWhenIdle(TheWorldHasChanged, (ClientData) fiPtr); } } cacheHashPtr = Tcl_NextHashEntry(&search); } } static void TheWorldHasChanged(clientData) ClientData clientData; /* Info about application's fonts. */ { TkFontInfo *fiPtr; fiPtr = (TkFontInfo *) clientData; fiPtr->updatePending = 0; RecomputeWidgets(fiPtr->mainPtr->winPtr); } static void RecomputeWidgets(winPtr) TkWindow *winPtr; /* Window to which command is sent. */ { if ((winPtr->classProcsPtr != NULL) && (winPtr->classProcsPtr->geometryProc != NULL)) { (*winPtr->classProcsPtr->geometryProc)(winPtr->instanceData); } for (winPtr = winPtr->childList; winPtr != NULL; winPtr = winPtr->nextPtr) { RecomputeWidgets(winPtr); } } /* *--------------------------------------------------------------------------- * * TkCreateNamedFont -- * * Create the specified named font with the given attributes in the * named font table associated with the interp. * * Results: * Returns TCL_OK if the font was successfully created, or TCL_ERROR * if the named font already existed. If TCL_ERROR is returned, an * error message is left in interp->result. * * Side effects: * Assume there used to exist a named font by the specified name, and * that the named font had been deleted, but there were still some * widgets using the named font at the time it was deleted. If a * new named font is created with the same name, all those widgets * that were using the old named font will be redisplayed using * the new named font's attributes. * *--------------------------------------------------------------------------- */ int TkCreateNamedFont(interp, tkwin, name, faPtr) Tcl_Interp *interp; /* Interp for error return. */ Tk_Window tkwin; /* A window associated with interp. */ CONST char *name; /* Name for the new named font. */ TkFontAttributes *faPtr; /* Attributes for the new named font. */ { TkFontInfo *fiPtr; Tcl_HashEntry *namedHashPtr; int new; NamedFont *nfPtr; fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr; name = Tk_GetUid(name); namedHashPtr = Tcl_CreateHashEntry(&fiPtr->namedTable, name, &new); if (new == 0) { nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr); if (nfPtr->deletePending == 0) { interp->result[0] = '\0'; Tcl_AppendResult(interp, "font \"", name, "\" already exists", (char *) NULL); return TCL_ERROR; } /* * Recreating a named font with the same name as a previous * named font. Some widgets were still using that named * font, so they need to get redisplayed. */ nfPtr->fa = *faPtr; nfPtr->deletePending = 0; UpdateDependantFonts(fiPtr, tkwin, namedHashPtr); return TCL_OK; } nfPtr = (NamedFont *) ckalloc(sizeof(NamedFont)); nfPtr->deletePending = 0; Tcl_SetHashValue(namedHashPtr, nfPtr); nfPtr->fa = *faPtr; nfPtr->refCount = 0; nfPtr->deletePending = 0; return TCL_OK; } /* *--------------------------------------------------------------------------- * * Tk_GetFont -- * * Given a string description of a font, map the description to a * corresponding Tk_Font that represents the font. * * Results: * The return value is token for the font, or NULL if an error * prevented the font from being created. If NULL is returned, an * error message will be left in interp->result. * * Side effects: * Calls Tk_GetFontFromObj(), which modifies interp's result object, * then copies the string from the result object into interp->result. * This procedure will go away when Tk_ConfigureWidget() is * made into an object command. * *--------------------------------------------------------------------------- */ Tk_Font Tk_GetFont(interp, tkwin, string) Tcl_Interp *interp; /* Interp for database and error return. */ Tk_Window tkwin; /* For display on which font will be used. */ CONST char *string; /* String describing font, as: named font, * native format, or parseable string. */ { Tcl_Obj *strPtr; Tk_Font tkfont; strPtr = Tcl_NewStringObj((char *) string, -1); tkfont = Tk_GetFontFromObj(interp, tkwin, strPtr); if (tkfont == NULL) { Tcl_SetResult(interp, Tcl_GetStringFromObj(Tcl_GetObjResult(interp), NULL), TCL_VOLATILE); } Tcl_DecrRefCount(strPtr); /* done with object */ return tkfont; } /* *--------------------------------------------------------------------------- * * Tk_GetFontFromObj -- * * Given a string description of a font, map the description to a * corresponding Tk_Font that represents the font. * * Results: * The return value is token for the font, or NULL if an error * prevented the font from being created. If NULL is returned, an * error message will be left in interp's result object. * * Side effects: * The font is added to an internal database with a reference * count. For each call to this procedure, there should eventually * be a call to Tk_FreeFont() so that the database is cleaned up when * fonts aren't in use anymore. * *--------------------------------------------------------------------------- */ Tk_Font Tk_GetFontFromObj(interp, tkwin, objPtr) Tcl_Interp *interp; /* Interp for database and error return. */ Tk_Window tkwin; /* For display on which font will be used. */ Tcl_Obj *objPtr; /* Object describing font, as: named font, * native format, or parseable string. */ { TkFontInfo *fiPtr; CachedFontKey key; Tcl_HashEntry *cacheHashPtr, *namedHashPtr; TkFont *fontPtr; int new, descent; NamedFont *nfPtr; char *string; fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr; string = Tcl_GetStringFromObj(objPtr, NULL); key.display = Tk_Display(tkwin); key.string = Tk_GetUid(string); cacheHashPtr = Tcl_CreateHashEntry(&fiPtr->fontCache, (char *) &key, &new); if (new == 0) { /* * We have already constructed a font with this description for * this display. Bump the reference count of the cached font. */ fontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr); fontPtr->refCount++; return (Tk_Font) fontPtr; } namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, key.string); if (namedHashPtr != NULL) { /* * Construct a font based on a named font. */ nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr); nfPtr->refCount++; fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &nfPtr->fa); } else { /* * Native font? */ fontPtr = TkpGetNativeFont(tkwin, string); if (fontPtr == NULL) { TkFontAttributes fa; TkInitFontAttributes(&fa); if (ParseFontNameObj(interp, tkwin, objPtr, &fa) != TCL_OK) { Tcl_DeleteHashEntry(cacheHashPtr); return NULL; } /* * String contained the attributes inline. */ fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &fa); } } Tcl_SetHashValue(cacheHashPtr, fontPtr); fontPtr->refCount = 1; fontPtr->cacheHashPtr = cacheHashPtr; fontPtr->namedHashPtr = namedHashPtr; Tk_MeasureChars((Tk_Font) fontPtr, "0", 1, 0, 0, &fontPtr->tabWidth); if (fontPtr->tabWidth == 0) { fontPtr->tabWidth = fontPtr->fm.maxWidth; } fontPtr->tabWidth *= 8; /* * Make sure the tab width isn't zero (some fonts may not have enough * information to set a reasonable tab width). */ if (fontPtr->tabWidth == 0) { fontPtr->tabWidth = 1; } /* * Get information used for drawing underlines in generic code on a * non-underlined font. */ descent = fontPtr->fm.descent; fontPtr->underlinePos = descent / 2; fontPtr->underlineHeight = fontPtr->fa.pointsize / 10; if (fontPtr->underlineHeight == 0) { fontPtr->underlineHeight = 1; } if (fontPtr->underlinePos + fontPtr->underlineHeight > descent) { /* * If this set of values would cause the bottom of the underline * bar to stick below the descent of the font, jack the underline * up a bit higher. */ fontPtr->underlineHeight = descent - fontPtr->underlinePos; if (fontPtr->underlineHeight == 0) { fontPtr->underlinePos--; fontPtr->underlineHeight = 1; } } return (Tk_Font) fontPtr; } /* *--------------------------------------------------------------------------- * * Tk_NameOfFont -- * * Given a font, return a textual string identifying it. * * Results: * The return value is the description that was passed to * Tk_GetFont() to create the font. The storage for the returned * string is only guaranteed to persist until the font is deleted. * The caller should not modify this string. * * Side effects: * None. * *--------------------------------------------------------------------------- */ char * Tk_NameOfFont(tkfont) Tk_Font tkfont; /* Font whose name is desired. */ { TkFont *fontPtr; Tcl_HashEntry *hPtr; CachedFontKey *keyPtr; fontPtr = (TkFont *) tkfont; hPtr = fontPtr->cacheHashPtr; keyPtr = (CachedFontKey *) Tcl_GetHashKey(hPtr->tablePtr, hPtr); return (char *) keyPtr->string; } /* *--------------------------------------------------------------------------- * * Tk_FreeFont -- * * Called to release a font allocated by Tk_GetFont(). * * Results: * None. * * Side effects: * The reference count associated with font is decremented, and * only deallocated when no one is using it. * *--------------------------------------------------------------------------- */ void Tk_FreeFont(tkfont) Tk_Font tkfont; /* Font to be released. */ { TkFont *fontPtr; NamedFont *nfPtr; if (tkfont == NULL) { return; } fontPtr = (TkFont *) tkfont; fontPtr->refCount--; if (fontPtr->refCount == 0) { if (fontPtr->namedHashPtr != NULL) { /* * The font is being deleted. Determine if the associated named * font definition should and/or can be deleted too. */ nfPtr = (NamedFont *) Tcl_GetHashValue(fontPtr->namedHashPtr); nfPtr->refCount--; if ((nfPtr->refCount == 0) && (nfPtr->deletePending != 0)) { Tcl_DeleteHashEntry(fontPtr->namedHashPtr); ckfree((char *) nfPtr); } } Tcl_DeleteHashEntry(fontPtr->cacheHashPtr); TkpDeleteFont(fontPtr); } } /* *--------------------------------------------------------------------------- * * Tk_FontId -- * * Given a font, return an opaque handle that should be selected * into the XGCValues structure in order to get the constructed * gc to use this font. This procedure would go away if the * XGCValues structure were replaced with a TkGCValues structure. * * Results: * As above. * * Side effects: * None. * *--------------------------------------------------------------------------- */ Font Tk_FontId(tkfont) Tk_Font tkfont; /* Font that is going to be selected into GC. */ { TkFont *fontPtr; fontPtr = (TkFont *) tkfont; return fontPtr->fid; } /* *--------------------------------------------------------------------------- * * Tk_GetFontMetrics -- * * Returns overall ascent and descent metrics for the given font. * These values can be used to space multiple lines of text and * to align the baselines of text in different fonts. * * Results: * If *heightPtr is non-NULL, it is filled with the overall height * of the font, which is the sum of the ascent and descent. * If *ascentPtr or *descentPtr is non-NULL, they are filled with * the ascent and/or descent information for the font. * * Side effects: * None. * *--------------------------------------------------------------------------- */ void Tk_GetFontMetrics(tkfont, fmPtr) Tk_Font tkfont; /* Font in which metrics are calculated. */ Tk_FontMetrics *fmPtr; /* Pointer to structure in which font * metrics for tkfont will be stored. */ { TkFont *fontPtr; fontPtr = (TkFont *) tkfont; fmPtr->ascent = fontPtr->fm.ascent; fmPtr->descent = fontPtr->fm.descent; fmPtr->linespace = fontPtr->fm.ascent + fontPtr->fm.descent; } /* *--------------------------------------------------------------------------- * * Tk_PostscriptFontName -- * * Given a Tk_Font, return the name of the corresponding Postscript * font. * * Results: * The return value is the pointsize of the given Tk_Font. * The name of the Postscript font is appended to dsPtr. * * Side effects: * If the font does not exist on the printer, the print job will * fail at print time. Given a "reasonable" Postscript printer, * the following Tk_Font font families should print correctly: * * Avant Garde, Arial, Bookman, Courier, Courier New, Geneva, * Helvetica, Monaco, New Century Schoolbook, New York, * Palatino, Symbol, Times, Times New Roman, Zapf Chancery, * and Zapf Dingbats. * * Any other Tk_Font font families may not print correctly * because the computed Postscript font name may be incorrect. * *--------------------------------------------------------------------------- */ int Tk_PostscriptFontName(tkfont, dsPtr) Tk_Font tkfont; /* Font in which text will be printed. */ Tcl_DString *dsPtr; /* Pointer to an initialized Tcl_DString to * which the name of the Postscript font that * corresponds to tkfont will be appended. */ { TkFont *fontPtr; char *family, *weightString, *slantString; char *src, *dest; int upper, len; len = Tcl_DStringLength(dsPtr); fontPtr = (TkFont *) tkfont; /* * Convert the case-insensitive Tk_Font family name to the * case-sensitive Postscript family name. Take out any spaces and * capitalize the first letter of each word. */ family = fontPtr->fa.family; if (strncasecmp(family, "itc ", 4) == 0) { family = family + 4; } if ((strcasecmp(family, "Arial") == 0) || (strcasecmp(family, "Geneva") == 0)) { family = "Helvetica"; } else if ((strcasecmp(family, "Times New Roman") == 0) || (strcasecmp(family, "New York") == 0)) { family = "Times"; } else if ((strcasecmp(family, "Courier New") == 0) || (strcasecmp(family, "Monaco") == 0)) { family = "Courier"; } else if (strcasecmp(family, "AvantGarde") == 0) { family = "AvantGarde"; } else if (strcasecmp(family, "ZapfChancery") == 0) { family = "ZapfChancery"; } else if (strcasecmp(family, "ZapfDingbats") == 0) { family = "ZapfDingbats"; } else { /* * Inline, capitalize the first letter of each word, lowercase the * rest of the letters in each word, and then take out the spaces * between the words. This may make the DString shorter, which is * safe to do. */ Tcl_DStringAppend(dsPtr, family, -1); src = dest = Tcl_DStringValue(dsPtr) + len; upper = 1; for (; *src != '\0'; src++, dest++) { while (isspace(UCHAR(*src))) { src++; upper = 1; } *dest = *src; if ((upper != 0) && (islower(UCHAR(*src)))) { *dest = toupper(UCHAR(*src)); } upper = 0; } *dest = '\0'; Tcl_DStringSetLength(dsPtr, dest - Tcl_DStringValue(dsPtr)); family = Tcl_DStringValue(dsPtr) + len; } if (family != Tcl_DStringValue(dsPtr) + len) { Tcl_DStringAppend(dsPtr, family, -1); family = Tcl_DStringValue(dsPtr) + len; } if (strcasecmp(family, "NewCenturySchoolbook") == 0) { Tcl_DStringSetLength(dsPtr, len); Tcl_DStringAppend(dsPtr, "NewCenturySchlbk", -1); family = Tcl_DStringValue(dsPtr) + len; } /* * Get the string to use for the weight. */ weightString = NULL; if (fontPtr->fa.weight == TK_FW_NORMAL) { if (strcmp(family, "Bookman") == 0) { weightString = "Light"; } else if (strcmp(family, "AvantGarde") == 0) { weightString = "Book"; } else if (strcmp(family, "ZapfChancery") == 0) { weightString = "Medium"; } } else { if ((strcmp(family, "Bookman") == 0) || (strcmp(family, "AvantGarde") == 0)) { weightString = "Demi"; } else { weightString = "Bold"; } } /* * Get the string to use for the slant. */ slantString = NULL; if (fontPtr->fa.slant == TK_FS_ROMAN) { ; } else { if ((strcmp(family, "Helvetica") == 0) || (strcmp(family, "Courier") == 0) || (strcmp(family, "AvantGarde") == 0)) { slantString = "Oblique"; } else { slantString = "Italic"; } } /* * The string "Roman" needs to be added to some fonts that are not bold * and not italic. */ if ((slantString == NULL) && (weightString == NULL)) { if ((strcmp(family, "Times") == 0) || (strcmp(family, "NewCenturySchlbk") == 0) || (strcmp(family, "Palatino") == 0)) { Tcl_DStringAppend(dsPtr, "-Roman", -1); } } else { Tcl_DStringAppend(dsPtr, "-", -1); if (weightString != NULL) { Tcl_DStringAppend(dsPtr, weightString, -1); } if (slantString != NULL) { Tcl_DStringAppend(dsPtr, slantString, -1); } } return fontPtr->fa.pointsize; } /* *--------------------------------------------------------------------------- * * Tk_TextWidth -- * * A wrapper function for the more complicated interface of * Tk_MeasureChars. Computes how much space the given * simple string needs. * * Results: * The return value is the width (in pixels) of the given string. * * Side effects: * None. * *--------------------------------------------------------------------------- */ int Tk_TextWidth(tkfont, string, numChars) Tk_Font tkfont; /* Font in which text will be measured. */ CONST char *string; /* String whose width will be computed. */ int numChars; /* Number of characters to consider from * string, or < 0 for strlen(). */ { int width; if (numChars < 0) { numChars = strlen(string); } Tk_MeasureChars(tkfont, string, numChars, 0, 0, &width); return width; } /* *--------------------------------------------------------------------------- * * Tk_UnderlineChars -- * * This procedure draws an underline for a given range of characters * in a given string. It doesn't draw the characters (which are * assumed to have been displayed previously); it just draws the * underline. This procedure would mainly be used to quickly * underline a few characters without having to construct an * underlined font. To produce properly underlined text, the * appropriate underlined font should be constructed and used. * * Results: * None. * * Side effects: * Information gets displayed in "drawable". * *---------------------------------------------------------------------- */ void Tk_UnderlineChars(display, drawable, gc, tkfont, string, x, y, firstChar, lastChar) Display *display; /* Display on which to draw. */ Drawable drawable; /* Window or pixmap in which to draw. */ GC gc; /* Graphics context for actually drawing * line. */ Tk_Font tkfont; /* Font used in GC; must have been allocated * by Tk_GetFont(). Used for character * dimensions, etc. */ CONST char *string; /* String containing characters to be * underlined or overstruck. */ int x, y; /* Coordinates at which first character of * string is drawn. */ int firstChar; /* Index of first character. */ int lastChar; /* Index of one after the last character. */ { TkFont *fontPtr; int startX, endX; fontPtr = (TkFont *) tkfont; Tk_MeasureChars(tkfont, string, firstChar, 0, 0, &startX); Tk_MeasureChars(tkfont, string, lastChar, 0, 0, &endX); XFillRectangle(display, drawable, gc, x + startX, y + fontPtr->underlinePos, (unsigned int) (endX - startX), (unsigned int) fontPtr->underlineHeight); } /* *--------------------------------------------------------------------------- * * Tk_ComputeTextLayout -- * * Computes the amount of screen space needed to display a * multi-line, justified string of text. Records all the * measurements that were done to determine to size and * positioning of the individual lines of text; this information * can be used by the Tk_DrawTextLayout() procedure to * display the text quickly (without remeasuring it). * * This procedure is useful for simple widgets that want to * display single-font, multi-line text and want Tk to handle the * details. * * Results: * The return value is a Tk_TextLayout token that holds the * measurement information for the given string. The token is * only valid for the given string. If the string is freed, * the token is no longer valid and must also be freed. To free * the token, call Tk_FreeTextLayout(). * * The dimensions of the screen area needed to display the text * are stored in *widthPtr and *heightPtr. * * Side effects: * Memory is allocated to hold the measurement information. * *--------------------------------------------------------------------------- */ Tk_TextLayout Tk_ComputeTextLayout(tkfont, string, numChars, wrapLength, justify, flags, widthPtr, heightPtr) Tk_Font tkfont; /* Font that will be used to display text. */ CONST char *string; /* String whose dimensions are to be * computed. */ int numChars; /* Number of characters to consider from * string, or < 0 for strlen(). */ int wrapLength; /* Longest permissible line length, in * pixels. <= 0 means no automatic wrapping: * just let lines get as long as needed. */ Tk_Justify justify; /* How to justify lines. */ int flags; /* Flag bits OR-ed together. * TK_IGNORE_TABS means that tab characters * should not be expanded. TK_IGNORE_NEWLINES * means that newline characters should not * cause a line break. */ int *widthPtr; /* Filled with width of string. */ int *heightPtr; /* Filled with height of string. */ { TkFont *fontPtr; CONST char *start, *end, *special; int n, y, charsThisChunk, maxChunks; int baseline, height, curX, newX, maxWidth; TextLayout *layoutPtr; LayoutChunk *chunkPtr; CONST TkFontMetrics *fmPtr; #define MAX_LINES 50 int staticLineLengths[MAX_LINES]; int *lineLengths; int maxLines, curLine, layoutHeight; lineLengths = staticLineLengths; maxLines = MAX_LINES; fontPtr = (TkFont *) tkfont; fmPtr = &fontPtr->fm; height = fmPtr->ascent + fmPtr->descent; if (numChars < 0) { numChars = strlen(string); } maxChunks = 1; layoutPtr = (TextLayout *) ckalloc(sizeof(TextLayout) + (maxChunks - 1) * sizeof(LayoutChunk)); layoutPtr->tkfont = tkfont; layoutPtr->string = string; layoutPtr->numChunks = 0; baseline = fmPtr->ascent; maxWidth = 0; /* * Divide the string up into simple strings and measure each string. */ curX = 0; end = string + numChars; special = string; flags &= TK_IGNORE_TABS | TK_IGNORE_NEWLINES; flags |= TK_WHOLE_WORDS | TK_AT_LEAST_ONE; curLine = 0; for (start = string; start < end; ) { if (start >= special) { /* * Find the next special character in the string. */ for (special = start; special < end; special++) { if (!(flags & TK_IGNORE_NEWLINES)) { if ((*special == '\n') || (*special == '\r')) { break; } } if (!(flags & TK_IGNORE_TABS)) { if (*special == '\t') { break; } } } } /* * Special points at the next special character (or the end of the * string). Process characters between start and special. */ chunkPtr = NULL; if (start < special) { charsThisChunk = Tk_MeasureChars(tkfont, start, special - start, wrapLength - curX, flags, &newX); newX += curX; flags &= ~TK_AT_LEAST_ONE; if (charsThisChunk > 0) { chunkPtr = NewChunk(&layoutPtr, &maxChunks, start, charsThisChunk, curX, newX, baseline); start += charsThisChunk; curX = newX; } } if ((start == special) && (special < end)) { /* * Handle the special character. */ chunkPtr = NULL; if (*special == '\t') { newX = curX + fontPtr->tabWidth; newX -= newX % fontPtr->tabWidth; NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX, baseline)->numDisplayChars = -1; start++; if ((start < end) && ((wrapLength <= 0) || (newX <= wrapLength))) { /* * More chars can still fit on this line. */ curX = newX; flags &= ~TK_AT_LEAST_ONE; continue; } } else { NewChunk(&layoutPtr, &maxChunks, start, 1, curX, 1000000000, baseline)->numDisplayChars = -1; start++; goto wrapLine; } } /* * No more characters are going to go on this line, either because * no more characters can fit or there are no more characters left. * Consume all extra spaces at end of line. */ while ((start < end) && isspace(UCHAR(*start))) { if (!(flags & TK_IGNORE_NEWLINES)) { if ((*start == '\n') || (*start == '\r')) { break; } } if (!(flags & TK_IGNORE_TABS)) { if (*start == '\t') { break; } } start++; } if (chunkPtr != NULL) { /* * Append all the extra spaces on this line to the end of the * last text chunk. */ charsThisChunk = start - (chunkPtr->start + chunkPtr->numChars); if (charsThisChunk > 0) { chunkPtr->numChars += Tk_MeasureChars(tkfont, chunkPtr->start + chunkPtr->numChars, charsThisChunk, 0, 0, &chunkPtr->totalWidth); chunkPtr->totalWidth += curX; } } wrapLine: flags |= TK_AT_LEAST_ONE; /* * Save current line length, then move current position to start of * next line. */ if (curX > maxWidth) { maxWidth = curX; } /* * Remember width of this line, so that all chunks on this line * can be centered or right justified, if necessary. */ if (curLine >= maxLines) { int *newLengths; newLengths = (int *) ckalloc(2 * maxLines * sizeof(int)); memcpy((void *) newLengths, lineLengths, maxLines * sizeof(int)); if (lineLengths != staticLineLengths) { ckfree((char *) lineLengths); } lineLengths = newLengths; maxLines *= 2; } lineLengths[curLine] = curX; curLine++; curX = 0; baseline += height; } /* * Using maximum line length, shift all the chunks so that the lines are * all justified correctly. */ curLine = 0; chunkPtr = layoutPtr->chunks; y = chunkPtr->y; for (n = 0; n < layoutPtr->numChunks; n++) { int extra; if (chunkPtr->y != y) { curLine++; y = chunkPtr->y; } extra = maxWidth - lineLengths[curLine]; if (justify == TK_JUSTIFY_CENTER) { chunkPtr->x += extra / 2; } else if (justify == TK_JUSTIFY_RIGHT) { chunkPtr->x += extra; } chunkPtr++; } layoutPtr->width = maxWidth; layoutHeight = baseline - fmPtr->ascent; if (layoutPtr->numChunks == 0) { layoutHeight = height; /* * This fake chunk is used by the other procedures so that they can * pretend that there is a chunk with no chars in it, which makes * the coding simpler. */ layoutPtr->numChunks = 1; layoutPtr->chunks[0].start = string; layoutPtr->chunks[0].numChars = 0; layoutPtr->chunks[0].numDisplayChars = -1; layoutPtr->chunks[0].x = 0; layoutPtr->chunks[0].y = fmPtr->ascent; layoutPtr->chunks[0].totalWidth = 0; layoutPtr->chunks[0].displayWidth = 0; } if (widthPtr != NULL) { *widthPtr = layoutPtr->width; } if (heightPtr != NULL) { *heightPtr = layoutHeight; } if (lineLengths != staticLineLengths) { ckfree((char *) lineLengths); } return (Tk_TextLayout) layoutPtr; } /* *--------------------------------------------------------------------------- * * Tk_FreeTextLayout -- * * This procedure is called to release the storage associated with * a Tk_TextLayout when it is no longer needed. * * Results: * None. * * Side effects: * Memory is freed. * *--------------------------------------------------------------------------- */ void Tk_FreeTextLayout(textLayout) Tk_TextLayout textLayout; /* The text layout to be released. */ { TextLayout *layoutPtr; layoutPtr = (TextLayout *) textLayout; if (layoutPtr != NULL) { ckfree((char *) layoutPtr); } } /* *--------------------------------------------------------------------------- * * Tk_DrawTextLayout -- * * Use the information in the Tk_TextLayout token to display a * multi-line, justified string of text. * * This procedure is useful for simple widgets that need to * display single-font, multi-line text and want Tk to handle * the details. * * Results: * None. * * Side effects: * Text drawn on the screen. * *--------------------------------------------------------------------------- */ void Tk_DrawTextLayout(display, drawable, gc, layout, x, y, firstChar, lastChar) Display *display; /* Display on which to draw. */ Drawable drawable; /* Window or pixmap in which to draw. */ GC gc; /* Graphics context to use for drawing text. */ Tk_TextLayout layout; /* Layout information, from a previous call * to Tk_ComputeTextLayout(). */ int x, y; /* Upper-left hand corner of rectangle in * which to draw (pixels). */ int firstChar; /* The index of the first character to draw * from the given text item. 0 specfies the * beginning. */ int lastChar; /* The index just after the last character * to draw from the given text item. A number * < 0 means to draw all characters. */ { TextLayout *layoutPtr; int i, numDisplayChars, drawX; LayoutChunk *chunkPtr; layoutPtr = (TextLayout *) layout; if (layoutPtr == NULL) { return; } if (lastChar < 0) { lastChar = 100000000; } chunkPtr = layoutPtr->chunks; for (i = 0; i < layoutPtr->numChunks; i++) { numDisplayChars = chunkPtr->numDisplayChars; if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) { if (firstChar <= 0) { drawX = 0; firstChar = 0; } else { Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start, firstChar, 0, 0, &drawX); } if (lastChar < numDisplayChars) { numDisplayChars = lastChar; } Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont, chunkPtr->start + firstChar, numDisplayChars - firstChar, x + chunkPtr->x + drawX, y + chunkPtr->y); } firstChar -= chunkPtr->numChars; lastChar -= chunkPtr->numChars; if (lastChar <= 0) { break; } chunkPtr++; } } /* *--------------------------------------------------------------------------- * * Tk_UnderlineTextLayout -- * * Use the information in the Tk_TextLayout token to display an * underline below an individual character. This procedure does * not draw the text, just the underline. * * This procedure is useful for simple widgets that need to * display single-font, multi-line text with an individual * character underlined and want Tk to handle the details. * To display larger amounts of underlined text, construct * and use an underlined font. * * Results: * None. * * Side effects: * Underline drawn on the screen. * *--------------------------------------------------------------------------- */ void Tk_UnderlineTextLayout(display, drawable, gc, layout, x, y, underline) Display *display; /* Display on which to draw. */ Drawable drawable; /* Window or pixmap in which to draw. */ GC gc; /* Graphics context to use for drawing text. */ Tk_TextLayout layout; /* Layout information, from a previous call * to Tk_ComputeTextLayout(). */ int x, y; /* Upper-left hand corner of rectangle in * which to draw (pixels). */ int underline; /* Index of the single character to * underline, or -1 for no underline. */ { TextLayout *layoutPtr; TkFont *fontPtr; int xx, yy, width, height; if ((Tk_CharBbox(layout, underline, &xx, &yy, &width, &height) != 0) && (width != 0)) { layoutPtr = (TextLayout *) layout; fontPtr = (TkFont *) layoutPtr->tkfont; XFillRectangle(display, drawable, gc, x + xx, y + yy + fontPtr->fm.ascent + fontPtr->underlinePos, (unsigned int) width, (unsigned int) fontPtr->underlineHeight); } } /* *--------------------------------------------------------------------------- * * Tk_PointToChar -- * * Use the information in the Tk_TextLayout token to determine the * character closest to the given point. The point must be * specified with respect to the upper-left hand corner of the * text layout, which is considered to be located at (0, 0). * * Any point whose y-value is less that 0 will be considered closest * to the first character in the text layout; any point whose y-value * is greater than the height of the text layout will be considered * closest to the last character in the text layout. * * Any point whose x-value is less than 0 will be considered closest * to the first character on that line; any point whose x-value is * greater than the width of the text layout will be considered * closest to the last character on that line. * * Results: * The return value is the index of the character that was * closest to the point. Given a text layout with no characters, * the value 0 will always be returned, referring to a hypothetical * zero-width placeholder character. * * Side effects: * None. * *--------------------------------------------------------------------------- */ int Tk_PointToChar(layout, x, y) Tk_TextLayout layout; /* Layout information, from a previous call * to Tk_ComputeTextLayout(). */ int x, y; /* Coordinates of point to check, with * respect to the upper-left corner of the * text layout. */ { TextLayout *layoutPtr; LayoutChunk *chunkPtr, *lastPtr; TkFont *fontPtr; int i, n, dummy, baseline, pos; if (y < 0) { /* * Point lies above any line in this layout. Return the index of * the first char. */ return 0; } /* * Find which line contains the point. */ layoutPtr = (TextLayout *) layout; fontPtr = (TkFont *) layoutPtr->tkfont; lastPtr = chunkPtr = layoutPtr->chunks; for (i = 0; i < layoutPtr->numChunks; i++) { baseline = chunkPtr->y; if (y < baseline + fontPtr->fm.descent) { if (x < chunkPtr->x) { /* * Point is to the left of all chunks on this line. Return * the index of the first character on this line. */ return chunkPtr->start - layoutPtr->string; } if (x >= layoutPtr->width) { /* * If point lies off right side of the text layout, return * the last char in the last chunk on this line. Without * this, it might return the index of the first char that * was located outside of the text layout. */ x = INT_MAX; } /* * Examine all chunks on this line to see which one contains * the specified point. */ lastPtr = chunkPtr; while ((i < layoutPtr->numChunks) && (chunkPtr->y == baseline)) { if (x < chunkPtr->x + chunkPtr->totalWidth) { /* * Point falls on one of the characters in this chunk. */ if (chunkPtr->numDisplayChars < 0) { /* * This is a special chunk that encapsulates a single * tab or newline char. */ return chunkPtr->start - layoutPtr->string; } n = Tk_MeasureChars((Tk_Font) fontPtr, chunkPtr->start, chunkPtr->numChars, x + 1 - chunkPtr->x, TK_PARTIAL_OK, &dummy); return (chunkPtr->start + n - 1) - layoutPtr->string; } lastPtr = chunkPtr; chunkPtr++; i++; } /* * Point is to the right of all chars in all the chunks on this * line. Return the index just past the last char in the last * chunk on this line. */ pos = (lastPtr->start + lastPtr->numChars) - layoutPtr->string; if (i < layoutPtr->numChunks) { pos--; } return pos; } lastPtr = chunkPtr; chunkPtr++; } /* * Point lies below any line in this text layout. Return the index * just past the last char. */ return (lastPtr->start + lastPtr->numChars) - layoutPtr->string; } /* *--------------------------------------------------------------------------- * * Tk_CharBbox -- * * Use the information in the Tk_TextLayout token to return the * bounding box for the character specified by index. * * The width of the bounding box is the advance width of the * character, and does not include and left- or right-bearing. * Any character that extends partially outside of the * text layout is considered to be truncated at the edge. Any * character which is located completely outside of the text * layout is considered to be zero-width and pegged against * the edge. * * The height of the bounding box is the line height for this font, * extending from the top of the ascent to the bottom of the * descent. Information about the actual height of the individual * letter is not available. * * A text layout that contains no characters is considered to * contain a single zero-width placeholder character. * * Results: * The return value is 0 if the index did not specify a character * in the text layout, or non-zero otherwise. In that case, * *bbox is filled with the bounding box of the character. * * Side effects: * None. * *--------------------------------------------------------------------------- */ int Tk_CharBbox(layout, index, xPtr, yPtr, widthPtr, heightPtr) Tk_TextLayout layout; /* Layout information, from a previous call to * Tk_ComputeTextLayout(). */ int index; /* The index of the character whose bbox is * desired. */ int *xPtr, *yPtr; /* Filled with the upper-left hand corner, in * pixels, of the bounding box for the character * specified by index, if non-NULL. */ int *widthPtr, *heightPtr; /* Filled with the width and height of the * bounding box for the character specified by * index, if non-NULL. */ { TextLayout *layoutPtr; LayoutChunk *chunkPtr; int i, x, w; Tk_Font tkfont; TkFont *fontPtr; if (index < 0) { return 0; } layoutPtr = (TextLayout *) layout; chunkPtr = layoutPtr->chunks; tkfont = layoutPtr->tkfont; fontPtr = (TkFont *) tkfont; for (i = 0; i < layoutPtr->numChunks; i++) { if (chunkPtr->numDisplayChars < 0) { if (index == 0) { x = chunkPtr->x; w = chunkPtr->totalWidth; goto check; } } else if (index < chunkPtr->numChars) { if (xPtr != NULL) { Tk_MeasureChars(tkfont, chunkPtr->start, index, 0, 0, &x); x += chunkPtr->x; } if (widthPtr != NULL) { Tk_MeasureChars(tkfont, chunkPtr->start + index, 1, 0, 0, &w); } goto check; } index -= chunkPtr->numChars; chunkPtr++; } if (index == 0) { /* * Special case to get location just past last char in layout. */ chunkPtr--; x = chunkPtr->x + chunkPtr->totalWidth; w = 0; } else { return 0; } /* * Ensure that the bbox lies within the text layout. This forces all * chars that extend off the right edge of the text layout to have * truncated widths, and all chars that are completely off the right * edge of the text layout to peg to the edge and have 0 width. */ check: if (yPtr != NULL) { *yPtr = chunkPtr->y - fontPtr->fm.ascent; } if (heightPtr != NULL) { *heightPtr = fontPtr->fm.ascent + fontPtr->fm.descent; } if (x > layoutPtr->width) { x = layoutPtr->width; } if (xPtr != NULL) { *xPtr = x; } if (widthPtr != NULL) { if (x + w > layoutPtr->width) { w = layoutPtr->width - x; } *widthPtr = w; } return 1; } /* *--------------------------------------------------------------------------- * * Tk_DistanceToTextLayout -- * * Computes the distance in pixels from the given point to the * given text layout. Non-displaying space characters that occur * at the end of individual lines in the text layout are ignored * for hit detection purposes. * * Results: * The return value is 0 if the point (x, y) is inside the text * layout. If the point isn't inside the text layout then the * return value is the distance in pixels from the point to the * text item. * * Side effects: * None. * *--------------------------------------------------------------------------- */ int Tk_DistanceToTextLayout(layout, x, y) Tk_TextLayout layout; /* Layout information, from a previous call * to Tk_ComputeTextLayout(). */ int x, y; /* Coordinates of point to check, with * respect to the upper-left corner of the * text layout (in pixels). */ { int i, x1, x2, y1, y2, xDiff, yDiff, dist, minDist, ascent, descent; LayoutChunk *chunkPtr; TextLayout *layoutPtr; TkFont *fontPtr; layoutPtr = (TextLayout *) layout; fontPtr = (TkFont *) layoutPtr->tkfont; ascent = fontPtr->fm.ascent; descent = fontPtr->fm.descent; minDist = 0; chunkPtr = layoutPtr->chunks; for (i = 0; i < layoutPtr->numChunks; i++) { if (chunkPtr->start[0] == '\n') { /* * Newline characters are not counted when computing distance * (but tab characters would still be considered). */ chunkPtr++; continue; } x1 = chunkPtr->x; y1 = chunkPtr->y - ascent; x2 = chunkPtr->x + chunkPtr->displayWidth; y2 = chunkPtr->y + descent; if (x < x1) { xDiff = x1 - x; } else if (x >= x2) { xDiff = x - x2 + 1; } else { xDiff = 0; } if (y < y1) { yDiff = y1 - y; } else if (y >= y2) { yDiff = y - y2 + 1; } else { yDiff = 0; } if ((xDiff == 0) && (yDiff == 0)) { return 0; } dist = (int) hypot((double) xDiff, (double) yDiff); if ((dist < minDist) || (minDist == 0)) { minDist = dist; } chunkPtr++; } return minDist; } /* *--------------------------------------------------------------------------- * * Tk_IntersectTextLayout -- * * Determines whether a text layout lies entirely inside, * entirely outside, or overlaps a given rectangle. Non-displaying * space characters that occur at the end of individual lines in * the text layout are ignored for intersection calculations. * * Results: * The return value is -1 if the text layout is entirely outside of * the rectangle, 0 if it overlaps, and 1 if it is entirely inside * of the rectangle. * * Side effects: * None. * *--------------------------------------------------------------------------- */ int Tk_IntersectTextLayout(layout, x, y, width, height) Tk_TextLayout layout; /* Layout information, from a previous call * to Tk_ComputeTextLayout(). */ int x, y; /* Upper-left hand corner, in pixels, of * rectangular area to compare with text * layout. Coordinates are with respect to * the upper-left hand corner of the text * layout itself. */ int width, height; /* The width and height of the above * rectangular area, in pixels. */ { int result, i, x1, y1, x2, y2; TextLayout *layoutPtr; LayoutChunk *chunkPtr; TkFont *fontPtr; int left, top, right, bottom; /* * Scan the chunks one at a time, seeing whether each is entirely in, * entirely out, or overlapping the rectangle. If an overlap is * detected, return immediately; otherwise wait until all chunks have * been processed and see if they were all inside or all outside. */ layoutPtr = (TextLayout *) layout; chunkPtr = layoutPtr->chunks; fontPtr = (TkFont *) layoutPtr->tkfont; left = x; top = y; right = x + width; bottom = y + height; result = 0; for (i = 0; i < layoutPtr->numChunks; i++) { if (chunkPtr->start[0] == '\n') { /* * Newline characters are not counted when computing area * intersection (but tab characters would still be considered). */ chunkPtr++; continue; } x1 = chunkPtr->x; y1 = chunkPtr->y - fontPtr->fm.ascent; x2 = chunkPtr->x + chunkPtr->displayWidth; y2 = chunkPtr->y + fontPtr->fm.descent; if ((right < x1) || (left >= x2) || (bottom < y1) || (top >= y2)) { if (result == 1) { return 0; } result = -1; } else if ((x1 < left) || (x2 >= right) || (y1 < top) || (y2 >= bottom)) { return 0; } else if (result == -1) { return 0; } else { result = 1; } chunkPtr++; } return result; } /* *--------------------------------------------------------------------------- * * Tk_TextLayoutToPostscript -- * * Outputs the contents of a text layout in Postscript format. * The set of lines in the text layout will be rendered by the user * supplied Postscript function. The function should be of the form: * * justify x y string function -- * * Justify is -1, 0, or 1, depending on whether the following string * should be left, center, or right justified, x and y is the * location for the origin of the string, string is the sequence * of characters to be printed, and function is the name of the * caller-provided function; the function should leave nothing * on the stack. * * The meaning of the origin of the string (x and y) depends on * the justification. For left justification, x is where the * left edge of the string should appear. For center justification, * x is where the center of the string should appear. And for right * justification, x is where the right edge of the string should * appear. This behavior is necessary because, for example, right * justified text on the screen is justified with screen metrics. * The same string needs to be justified with printer metrics on * the printer to appear in the correct place with respect to other * similarly justified strings. In all circumstances, y is the * location of the baseline for the string. * * Results: * Interp->result is modified to hold the Postscript code that * will render the text layout. * * Side effects: * None. * *--------------------------------------------------------------------------- */ void Tk_TextLayoutToPostscript(interp, layout) Tcl_Interp *interp; /* Filled with Postscript code. */ Tk_TextLayout layout; /* The layout to be rendered. */ { #define MAXUSE 128 char buf[MAXUSE+10]; LayoutChunk *chunkPtr; int i, j, used, c, baseline; TextLayout *layoutPtr; layoutPtr = (TextLayout *) layout; chunkPtr = layoutPtr->chunks; baseline = chunkPtr->y; used = 0; buf[used++] = '('; for (i = 0; i < layoutPtr->numChunks; i++) { if (baseline != chunkPtr->y) { buf[used++] = ')'; buf[used++] = '\n'; buf[used++] = '('; baseline = chunkPtr->y; } if (chunkPtr->numDisplayChars <= 0) { if (chunkPtr->start[0] == '\t') { buf[used++] = '\\'; buf[used++] = 't'; } } else { for (j = 0; j < chunkPtr->numDisplayChars; j++) { c = UCHAR(chunkPtr->start[j]); if ((c == '(') || (c == ')') || (c == '\\') || (c < 0x20) || (c >= UCHAR(0x7f))) { /* * Tricky point: the "03" is necessary in the sprintf * below, so that a full three digits of octal are * always generated. Without the "03", a number * following this sequence could be interpreted by * Postscript as part of this sequence. */ sprintf(buf + used, "\\%03o", c); used += 4; } else { buf[used++] = c; } if (used >= MAXUSE) { buf[used] = '\0'; Tcl_AppendResult(interp, buf, (char *) NULL); used = 0; } } } if (used >= MAXUSE) { /* * If there are a whole bunch of returns or tabs in a row, * then buf[] could get filled up. */ buf[used] = '\0'; Tcl_AppendResult(interp, buf, (char *) NULL); used = 0; } chunkPtr++; } buf[used++] = ')'; buf[used++] = '\n'; buf[used] = '\0'; Tcl_AppendResult(interp, buf, (char *) NULL); } /* *--------------------------------------------------------------------------- * * TkInitFontAttributes -- * * Initialize the font attributes structure to contain sensible * values. This must be called before using any other font * attributes functions. * * Results: * None. * * Side effects. * None. * *--------------------------------------------------------------------------- */ void TkInitFontAttributes(faPtr) TkFontAttributes *faPtr; /* The attributes structure to initialize. */ { faPtr->family = NULL; faPtr->pointsize = 0; faPtr->weight = TK_FW_NORMAL; faPtr->slant = TK_FS_ROMAN; faPtr->underline = 0; faPtr->overstrike = 0; } /* *--------------------------------------------------------------------------- * * ConfigAttributesObj -- * * Process command line options to fill in fields of a properly * initialized font attributes structure. * * Results: * A standard Tcl return value. If TCL_ERROR is returned, an * error message will be left in interp's result object. * * Side effects: * The fields of the font attributes structure get filled in with * information from argc/argv. If an error occurs while parsing, * the font attributes structure will contain all modifications * specified in the command line options up to the point of the * error. * *--------------------------------------------------------------------------- */ static int ConfigAttributesObj(interp, tkwin, objc, objv, faPtr) Tcl_Interp *interp; /* Interp for error return. */ Tk_Window tkwin; /* For display on which font will be used. */ int objc; /* Number of elements in argv. */ Tcl_Obj *CONST objv[]; /* Command line options. */ TkFontAttributes *faPtr; /* Font attributes structure whose fields * are to be modified. Structure must already * be properly initialized. */ { int i, n, index; Tcl_Obj *value; char *option, *string; if (objc & 1) { string = Tcl_GetStringFromObj(objv[objc - 1], NULL); Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "missing value for \"", string, "\" option", (char *) NULL); return TCL_ERROR; } for (i = 0; i < objc; i += 2) { option = Tcl_GetStringFromObj(objv[i], NULL); value = objv[i + 1]; if (Tcl_GetIndexFromObj(interp, objv[i], fontOpt, "option", 1, &index) != TCL_OK) { return TCL_ERROR; } switch (index) { case FONT_FAMILY: string = Tcl_GetStringFromObj(value, NULL); faPtr->family = Tk_GetUid(string); break; case FONT_SIZE: if (Tcl_GetIntFromObj(interp, value, &n) != TCL_OK) { return TCL_ERROR; } faPtr->pointsize = n; break; case FONT_WEIGHT: string = Tcl_GetStringFromObj(value, NULL); n = TkFindStateNum(interp, option, weightMap, string); if (n == TK_FW_UNKNOWN) { return TCL_ERROR; } faPtr->weight = n; break; case FONT_SLANT: string = Tcl_GetStringFromObj(value, NULL); n = TkFindStateNum(interp, option, slantMap, string); if (n == TK_FS_UNKNOWN) { return TCL_ERROR; } faPtr->slant = n; break; case FONT_UNDERLINE: if (Tcl_GetBooleanFromObj(interp, value, &n) != TCL_OK) { return TCL_ERROR; } faPtr->underline = n; break; case FONT_OVERSTRIKE: if (Tcl_GetBooleanFromObj(interp, value, &n) != TCL_OK) { return TCL_ERROR; } faPtr->overstrike = n; break; } } return TCL_OK; } /* *--------------------------------------------------------------------------- * * GetAttributeInfoObj -- * * Return information about the font attributes as a Tcl list. * * Results: * Interp's result object is modified to hold a description of either * the current value of a single option, or a list of all options * and their current values for the given font attributes. * * Side effects: * None. * *--------------------------------------------------------------------------- */ static void GetAttributeInfoObj(interp, faPtr, objPtr) Tcl_Interp *interp; /* Interp to hold result. */ CONST TkFontAttributes *faPtr; /* The font attributes to inspect. */ Tcl_Obj *objPtr; /* If non-NULL, indicates the single * option whose value is to be * returned. Otherwise * information is returned for * all options. */ { int i, index, start, end, num; #ifdef STk_CODE int bool; #endif char *str; Tcl_Obj *newPtr; start = 0; end = FONT_NUMFIELDS; if (objPtr != NULL) { if (Tcl_GetIndexFromObj(NULL, objPtr, fontOpt, "option", 1, &index) != TCL_OK) { return; } start = index; end = index + 1; } for (i = start; i < end; i++) { str = NULL; num = 0; /* Needed only to prevent compiler * warning. */ #ifdef STk_CODE bool =-1; #endif switch (i) { case FONT_FAMILY: str = faPtr->family; if (str == NULL) { str = ""; } break; case FONT_SIZE: num = faPtr->pointsize; break; case FONT_WEIGHT: str = TkFindStateString(weightMap, faPtr->weight); break; case FONT_SLANT: str = TkFindStateString(slantMap, faPtr->slant); break; case FONT_UNDERLINE: #ifdef STk_CODE bool = faPtr->underline; #else num = faPtr->underline; #endif break; case FONT_OVERSTRIKE: #ifdef STk_CODE bool = faPtr->overstrike; #else num = faPtr->overstrike; #endif break; } if (objPtr == NULL) { Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), #ifdef STk_CODE STk_NewKeywordObj(fontOpt[i])); #else Tcl_NewStringObj(fontOpt[i], -1)); #endif if (str != NULL) { newPtr = Tcl_NewStringObj(str, -1); } else { #ifdef STk_CODE newPtr = (bool>=0) ? Tcl_NewBooleanObj(bool) : Tcl_NewIntObj(num); #else newPtr = Tcl_NewIntObj(num); #endif } Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), newPtr); } else { if (str != NULL) { Tcl_SetStringObj(Tcl_GetObjResult(interp), str, -1); } else { Tcl_SetIntObj(Tcl_GetObjResult(interp), num); } } } } /* *--------------------------------------------------------------------------- * * ParseFontNameObj -- * * Converts a object into a set of font attributes that can be used * to construct a font. * * The string rep of the object can be one of the following forms: * XLFD (see X documentation) * "Family [size [style] [style ...]]" * "-option value [-option value ...]" * * Results: * The return value is TCL_ERROR if the object was syntactically * invalid. In that case an error message is left in interp's * result object. Otherwise, fills the font attribute buffer with * the values parsed from the string and returns TCL_OK; * * Side effects: * None. * *--------------------------------------------------------------------------- */ static int ParseFontNameObj(interp, tkwin, objPtr, faPtr) Tcl_Interp *interp; /* Interp for error return. */ Tk_Window tkwin; /* For display on which font is used. */ Tcl_Obj *objPtr; /* Parseable font description object. */ TkFontAttributes *faPtr; /* Font attributes structure whose fields * are to be modified. Structure must already * be properly initialized. */ { char *dash; int objc, result, i, n; Tcl_Obj **objv; TkXLFDAttributes xa; char *string; string = Tcl_GetStringFromObj(objPtr, NULL); if (*string == '-') { /* * This may be an XLFD or an "-option value" string. * * If the string begins with "-*" or a "-foundry-family-*" pattern, * then consider it an XLFD. */ if (string[1] == '*') { goto xlfd; } dash = strchr(string + 1, '-'); if ((dash != NULL) && (!isspace(UCHAR(dash[-1])))) { goto xlfd; } if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) { return TCL_ERROR; } return ConfigAttributesObj(interp, tkwin, objc, objv, faPtr); } if (*string == '*') { /* * This appears to be an XLFD. */ xlfd: xa.fa = *faPtr; result = TkParseXLFD(string, &xa); if (result == TCL_OK) { *faPtr = xa.fa; return result; } } /* * Wasn't an XLFD or "-option value" string. Try it as a * "font size style" list. */ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) { return TCL_ERROR; } if (objc < 1) { Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "font \"", string, "\" doesn't exist", (char *) NULL); return TCL_ERROR; } faPtr->family = Tk_GetUid(Tcl_GetStringFromObj(objv[0], NULL)); if (objc > 1) { if (Tcl_GetIntFromObj(interp, objv[1], &n) != TCL_OK) { return TCL_ERROR; } faPtr->pointsize = n; } i = 2; if (objc == 3) { if (Tcl_ListObjGetElements(interp, objv[2], &objc, &objv) != TCL_OK) { return TCL_ERROR; } i = 0; } for ( ; i < objc; i++) { string = Tcl_GetStringFromObj(objv[i], NULL); n = TkFindStateNum(NULL, NULL, weightMap, string); if (n != TK_FW_UNKNOWN) { faPtr->weight = n; continue; } n = TkFindStateNum(NULL, NULL, slantMap, string); if (n != TK_FS_UNKNOWN) { faPtr->slant = n; continue; } n = TkFindStateNum(NULL, NULL, underlineMap, string); if (n != 0) { faPtr->underline = n; continue; } n = TkFindStateNum(NULL, NULL, overstrikeMap, string); if (n != 0) { faPtr->overstrike = n; continue; } /* * Unknown style. */ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "unknown font style \"", string, "\"", (char *) NULL); return TCL_ERROR; } return TCL_OK; } /* *--------------------------------------------------------------------------- * * TkParseXLFD -- * * Break up a fully specified XLFD into a set of font attributes. * * Results: * Return value is TCL_ERROR if string was not a fully specified XLFD. * Otherwise, fills font attribute buffer with the values parsed * from the XLFD and returns TCL_OK. * * Side effects: * None. * *--------------------------------------------------------------------------- */ int TkParseXLFD(string, xaPtr) CONST char *string; /* Parseable font description string. */ TkXLFDAttributes *xaPtr; /* XLFD attributes structure whose fields * are to be modified. Structure must already * be properly initialized. */ { char *src; CONST char *str; int i, j; char *field[XLFD_NUMFIELDS + 2]; Tcl_DString ds; memset(field, '\0', sizeof(field)); str = string; if (*str == '-') { str++; } Tcl_DStringInit(&ds); Tcl_DStringAppend(&ds, (char *) str, -1); src = Tcl_DStringValue(&ds); field[0] = src; for (i = 0; *src != '\0'; src++) { if (isupper(UCHAR(*src))) { *src = tolower(UCHAR(*src)); } if (*src == '-') { i++; if (i > XLFD_NUMFIELDS) { break; } *src = '\0'; field[i] = src + 1; } } /* * An XLFD of the form -adobe-times-medium-r-*-12-*-* is pretty common, * but it is (strictly) malformed, because the first * is eliding both * the Setwidth and the Addstyle fields. If the Addstyle field is a * number, then assume the above incorrect form was used and shift all * the rest of the fields up by one, so the number gets interpreted * as a pixelsize. This fix is so that we don't get a million reports * that "it works under X, but gives a syntax error under Windows". */ if ((i > XLFD_ADD_STYLE) && (FieldSpecified(field[XLFD_ADD_STYLE]))) { if (atoi(field[XLFD_ADD_STYLE]) != 0) { for (j = XLFD_NUMFIELDS - 1; j >= XLFD_ADD_STYLE; j--) { field[j + 1] = field[j]; } field[XLFD_ADD_STYLE] = NULL; i++; } } /* * Bail if we don't have enough of the fields (up to pointsize). */ if (i < XLFD_FAMILY) { Tcl_DStringFree(&ds); return TCL_ERROR; } if (FieldSpecified(field[XLFD_FOUNDRY])) { xaPtr->foundry = Tk_GetUid(field[XLFD_FOUNDRY]); } if (FieldSpecified(field[XLFD_FAMILY])) { xaPtr->fa.family = Tk_GetUid(field[XLFD_FAMILY]); } if (FieldSpecified(field[XLFD_WEIGHT])) { xaPtr->fa.weight = TkFindStateNum(NULL, NULL, xlfdWeightMap, field[XLFD_WEIGHT]); } if (FieldSpecified(field[XLFD_SLANT])) { xaPtr->slant = TkFindStateNum(NULL, NULL, xlfdSlantMap, field[XLFD_SLANT]); if (xaPtr->slant == TK_FS_ROMAN) { xaPtr->fa.slant = TK_FS_ROMAN; } else { xaPtr->fa.slant = TK_FS_ITALIC; } } if (FieldSpecified(field[XLFD_SETWIDTH])) { xaPtr->setwidth = TkFindStateNum(NULL, NULL, xlfdSetwidthMap, field[XLFD_SETWIDTH]); } /* XLFD_ADD_STYLE ignored. */ /* * Pointsize in tenths of a point, but treat it as tenths of a pixel. */ if (FieldSpecified(field[XLFD_POINT_SIZE])) { if (field[XLFD_POINT_SIZE][0] == '[') { /* * Some X fonts have the point size specified as follows: * * [ N1 N2 N3 N4 ] * * where N1 is the point size (in points, not decipoints!), and * N2, N3, and N4 are some additional numbers that I don't know * the purpose of, so I ignore them. */ xaPtr->fa.pointsize = atoi(field[XLFD_POINT_SIZE] + 1); } else if (Tcl_GetInt(NULL, field[XLFD_POINT_SIZE], &xaPtr->fa.pointsize) == TCL_OK) { xaPtr->fa.pointsize /= 10; } else { return TCL_ERROR; } } /* * Pixel height of font. If specified, overrides pointsize. */ if (FieldSpecified(field[XLFD_PIXEL_SIZE])) { if (field[XLFD_PIXEL_SIZE][0] == '[') { /* * Some X fonts have the pixel size specified as follows: * * [ N1 N2 N3 N4 ] * * where N1 is the pixel size, and where N2, N3, and N4 * are some additional numbers that I don't know * the purpose of, so I ignore them. */ xaPtr->fa.pointsize = atoi(field[XLFD_PIXEL_SIZE] + 1); } else if (Tcl_GetInt(NULL, field[XLFD_PIXEL_SIZE], &xaPtr->fa.pointsize) != TCL_OK) { return TCL_ERROR; } } xaPtr->fa.pointsize = -xaPtr->fa.pointsize; /* XLFD_RESOLUTION_X ignored. */ /* XLFD_RESOLUTION_Y ignored. */ /* XLFD_SPACING ignored. */ /* XLFD_AVERAGE_WIDTH ignored. */ if (FieldSpecified(field[XLFD_REGISTRY])) { xaPtr->charset = TkFindStateNum(NULL, NULL, xlfdCharsetMap, field[XLFD_REGISTRY]); } if (FieldSpecified(field[XLFD_ENCODING])) { xaPtr->encoding = atoi(field[XLFD_ENCODING]); } Tcl_DStringFree(&ds); return TCL_OK; } /* *--------------------------------------------------------------------------- * * FieldSpecified -- * * Helper function for TkParseXLFD(). Determines if a field in the * XLFD was set to a non-null, non-don't-care value. * * Results: * The return value is 0 if the field in the XLFD was not set and * should be ignored, non-zero otherwise. * * Side effects: * None. * *--------------------------------------------------------------------------- */ static int FieldSpecified(field) CONST char *field; /* The field of the XLFD to check. Strictly * speaking, only when the string is "*" does it mean * don't-care. However, an unspecified or question * mark is also interpreted as don't-care. */ { char ch; if (field == NULL) { return 0; } ch = field[0]; return (ch != '*' && ch != '?'); } /* *--------------------------------------------------------------------------- * * NewChunk -- * * Helper function for Tk_ComputeTextLayout(). Encapsulates a * measured set of characters in a chunk that can be quickly * drawn. * * Results: * A pointer to the new chunk in the text layout. * * Side effects: * The text layout is reallocated to hold more chunks as necessary. * * Currently, Tk_ComputeTextLayout() stores contiguous ranges of * "normal" characters in a chunk, along with individual tab * and newline chars in their own chunks. All characters in the * text layout are accounted for. * *--------------------------------------------------------------------------- */ static LayoutChunk * NewChunk(layoutPtrPtr, maxPtr, start, numChars, curX, newX, y) TextLayout **layoutPtrPtr; int *maxPtr; CONST char *start; int numChars; int curX; int newX; int y; { TextLayout *layoutPtr; LayoutChunk *chunkPtr; int maxChunks; size_t s; layoutPtr = *layoutPtrPtr; maxChunks = *maxPtr; if (layoutPtr->numChunks == maxChunks) { maxChunks *= 2; s = sizeof(TextLayout) + ((maxChunks - 1) * sizeof(LayoutChunk)); layoutPtr = (TextLayout *) ckrealloc((char *) layoutPtr, s); *layoutPtrPtr = layoutPtr; *maxPtr = maxChunks; } chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks]; chunkPtr->start = start; chunkPtr->numChars = numChars; chunkPtr->numDisplayChars = numChars; chunkPtr->x = curX; chunkPtr->y = y; chunkPtr->totalWidth = newX - curX; chunkPtr->displayWidth = newX - curX; layoutPtr->numChunks++; return chunkPtr; }