/*
* 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-1998 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* RCS: @(#) $Id: tkFont.c,v 1.21 2002/09/02 19:13:47 hobbs Exp $
*/
#include "tkPort.h"
#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 string font names, values are
* TkFont pointers. */
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
* strings, values are NamedFont pointers. */
TkMainInfo *mainPtr; /* Application that owns this structure. */
int updatePending; /* Non-zero when a World Changed event has
* already been queued to handle a change to
* a named font. */
} TkFontInfo;
/*
* 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 numBytes; /* The number of bytes in this chunk. */
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"},
/* These are additions from the X world */
{TK_FW_NORMAL, "medium"},
{TK_FW_NORMAL, "book"},
{TK_FW_NORMAL, "light"},
{TK_FW_BOLD, "demi"},
{TK_FW_BOLD, "demibold"},
{TK_FW_UNKNOWN, NULL}
};
static TkStateMap slantMap[] = {
{TK_FS_ROMAN, "roman"},
{TK_FS_ITALIC, "italic"},
/* These are additions from the X world */
{TK_FS_ROMAN, "r"},
{TK_FS_ITALIC, "i"},
{TK_FS_ITALIC, "o"},
{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}
};
/*
* The following structure and defines specify the valid builtin options
* when configuring a set of font attributes.
*/
static CONST 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
/*
* Hardcoded font aliases. These are used to describe (mostly) identical
* fonts whose names differ from platform to platform. If the
* user-supplied font name matches any of the names in one of the alias
* lists, the other names in the alias list are also automatically tried.
*/
static char *timesAliases[] = {
"Times", /* Unix. */
"Times New Roman", /* Windows. */
"New York", /* Mac. */
NULL
};
static char *helveticaAliases[] = {
"Helvetica", /* Unix. */
"Arial", /* Windows. */
"Geneva", /* Mac. */
NULL
};
static char *courierAliases[] = {
"Courier", /* Unix and Mac. */
"Courier New", /* Windows. */
NULL
};
static char *minchoAliases[] = {
"mincho", /* Unix. */
"\357\274\255\357\274\263 \346\230\216\346\234\235",
/* Windows (MS mincho). */
"\346\234\254\346\230\216\346\234\235\342\210\222\357\274\255",
/* Mac (honmincho-M). */
NULL
};
static char *gothicAliases[] = {
"gothic", /* Unix. */
"\357\274\255\357\274\263 \343\202\264\343\202\267\343\203\203\343\202\257",
/* Windows (MS goshikku). */
"\344\270\270\343\202\264\343\202\267\343\203\203\343\202\257\342\210\222\357\274\255",
/* Mac (goshikku-M). */
NULL
};
static char *dingbatsAliases[] = {
"dingbats", "zapfdingbats", "itc zapfdingbats",
/* Unix. */
/* Windows. */
"zapf dingbats", /* Mac. */
NULL
};
static char **fontAliases[] = {
timesAliases,
helveticaAliases,
courierAliases,
minchoAliases,
gothicAliases,
dingbatsAliases,
NULL
};
/*
* Hardcoded font classes. If the character cannot be found in the base
* font, the classes are examined in order to see if some other similar
* font should be examined also.
*/
static char *systemClass[] = {
"fixed", /* Unix. */
/* Windows. */
"chicago", "osaka", "sistemny", /* Mac. */
NULL
};
static char *serifClass[] = {
"times", "palatino", "mincho", /* All platforms. */
"song ti", /* Unix. */
"ms serif", "simplified arabic", /* Windows. */
"latinski", /* Mac. */
NULL
};
static char *sansClass[] = {
"helvetica", "gothic", /* All platforms. */
/* Unix. */
"arial unicode ms", /* MS Office etc. */
"ms sans serif", "traditional arabic",
/* Windows. */
"bastion", /* Mac. */
NULL
};
static char *monoClass[] = {
"courier", "gothic", /* All platforms. */
"fangsong ti", /* Unix. */
"simplified arabic fixed", /* Windows. */
"monaco", "pryamoy", /* Mac. */
NULL
};
static char *symbolClass[] = {
"symbol", "dingbats", "wingdings", NULL
};
static char **fontFallbacks[] = {
systemClass,
serifClass,
sansClass,
monoClass,
symbolClass,
NULL
};
/*
* Global fallbacks. If the character could not be found in the preferred
* fallback list, this list is examined. If the character still cannot be
* found, all font families in the system are examined.
*/
static char *globalFontClass[] = {
"symbol", /* All platforms. */
/* Unix. */
"lucida sans unicode", /* Windows. */
"bitstream cyberbit", /* Windows popular CJK font */
"chicago", /* Mac. */
"fixed", /* X11 font with growing repertoire */
"arial unicode ms", /* MS Office etc. */
"unifont", /* GNU font with wide repertoire but ugly */
NULL
};
#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 CreateNamedFont _ANSI_ARGS_((Tcl_Interp *interp,
Tk_Window tkwin, CONST char *name,
TkFontAttributes *faPtr));
static void DupFontObjProc _ANSI_ARGS_((Tcl_Obj *srcObjPtr,
Tcl_Obj *dupObjPtr));
static int FieldSpecified _ANSI_ARGS_((CONST char *field));
static void FreeFontObjProc _ANSI_ARGS_((Tcl_Obj *objPtr));
static int 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 int SetFontFromAny _ANSI_ARGS_((Tcl_Interp *interp,
Tcl_Obj *objPtr));
static void TheWorldHasChanged _ANSI_ARGS_((
ClientData clientData));
static void UpdateDependentFonts _ANSI_ARGS_((TkFontInfo *fiPtr,
Tk_Window tkwin, Tcl_HashEntry *namedHashPtr));
/*
* The following structure defines the implementation of the "font" Tcl
* object, used for drawing. The internalRep.twoPtrValue.ptr1 field of
* each font object points to the TkFont structure for the font, or
* NULL.
*/
Tcl_ObjType tkFontObjType = {
"font", /* name */
FreeFontObjProc, /* freeIntRepProc */
DupFontObjProc, /* dupIntRepProc */
NULL, /* updateStringProc */
SetFontFromAny /* setFromAnyProc */
};
/*
*---------------------------------------------------------------------------
*
* 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:
* Stores a token in the mainPtr to hold information needed by this
* package on a per application basis.
*
* Side effects:
* Memory allocated.
*
*---------------------------------------------------------------------------
*/
void
TkFontPkgInit(mainPtr)
TkMainInfo *mainPtr; /* The application being created. */
{
TkFontInfo *fiPtr;
fiPtr = (TkFontInfo *) ckalloc(sizeof(TkFontInfo));
Tcl_InitHashTable(&fiPtr->fontCache, TCL_STRING_KEYS);
Tcl_InitHashTable(&fiPtr->namedTable, TCL_STRING_KEYS);
fiPtr->mainPtr = mainPtr;
fiPtr->updatePending = 0;
mainPtr->fontInfoPtr = fiPtr;
TkpFontPkgInit(mainPtr);
}
/*
*---------------------------------------------------------------------------
*
* 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, *searchPtr;
Tcl_HashSearch search;
int fontsLeft;
fiPtr = mainPtr->fontInfoPtr;
fontsLeft = 0;
for (searchPtr = Tcl_FirstHashEntry(&fiPtr->fontCache, &search);
searchPtr != NULL;
searchPtr = Tcl_NextHashEntry(&search)) {
fontsLeft++;
fprintf(stderr, "Font %s still in cache.\n",
Tcl_GetHashKey(&fiPtr->fontCache, searchPtr));
}
#ifdef PURIFY
if (fontsLeft) {
panic("TkFontPkgFree: all fonts should have been freed already");
}
#endif
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 CONST char *optionStrings[] = {
"actual", "configure", "create", "delete",
"families", "measure", "metrics", "names",
"subfonts",
NULL
};
enum options {
FONT_ACTUAL, FONT_CONFIGURE, FONT_CREATE, FONT_DELETE,
FONT_FAMILIES, FONT_MEASURE, FONT_METRICS, FONT_NAMES,
FONT_SUBFONTS
};
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_SUBFONTS: {
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,
"font ?-displayof window? ?option?");
return TCL_ERROR;
}
tkfont = Tk_AllocFontFromObj(interp, tkwin, objv[2]);
if (tkfont == NULL) {
return TCL_ERROR;
}
TkpGetSubFonts(interp, tkfont);
Tk_FreeFont(tkfont);
return TCL_OK;
}
case FONT_ACTUAL: {
int skip, result;
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,
"font ?-displayof window? ?option?");
return TCL_ERROR;
}
tkfont = Tk_AllocFontFromObj(interp, tkwin, objv[2]);
if (tkfont == NULL) {
return TCL_ERROR;
}
objc -= skip;
objv += skip;
faPtr = GetFontAttributes(tkfont);
objPtr = NULL;
if (objc > 3) {
objPtr = objv[3];
}
result = GetAttributeInfoObj(interp, faPtr, objPtr);
Tk_FreeFont(tkfont);
return result;
}
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 = Tcl_GetString(objv[2]);
namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string);
nfPtr = NULL; /* lint. */
if (namedHashPtr != NULL) {
nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
}
if ((namedHashPtr == NULL) || (nfPtr->deletePending != 0)) {
Tcl_AppendResult(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);
UpdateDependentFonts(fiPtr, tkwin, namedHashPtr);
return result;
}
return GetAttributeInfoObj(interp, &nfPtr->fa, objPtr);
}
case FONT_CREATE: {
int skip, i;
char *name;
char buf[16 + TCL_INTEGER_SPACE];
TkFontAttributes fa;
Tcl_HashEntry *namedHashPtr;
skip = 3;
if (objc < 3) {
name = NULL;
} else {
name = Tcl_GetString(objv[2]);
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, 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 (CreateNamedFont(interp, tkwin, name, &fa) != TCL_OK) {
return TCL_ERROR;
}
Tcl_SetObjResult(interp, LangFontObj( interp, NULL, name));
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 = Tcl_GetString(objv[i]);
namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string);
if (namedHashPtr == NULL) {
Tcl_AppendResult(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) {
Tcl_WrongNumArgs(interp, 2, objv, "?-displayof window?");
return TCL_ERROR;
}
TkpGetFontFamilies(interp, tkwin);
break;
}
case FONT_MEASURE: {
char *string;
Tk_Font tkfont;
int length, skip;
Tcl_Obj *resultPtr;
skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin);
if (skip < 0) {
return TCL_ERROR;
}
if (objc - skip != 4) {
Tcl_WrongNumArgs(interp, 2, objv,
"font ?-displayof window? text");
return TCL_ERROR;
}
tkfont = Tk_AllocFontFromObj(interp, tkwin, objv[2]);
if (tkfont == NULL) {
return TCL_ERROR;
}
string = Tcl_GetStringFromObj(objv[3 + skip], &length);
resultPtr = Tcl_GetObjResult(interp);
Tcl_SetIntObj(resultPtr, Tk_TextWidth(tkfont, string, length));
Tk_FreeFont(tkfont);
break;
}
case FONT_METRICS: {
Tk_Font tkfont;
int skip, index, i;
CONST TkFontMetrics *fmPtr;
static CONST char *switches[] = {
"-ascent", "-descent", "-linespace", "-fixed", NULL
};
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,
"font ?-displayof window? ?option?");
return TCL_ERROR;
}
tkfont = Tk_AllocFontFromObj(interp, tkwin, objv[2]);
if (tkfont == NULL) {
return TCL_ERROR;
}
objc -= skip;
objv += skip;
fmPtr = GetFontMetrics(tkfont);
if (objc == 3) {
Tcl_AppendElement(interp, "-ascent");
Tcl_IntResults(interp, 1, 1, fmPtr->ascent);
Tcl_AppendElement(interp, "-descent");
Tcl_IntResults(interp, 1, 1, fmPtr->descent);
Tcl_AppendElement(interp, "-linespace");
Tcl_IntResults(interp, 1, 1, fmPtr->ascent + fmPtr->descent);
Tcl_AppendElement(interp, "-fixed");
Tcl_IntResults(interp, 1, 1, fmPtr->fixed);
} else {
if (Tcl_GetIndexFromObj(interp, objv[3], 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;
}
Tcl_SetIntObj(Tcl_GetObjResult(interp), i);
}
Tk_FreeFont(tkfont);
break;
}
case FONT_NAMES: {
char *string;
NamedFont *nfPtr;
Tcl_HashSearch search;
Tcl_HashEntry *namedHashPtr;
Tcl_Obj *strPtr, *resultPtr;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "names");
return TCL_ERROR;
}
resultPtr = Tcl_GetObjResult(interp);
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 = LangFontObj(interp, NULL, string);
Tcl_ListObjAppendElement(interp, resultPtr, strPtr);
}
namedHashPtr = Tcl_NextHashEntry(&search);
}
break;
}
}
return TCL_OK;
}
/*
*---------------------------------------------------------------------------
*
* UpdateDependentFonts, 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
UpdateDependentFonts(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) {
for (fontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr);
fontPtr != NULL; fontPtr = fontPtr->nextPtr) {
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. */
{
Tk_ClassWorldChangedProc *proc;
proc = Tk_GetClassProc(winPtr->classProcsPtr, worldChangedProc);
if (proc != NULL) {
(*proc)(winPtr->instanceData);
}
/*
* Notify all the descendants of this window that the world has changed.
*
* This could be done recursively or iteratively. The recursive version
* is easier to implement and understand, and typically, windows with a
* -font option will be leaf nodes in the widget heirarchy (buttons,
* labels, etc.), so the recursion depth will be shallow.
*
* However, the additional overhead of the recursive calls may become
* a performance problem if typical usage alters such that -font'ed widgets
* appear high in the heirarchy, causing deep recursion. This could happen
* with text widgets, or more likely with the (not yet existant) labeled
* frame widget. With these widgets it is possible, even likely, that a
* -font'ed widget (text or labeled frame) will not be a leaf node, but
* will instead have many descendants. If this is ever found to cause
* a performance problem, it may be worth investigating an iterative
* version of the code below.
*/
for (winPtr = winPtr->childList; winPtr != NULL; winPtr = winPtr->nextPtr) {
RecomputeWidgets(winPtr);
}
}
/*
*---------------------------------------------------------------------------
*
* CreateNamedFont --
*
* 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 the interp's 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.
*
*---------------------------------------------------------------------------
*/
static int
CreateNamedFont(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;
namedHashPtr = Tcl_CreateHashEntry(&fiPtr->namedTable, name, &new);
if (new == 0) {
nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
if (nfPtr->deletePending == 0) {
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "named 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;
UpdateDependentFonts(fiPtr, tkwin, namedHashPtr);
return TCL_OK;
}
nfPtr = (NamedFont *) ckalloc(sizeof(NamedFont));
nfPtr->fa = *faPtr;
nfPtr->refCount = 0;
nfPtr->deletePending = 0;
Tcl_SetHashValue(namedHashPtr, nfPtr);
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 the interp's result.
*
* 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() or Tk_FreeFontFromObj() so that the
* database is cleaned up when fonts aren't in use anymore.
*
*---------------------------------------------------------------------------
*/
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. */
{
Tk_Font tkfont;
Tcl_Obj *strPtr;
strPtr = Tcl_NewStringObj((char *) string, -1);
Tcl_IncrRefCount(strPtr);
tkfont = Tk_AllocFontFromObj(interp, tkwin, strPtr);
Tcl_DecrRefCount(strPtr);
return tkfont;
}
/*
*---------------------------------------------------------------------------
*
* Tk_AllocFontFromObj --
*
* 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() or Tk_FreeFontFromObj() so that the
* database is cleaned up when fonts aren't in use anymore.
*
*---------------------------------------------------------------------------
*/
Tk_Font
Tk_AllocFontFromObj(interp, tkwin, objPtr)
Tcl_Interp *interp; /* Interp for database and error return. */
Tk_Window tkwin; /* For screen on which font will be used. */
Tcl_Obj *objPtr; /* Object describing font, as: named font,
* native format, or parseable string. */
{
TkFontInfo *fiPtr;
Tcl_HashEntry *cacheHashPtr, *namedHashPtr;
TkFont *fontPtr, *firstFontPtr, *oldFontPtr;
int new, descent;
NamedFont *nfPtr;
fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
if (objPtr->typePtr != &tkFontObjType) {
SetFontFromAny(interp, objPtr);
}
oldFontPtr = (TkFont *) objPtr->internalRep.twoPtrValue.ptr1;
if (oldFontPtr != NULL) {
if (oldFontPtr->resourceRefCount == 0) {
/*
* This is a stale reference: it refers to a TkFont that's
* no longer in use. Clear the reference.
*/
FreeFontObjProc(objPtr);
oldFontPtr = NULL;
} else if (Tk_Screen(tkwin) == oldFontPtr->screen) {
oldFontPtr->resourceRefCount++;
return (Tk_Font) oldFontPtr;
}
}
/*
* Next, search the list of fonts that have the name we want, to see
* if one of them is for the right screen.
*/
new = 0;
if (oldFontPtr != NULL) {
cacheHashPtr = oldFontPtr->cacheHashPtr;
FreeFontObjProc(objPtr);
} else {
cacheHashPtr = Tcl_CreateHashEntry(&fiPtr->fontCache,
Tcl_GetString(objPtr), &new);
}
firstFontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr);
for (fontPtr = firstFontPtr; (fontPtr != NULL);
fontPtr = fontPtr->nextPtr) {
if (Tk_Screen(tkwin) == fontPtr->screen) {
fontPtr->resourceRefCount++;
fontPtr->objRefCount++;
objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) fontPtr;
return (Tk_Font) fontPtr;
}
}
/*
* The desired font isn't in the table. Make a new one.
*/
namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable,
Tcl_GetString(objPtr));
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, Tcl_GetString(objPtr));
if (fontPtr == NULL) {
TkFontAttributes fa;
Tcl_Obj *dupObjPtr = Tcl_DuplicateObj(objPtr);
if (ParseFontNameObj(interp, tkwin, dupObjPtr, &fa) != TCL_OK) {
if (new) {
Tcl_DeleteHashEntry(cacheHashPtr);
}
Tcl_DecrRefCount(dupObjPtr);
return NULL;
}
Tcl_DecrRefCount(dupObjPtr);
/*
* String contained the attributes inline.
*/
fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &fa);
}
}
fontPtr->resourceRefCount = 1;
fontPtr->objRefCount = 1;
fontPtr->cacheHashPtr = cacheHashPtr;
fontPtr->namedHashPtr = namedHashPtr;
fontPtr->screen = Tk_Screen(tkwin);
fontPtr->nextPtr = firstFontPtr;
Tcl_SetHashValue(cacheHashPtr, fontPtr);
Tk_MeasureChars((Tk_Font) fontPtr, "0", 1, -1, 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 = TkFontGetPixels(Tk_Screen(tkwin), fontPtr->fa.size) / 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;
}
}
objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) fontPtr;
return (Tk_Font) fontPtr;
}
/*
*----------------------------------------------------------------------
*
* Tk_GetFontFromObj --
*
* Find the font that corresponds to a given object. The font must
* have already been created by Tk_GetFont or Tk_AllocFontFromObj.
*
* Results:
* The return value is a token for the font that matches objPtr
* and is suitable for use in tkwin.
*
* Side effects:
* If the object is not already a font ref, the conversion will free
* any old internal representation.
*
*----------------------------------------------------------------------
*/
Tk_Font
Tk_GetFontFromObj(tkwin, objPtr)
Tk_Window tkwin; /* The window that the font will be used in. */
Tcl_Obj *objPtr; /* The object from which to get the font. */
{
TkFontInfo *fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
TkFont *fontPtr;
Tcl_HashEntry *hashPtr;
if (objPtr->typePtr != &tkFontObjType) {
SetFontFromAny((Tcl_Interp *) NULL, objPtr);
}
fontPtr = (TkFont *) objPtr->internalRep.twoPtrValue.ptr1;
if (fontPtr != NULL) {
if (fontPtr->resourceRefCount == 0) {
/*
* This is a stale reference: it refers to a TkFont that's
* no longer in use. Clear the reference.
*/
FreeFontObjProc(objPtr);
fontPtr = NULL;
} else if (Tk_Screen(tkwin) == fontPtr->screen) {
return (Tk_Font) fontPtr;
}
}
/*
* Next, search the list of fonts that have the name we want, to see
* if one of them is for the right screen.
*/
if (fontPtr != NULL) {
hashPtr = fontPtr->cacheHashPtr;
FreeFontObjProc(objPtr);
} else {
hashPtr = Tcl_FindHashEntry(&fiPtr->fontCache, Tcl_GetString(objPtr));
}
if (hashPtr != NULL) {
for (fontPtr = (TkFont *) Tcl_GetHashValue(hashPtr); fontPtr != NULL;
fontPtr = fontPtr->nextPtr) {
if (Tk_Screen(tkwin) == fontPtr->screen) {
fontPtr->objRefCount++;
objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) fontPtr;
return (Tk_Font) fontPtr;
}
}
}
panic("Tk_GetFontFromObj called with non-existent font!");
return NULL;
}
/*
*----------------------------------------------------------------------
*
* SetFontFromAny --
*
* Convert the internal representation of a Tcl object to the
* font internal form.
*
* Results:
* Always returns TCL_OK.
*
* Side effects:
* The object is left with its typePtr pointing to tkFontObjType.
* The TkFont pointer is NULL.
*
*----------------------------------------------------------------------
*/
static int
SetFontFromAny(interp, objPtr)
Tcl_Interp *interp; /* Used for error reporting if not NULL. */
Tcl_Obj *objPtr; /* The object to convert. */
{
Tcl_ObjType *typePtr;
/*
* Free the old internalRep before setting the new one.
*/
Tcl_GetString(objPtr);
typePtr = objPtr->typePtr;
if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
(*typePtr->freeIntRepProc)(objPtr);
}
TclObjSetType(objPtr,&tkFontObjType);
objPtr->internalRep.twoPtrValue.ptr1 = NULL;
return TCL_OK;
}
/*
*---------------------------------------------------------------------------
*
* 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.
*
*---------------------------------------------------------------------------
*/
CONST char *
Tk_NameOfFont(tkfont)
Tk_Font tkfont; /* Font whose name is desired. */
{
TkFont *fontPtr;
fontPtr = (TkFont *) tkfont;
return fontPtr->cacheHashPtr->key.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, *prevPtr;
NamedFont *nfPtr;
if (tkfont == NULL) {
return;
}
fontPtr = (TkFont *) tkfont;
fontPtr->resourceRefCount--;
if (fontPtr->resourceRefCount > 0) {
return;
}
if (fontPtr->namedHashPtr != NULL) {
/*
* This font derived from a named font. Reduce the reference
* count on the named font and free it if no-one else is
* using it.
*/
nfPtr = (NamedFont *) Tcl_GetHashValue(fontPtr->namedHashPtr);
nfPtr->refCount--;
if ((nfPtr->refCount == 0) && (nfPtr->deletePending != 0)) {
Tcl_DeleteHashEntry(fontPtr->namedHashPtr);
ckfree((char *) nfPtr);
}
}
prevPtr = (TkFont *) Tcl_GetHashValue(fontPtr->cacheHashPtr);
if (prevPtr == fontPtr) {
if (fontPtr->nextPtr == NULL) {
Tcl_DeleteHashEntry(fontPtr->cacheHashPtr);
} else {
Tcl_SetHashValue(fontPtr->cacheHashPtr, fontPtr->nextPtr);
}
} else {
while (prevPtr->nextPtr != fontPtr) {
prevPtr = prevPtr->nextPtr;
}
prevPtr->nextPtr = fontPtr->nextPtr;
}
TkpDeleteFont(fontPtr);
if (fontPtr->objRefCount == 0) {
ckfree((char *) fontPtr);
}
}
/*
*---------------------------------------------------------------------------
*
* Tk_FreeFontFromObj --
*
* Called to release a font inside a Tcl_Obj *. Decrements the refCount
* of the font and removes it from the hash tables if necessary.
*
* Results:
* None.
*
* Side effects:
* The reference count associated with font is decremented, and
* only deallocated when no one is using it.
*
*---------------------------------------------------------------------------
*/
void
Tk_FreeFontFromObj(tkwin, objPtr)
Tk_Window tkwin; /* The window this font lives in. Needed
* for the screen value. */
Tcl_Obj *objPtr; /* The Tcl_Obj * to be freed. */
{
Tk_FreeFont(Tk_GetFontFromObj(tkwin, objPtr));
}
/*
*---------------------------------------------------------------------------
*
* FreeFontObjProc --
*
* This proc is called to release an object reference to a font.
* Called when the object's internal rep is released or when
* the cached fontPtr needs to be changed.
*
* Results:
* None.
*
* Side effects:
* The object reference count is decremented. When both it
* and the hash ref count go to zero, the font's resources
* are released.
*
*---------------------------------------------------------------------------
*/
static void
FreeFontObjProc(objPtr)
Tcl_Obj *objPtr; /* The object we are releasing. */
{
TkFont *fontPtr = (TkFont *) objPtr->internalRep.twoPtrValue.ptr1;
if (fontPtr != NULL) {
fontPtr->objRefCount--;
if ((fontPtr->resourceRefCount == 0) && (fontPtr->objRefCount == 0)) {
ckfree((char *) fontPtr);
objPtr->internalRep.twoPtrValue.ptr1 = NULL;
}
}
}
/*
*---------------------------------------------------------------------------
*
* DupFontObjProc --
*
* When a cached font object is duplicated, this is called to
* update the internal reps.
*
* Results:
* None.
*
* Side effects:
* The font's objRefCount is incremented and the internal rep
* of the copy is set to point to it.
*
*---------------------------------------------------------------------------
*/
static void
DupFontObjProc(srcObjPtr, dupObjPtr)
Tcl_Obj *srcObjPtr; /* The object we are copying from. */
Tcl_Obj *dupObjPtr; /* The object we are copying to. */
{
TkFont *fontPtr = (TkFont *) srcObjPtr->internalRep.twoPtrValue.ptr1;
dupObjPtr->typePtr = srcObjPtr->typePtr;
dupObjPtr->internalRep.twoPtrValue.ptr1 = (VOID *) fontPtr;
if (fontPtr != NULL) {
fontPtr->objRefCount++;
}
}
/*
*---------------------------------------------------------------------------
*
* 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;
Tk_Uid 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 {
Tcl_UniChar ch;
/*
* 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'; ) {
while (isspace(UCHAR(*src))) { /* INTL: ISO space */
src++;
upper = 1;
}
src += Tcl_UtfToUniChar(src, &ch);
if (upper) {
ch = Tcl_UniCharToUpper(ch);
upper = 0;
} else {
ch = Tcl_UniCharToLower(ch);
}
dest += Tcl_UniCharToUtf(ch, dest);
}
*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 TkFontGetPoints(fontPtr->screen, fontPtr->fa.size);
}
/*
*---------------------------------------------------------------------------
*
* 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, numBytes)
Tk_Font tkfont; /* Font in which text will be measured. */
CONST char *string; /* String whose width will be computed. */
int numBytes; /* Number of bytes to consider from
* string, or < 0 for strlen(). */
{
int width;
if (numBytes < 0) {
numBytes = strlen(string);
}
Tk_MeasureChars(tkfont, string, numBytes, -1, 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, firstByte,
lastByte)
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 firstByte; /* Index of first byte of first character. */
int lastByte; /* Index of first byte after the last
* character. */
{
TkFont *fontPtr;
int startX, endX;
fontPtr = (TkFont *) tkfont;
Tk_MeasureChars(tkfont, string, firstByte, -1, 0, &startX);
Tk_MeasureChars(tkfont, string, lastByte, -1, 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, bytesThisChunk, maxChunks;
int baseline, height, curX, newX, maxWidth;
TextLayout *layoutPtr;
LayoutChunk *chunkPtr;
CONST TkFontMetrics *fmPtr;
Tcl_DString lineBuffer;
int *lineLengths;
int curLine, layoutHeight;
Tcl_DStringInit(&lineBuffer);
fontPtr = (TkFont *) tkfont;
if ((fontPtr == NULL) || (string == NULL)) {
if (widthPtr != NULL) {
*widthPtr = 0;
}
if (heightPtr != NULL) {
*heightPtr = 0;
}
return NULL;
}
fmPtr = &fontPtr->fm;
height = fmPtr->ascent + fmPtr->descent;
if (numChars < 0) {
numChars = Tcl_NumUtfChars(string, -1);
}
if (wrapLength == 0) {
wrapLength = -1;
}
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 = Tcl_UtfAtIndex(string, numChars);
special = string;
flags &= TK_IGNORE_TABS | TK_IGNORE_NEWLINES;
flags |= TK_WHOLE_WORDS | TK_AT_LEAST_ONE;
for (start = string; start < end; ) {
if (start >= special) {
/*
* Find the next special character in the string.
*
* INTL: Note that it is safe to increment by byte, because we are
* looking for 7-bit characters that will appear unchanged in
* UTF-8. At some point we may need to support the full Unicode
* whitespace set.
*/
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) {
bytesThisChunk = Tk_MeasureChars(tkfont, start, special - start,
wrapLength - curX, flags, &newX);
newX += curX;
flags &= ~TK_AT_LEAST_ONE;
if (bytesThisChunk > 0) {
chunkPtr = NewChunk(&layoutPtr, &maxChunks, start,
bytesThisChunk, curX, newX, baseline);
start += bytesThisChunk;
curX = newX;
}
}
if ((start == special) && (special < end)) {
/*
* Handle the special character.
*
* INTL: Special will be pointing at a 7-bit character so we
* can safely treat it as a single byte.
*/
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, curX,
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))) { /* INTL: ISO space */
if (!(flags & TK_IGNORE_NEWLINES)) {
if ((*start == '\n') || (*start == '\r')) {
break;
}
}
if (!(flags & TK_IGNORE_TABS)) {
if (*start == '\t') {
break;
}
}
start++;
}
if (chunkPtr != NULL) {
CONST char *end;
/*
* Append all the extra spaces on this line to the end of the
* last text chunk. This is a little tricky because we are
* switching back and forth between characters and bytes.
*/
end = chunkPtr->start + chunkPtr->numBytes;
bytesThisChunk = start - end;
if (bytesThisChunk > 0) {
bytesThisChunk = Tk_MeasureChars(tkfont, end, bytesThisChunk,
-1, 0, &chunkPtr->totalWidth);
chunkPtr->numBytes += bytesThisChunk;
chunkPtr->numChars += Tcl_NumUtfChars(end, bytesThisChunk);
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.
*/
Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
curX = 0;
baseline += height;
}
/*
* If last line ends with a newline, then we need to make a 0 width
* chunk on the next line. Otherwise "Hello" and "Hello\n" are the
* same height.
*/
if ((layoutPtr->numChunks > 0) && ((flags & TK_IGNORE_NEWLINES) == 0)) {
if (layoutPtr->chunks[layoutPtr->numChunks - 1].start[0] == '\n') {
chunkPtr = NewChunk(&layoutPtr, &maxChunks, start, 0, curX,
curX, baseline);
chunkPtr->numDisplayChars = -1;
Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
baseline += height;
}
}
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].numBytes = 0;
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;
} else {
/*
* Using maximum line length, shift all the chunks so that the lines
* are all justified correctly.
*/
curLine = 0;
chunkPtr = layoutPtr->chunks;
y = chunkPtr->y;
lineLengths = (int *) Tcl_DStringValue(&lineBuffer);
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++;
}
}
if (widthPtr != NULL) {
*widthPtr = layoutPtr->width;
}
if (heightPtr != NULL) {
*heightPtr = layoutHeight;
}
Tcl_DStringFree(&lineBuffer);
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;
CONST char *firstByte;
CONST char *lastByte;
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;
firstByte = chunkPtr->start;
} else {
firstByte = Tcl_UtfAtIndex(chunkPtr->start, firstChar);
Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start,
firstByte - chunkPtr->start, -1, 0, &drawX);
}
if (lastChar < numDisplayChars) {
numDisplayChars = lastChar;
}
lastByte = Tcl_UtfAtIndex(chunkPtr->start, numDisplayChars);
Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont,
firstByte, lastByte - firstByte,
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, numChars;
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;
numChars = 0;
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 numChars;
}
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 numChars;
}
n = Tk_MeasureChars((Tk_Font) fontPtr, chunkPtr->start,
chunkPtr->numBytes, x - chunkPtr->x,
0, &dummy);
return numChars + Tcl_NumUtfChars(chunkPtr->start, n);
}
numChars += chunkPtr->numChars;
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 = numChars;
if (i < layoutPtr->numChunks) {
pos--;
}
return pos;
}
numChars += chunkPtr->numChars;
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;
CONST char *end;
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) {
end = Tcl_UtfAtIndex(chunkPtr->start, index);
if (xPtr != NULL) {
Tk_MeasureChars(tkfont, chunkPtr->start,
end - chunkPtr->start, -1, 0, &x);
x += chunkPtr->x;
}
if (widthPtr != NULL) {
Tk_MeasureChars(tkfont, end, Tcl_UtfNext(end) - end,
-1, 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:
* The interp's 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+30];
LayoutChunk *chunkPtr;
int i, j, used, c, baseline;
Tcl_UniChar ch;
CONST char *p, *last_p,*glyphname;
TextLayout *layoutPtr;
char uindex[5]="\0\0\0\0";
char one_char[5];
int charsize;
int bytecount=0;
layoutPtr = (TextLayout *) layout;
chunkPtr = layoutPtr->chunks;
baseline = chunkPtr->y;
used = 0;
buf[used++] = '[';
buf[used++] = '(';
for (i = 0; i < layoutPtr->numChunks; i++) {
if (baseline != chunkPtr->y) {
buf[used++] = ')';
buf[used++] = ']';
buf[used++] = '\n';
buf[used++] = '[';
buf[used++] = '(';
baseline = chunkPtr->y;
}
if (chunkPtr->numDisplayChars <= 0) {
if (chunkPtr->start[0] == '\t') {
buf[used++] = '\\';
buf[used++] = 't';
}
} else {
p = chunkPtr->start;
for (j = 0; j < chunkPtr->numDisplayChars; j++) {
/*
* INTL: For now we just treat the characters as binary
* data and display the lower byte. Eventually this should
* be revised to handle international postscript fonts.
*/
last_p=p;
p +=(charsize= Tcl_UtfToUniChar(p,&ch));
Tcl_UtfToExternal(interp,NULL,last_p,charsize,0,NULL,one_char,4,
NULL,&bytecount,NULL);
if (bytecount == 1) {
c = UCHAR(one_char[0]);
/* c = UCHAR( ch & 0xFF) */;
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;
}
} else {
/* This character doesn't belong to system character set.
* So, we must use full glyph name */
sprintf(uindex,"%04X",ch); /* endianness? */
if ((glyphname = Tcl_GetVar2( interp , "::tk::psglyphs",uindex,0))) {
if (used > 0 && buf [used-1] == '(')
--used;
else
buf[used++] = ')';
buf[used++] = '/';
while( (*glyphname) && (used < (MAXUSE+27)))
buf[used++] = *glyphname++ ;
buf[used++] = '(';
}
else {
LangDebug("No PostScript glyph for U+%04x\n",ch);
}
}
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++] = ']';
buf[used++] = '\n';
buf[used] = '\0';
Tcl_AppendResult(interp, buf, (char *) NULL);
}
/*
*---------------------------------------------------------------------------
*
* 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 *optionPtr, *valuePtr;
char *value;
for (i = 0; i < objc; i += 2) {
optionPtr = objv[i];
valuePtr = objv[i + 1];
if (Tcl_GetIndexFromObj(interp, optionPtr, fontOpt, "option", 1,
&index) != TCL_OK) {
return TCL_ERROR;
}
if ((i+2 >= objc) && (objc & 1)) {
/*
* This test occurs after Tcl_GetIndexFromObj() so that
* "font create xyz -xyz" will return the error message
* that "-xyz" is a bad option, rather than that the value
* for "-xyz" is missing.
*/
Tcl_AppendResult(interp, "value for \"",
Tcl_GetString(optionPtr), "\" option missing",
(char *) NULL);
return TCL_ERROR;
}
switch (index) {
case FONT_FAMILY: {
value = Tcl_GetString(valuePtr);
faPtr->family = Tk_GetUid(value);
break;
}
case FONT_SIZE: {
if (Tcl_GetIntFromObj(interp, valuePtr, &n) != TCL_OK) {
return TCL_ERROR;
}
faPtr->size = n;
break;
}
case FONT_WEIGHT: {
n = TkFindStateNumObj(interp, optionPtr, weightMap, valuePtr);
if (n == TK_FW_UNKNOWN) {
return TCL_ERROR;
}
faPtr->weight = n;
break;
}
case FONT_SLANT: {
n = TkFindStateNumObj(interp, optionPtr, slantMap, valuePtr);
if (n == TK_FS_UNKNOWN) {
return TCL_ERROR;
}
faPtr->slant = n;
break;
}
case FONT_UNDERLINE: {
if (Tcl_GetBooleanFromObj(interp, valuePtr, &n) != TCL_OK) {
return TCL_ERROR;
}
faPtr->underline = n;
break;
}
case FONT_OVERSTRIKE: {
if (Tcl_GetBooleanFromObj(interp, valuePtr, &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:
* The return value is TCL_OK if the objPtr was non-NULL and
* specified a valid font attribute, TCL_ERROR otherwise. If TCL_OK
* is returned, the 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. If TCL_ERROR is returned, the interp's result is
* set to an error message describing that the objPtr did not refer
* to a valid option.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
static int
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;
CONST char *str;
Tcl_Obj *optionPtr, *valuePtr, *resultPtr;
resultPtr = Tcl_GetObjResult(interp);
start = 0;
end = FONT_NUMFIELDS;
if (objPtr != NULL) {
if (Tcl_GetIndexFromObj(interp, objPtr, fontOpt, "option", TCL_EXACT,
&index) != TCL_OK) {
return TCL_ERROR;
}
start = index;
end = index + 1;
}
valuePtr = NULL;
for (i = start; i < end; i++) {
switch (i) {
case FONT_FAMILY:
str = faPtr->family;
valuePtr = Tcl_NewStringObj(str, ((str == NULL) ? 0 : -1));
break;
case FONT_SIZE:
valuePtr = Tcl_NewIntObj(faPtr->size);
break;
case FONT_WEIGHT:
str = TkFindStateString(weightMap, faPtr->weight);
valuePtr = Tcl_NewStringObj(str, -1);
break;
case FONT_SLANT:
str = TkFindStateString(slantMap, faPtr->slant);
valuePtr = Tcl_NewStringObj(str, -1);
break;
case FONT_UNDERLINE:
valuePtr = Tcl_NewBooleanObj(faPtr->underline);
break;
case FONT_OVERSTRIKE:
valuePtr = Tcl_NewBooleanObj(faPtr->overstrike);
break;
}
if (objPtr != NULL) {
Tcl_SetObjResult(interp, valuePtr);
return TCL_OK;
}
optionPtr = Tcl_NewStringObj(fontOpt[i], -1);
Tcl_ListObjAppendElement(NULL, resultPtr, optionPtr);
Tcl_ListObjAppendElement(NULL, resultPtr, valuePtr);
}
return TCL_OK;
}
/*
*---------------------------------------------------------------------------
*
* 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] [style1 [style2 ...]"
* "-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. Must not be
* NULL. */
Tk_Window tkwin; /* For display on which font is used. */
Tcl_Obj *objPtr; /* Parseable font description object. */
TkFontAttributes *faPtr; /* Filled with attributes parsed from font
* name. Any attributes that were not
* specified in font name are filled with
* default values. */
{
char *dash;
int objc, result, i, n;
Tcl_Obj **objv;
char *string;
TkInitFontAttributes(faPtr);
string = Tcl_GetString(objPtr);
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])))) { /* INTL: ISO space */
goto xlfd;
}
if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
return TCL_ERROR;
}
return ConfigAttributesObj(interp, tkwin, objc, objv, faPtr);
}
if (*string == '*') {
/*
* This is appears to be an XLFD. Under Unix, all valid XLFDs were
* already handled by TkpGetNativeFont. If we are here, either we
* have something that initially looks like an XLFD but isn't or we
* have encountered an XLFD on Windows or Mac.
*/
xlfd:
result = TkFontParseXLFD(string, faPtr, NULL);
if (result == TCL_OK) {
return TCL_OK;
}
}
/*
* Wasn't an XLFD or "-option value" string. Try it as a
* "font size style" list.
*/
if ((Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv) != TCL_OK)
|| (objc < 1)) {
Tcl_AppendResult(interp, "font \"", string, "\" doesn't exist",
(char *) NULL);
return TCL_ERROR;
}
faPtr->family = Tk_GetUid(Tcl_GetString(objv[0]));
if (objc > 1) {
if (Tcl_GetIntFromObj(interp, objv[1], &n) != TCL_OK) {
return TCL_ERROR;
}
faPtr->size = 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++) {
n = TkFindStateNumObj(NULL, NULL, weightMap, objv[i]);
if (n != TK_FW_UNKNOWN) {
faPtr->weight = n;
continue;
}
n = TkFindStateNumObj(NULL, NULL, slantMap, objv[i]);
if (n != TK_FS_UNKNOWN) {
faPtr->slant = n;
continue;
}
n = TkFindStateNumObj(NULL, NULL, underlineMap, objv[i]);
if (n != 0) {
faPtr->underline = n;
continue;
}
n = TkFindStateNumObj(NULL, NULL, overstrikeMap, objv[i]);
if (n != 0) {
faPtr->overstrike = n;
continue;
}
/*
* Unknown style.
*/
Tcl_AppendResult(interp, "unknown font style \"",
Tcl_GetString(objv[i]), "\"", (char *) NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
*---------------------------------------------------------------------------
*
* 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, numBytes, curX, newX, y)
TextLayout **layoutPtrPtr;
int *maxPtr;
CONST char *start;
int numBytes;
int curX;
int newX;
int y;
{
TextLayout *layoutPtr;
LayoutChunk *chunkPtr;
int maxChunks, numChars;
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;
}
numChars = Tcl_NumUtfChars(start, numBytes);
chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks];
chunkPtr->start = start;
chunkPtr->numBytes = numBytes;
chunkPtr->numChars = numChars;
chunkPtr->numDisplayChars = numChars;
chunkPtr->x = curX;
chunkPtr->y = y;
chunkPtr->totalWidth = newX - curX;
chunkPtr->displayWidth = newX - curX;
layoutPtr->numChunks++;
return chunkPtr;
}
/*
*---------------------------------------------------------------------------
*
* TkFontParseXLFD --
*
* 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
TkFontParseXLFD(string, faPtr, xaPtr)
CONST char *string; /* Parseable font description string. */
TkFontAttributes *faPtr; /* Filled with attributes parsed from font
* name. Any attributes that were not
* specified in font name are filled with
* default values. */
TkXLFDAttributes *xaPtr; /* Filled with X-specific attributes parsed
* from font name. Any attributes that were
* not specified in font name are filled with
* default values. May be NULL if such
* information is not desired. */
{
char *src;
CONST char *str;
int i, j;
char *field[XLFD_NUMFIELDS + 2];
Tcl_DString ds;
TkXLFDAttributes xa;
if (xaPtr == NULL) {
xaPtr = &xa;
}
TkInitFontAttributes(faPtr);
TkInitXLFDAttributes(xaPtr);
memset(field, '\0', sizeof(field));
if (!(str = string)) {
return TCL_ERROR;
}
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 (!(*src & 0x80)
&& Tcl_UniCharIsUpper(UCHAR(*src))) {
*src = (char) Tcl_UniCharToLower(UCHAR(*src));
}
if (*src == '-') {
i++;
if (i == XLFD_NUMFIELDS) {
continue;
}
*src = '\0';
field[i] = src + 1;
if (i > XLFD_NUMFIELDS) {
break;
}
}
}
/*
* 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 right 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 (as a native font name), but gives a syntax
* error under Windows (as a parsed set of attributes)".
*/
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])) {
faPtr->family = Tk_GetUid(field[XLFD_FAMILY]);
}
if (FieldSpecified(field[XLFD_WEIGHT])) {
faPtr->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) {
faPtr->slant = TK_FS_ROMAN;
} else {
faPtr->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
* for historical compatibility.
*/
faPtr->size = 12;
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.
*/
faPtr->size = atoi(field[XLFD_POINT_SIZE] + 1);
} else if (Tcl_GetInt(NULL, field[XLFD_POINT_SIZE],
&faPtr->size) == TCL_OK) {
faPtr->size /= 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.
*/
faPtr->size = atoi(field[XLFD_PIXEL_SIZE] + 1);
} else if (Tcl_GetInt(NULL, field[XLFD_PIXEL_SIZE],
&faPtr->size) != TCL_OK) {
return TCL_ERROR;
}
}
faPtr->size = -faPtr->size;
/* XLFD_RESOLUTION_X ignored. */
/* XLFD_RESOLUTION_Y ignored. */
/* XLFD_SPACING ignored. */
/* XLFD_AVERAGE_WIDTH ignored. */
if (FieldSpecified(field[XLFD_CHARSET])) {
xaPtr->charset = Tk_GetUid(field[XLFD_CHARSET]);
} else {
xaPtr->charset = Tk_GetUid("iso8859-1");
}
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 != '?');
}
/*
*---------------------------------------------------------------------------
*
* TkFontGetPixels --
*
* Given a font size specification (as described in the TkFontAttributes
* structure) return the number of pixels it represents.
*
* Results:
* As above.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
int
TkFontGetPixels(screen, size)
Screen *screen; /* For point->pixel conversion factor. */
int size; /* Font size. */
{
double d;
if (size < 0) {
return -size;
}
d = size * 25.4 / 72.0;
d *= WidthOfScreen(screen);
d /= WidthMMOfScreen(screen);
return (int) (d + 0.5);
}
/*
*---------------------------------------------------------------------------
*
* TkFontGetPoints --
*
* Given a font size specification (as described in the TkFontAttributes
* structure) return the number of points it represents.
*
* Results:
* As above.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
int
TkFontGetPoints(screen, size)
Screen *screen; /* For pixel->point conversion factor. */
int size; /* Font size. */
{
double d;
if (size >= 0) {
return size;
}
d = -size * 72.0 / 25.4;
d *= WidthMMOfScreen(screen);
d /= WidthOfScreen(screen);
return (int) (d + 0.5);
}
/*
*-------------------------------------------------------------------------
*
* TkFontGetAliasList --
*
* Given a font name, find the list of all aliases for that font
* name. One of the names in this list will probably be the name
* that this platform expects when asking for the font.
*
* Results:
* As above. The return value is NULL if the font name has no
* aliases.
*
* Side effects:
* None.
*
*-------------------------------------------------------------------------
*/
char **
TkFontGetAliasList(faceName)
CONST char *faceName; /* Font name to test for aliases. */
{
int i, j;
for (i = 0; fontAliases[i] != NULL; i++) {
for (j = 0; fontAliases[i][j] != NULL; j++) {
if (strcasecmp(faceName, fontAliases[i][j]) == 0) {
return fontAliases[i];
}
}
}
return NULL;
}
/*
*-------------------------------------------------------------------------
*
* TkFontGetFallbacks --
*
* Get the list of font fallbacks that the platform-specific code
* can use to try to find the closest matching font the name
* requested.
*
* Results:
* As above.
*
* Side effects:
* None.
*
*-------------------------------------------------------------------------
*/
char ***
TkFontGetFallbacks()
{
return fontFallbacks;
}
/*
*-------------------------------------------------------------------------
*
* TkFontGetGlobalClass --
*
* Get the list of fonts to try if the requested font name does not
* exist and no fallbacks for that font name could be used either.
* The names in this list are considered preferred over all the other
* font names in the system when looking for a last-ditch fallback.
*
* Results:
* As above.
*
* Side effects:
* None.
*
*-------------------------------------------------------------------------
*/
char **
TkFontGetGlobalClass()
{
return globalFontClass;
}
/*
*-------------------------------------------------------------------------
*
* TkFontGetSymbolClass --
*
* Get the list of fonts that are symbolic; used if the operating
* system cannot apriori identify symbolic fonts on its own.
*
* Results:
* As above.
*
* Side effects:
* None.
*
*-------------------------------------------------------------------------
*/
char **
TkFontGetSymbolClass()
{
return symbolClass;
}
/*
*----------------------------------------------------------------------
*
* TkDebugFont --
*
* This procedure returns debugging information about a font.
*
* Results:
* The return value is a list with one sublist for each TkFont
* corresponding to "name". Each sublist has two elements that
* contain the resourceRefCount and objRefCount fields from the
* TkFont structure.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
Tcl_Obj *
TkDebugFont(tkwin, name)
Tk_Window tkwin; /* The window in which the font will be
* used (not currently used). */
char *name; /* Name of the desired color. */
{
TkFont *fontPtr;
Tcl_HashEntry *hashPtr;
Tcl_Obj *resultPtr, *objPtr;
resultPtr = Tcl_NewObj();
hashPtr = Tcl_FindHashEntry(
&((TkWindow *) tkwin)->mainPtr->fontInfoPtr->fontCache, name);
if (hashPtr != NULL) {
fontPtr = (TkFont *) Tcl_GetHashValue(hashPtr);
if (fontPtr == NULL) {
panic("TkDebugFont found empty hash table entry");
}
for ( ; (fontPtr != NULL); fontPtr = fontPtr->nextPtr) {
objPtr = Tcl_NewObj();
Tcl_ListObjAppendElement(NULL, objPtr,
Tcl_NewIntObj(fontPtr->resourceRefCount));
Tcl_ListObjAppendElement(NULL, objPtr,
Tcl_NewIntObj(fontPtr->objRefCount));
Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
}
}
return resultPtr;
}
/*
*----------------------------------------------------------------------
*
* TkFontGetFirstTextLayout --
*
* This procedure returns the first chunk of a Tk_TextLayout,
* i.e. until the first font change on the first line (or the
* whole first line if there is no such font change).
*
* Results:
* The return value is the byte length of the chunk, the chunk
* itself is copied into dst and its Tk_Font into font.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TkFontGetFirstTextLayout(
Tk_TextLayout layout, /* Layout information, from a previous call
* to Tk_ComputeTextLayout(). */
Tk_Font * font,
char * dst)
{
TextLayout *layoutPtr;
LayoutChunk *chunkPtr;
int numBytesInChunk;
layoutPtr = (TextLayout *)layout;
if ((layoutPtr==NULL)
|| (layoutPtr->numChunks==0)
|| (layoutPtr->chunks->numDisplayChars <= 0)) {
dst[0] = '\0';
return 0;
}
chunkPtr = layoutPtr->chunks;
numBytesInChunk = chunkPtr->numBytes;
strncpy(dst, chunkPtr->start, (size_t) numBytesInChunk);
*font = layoutPtr->tkfont;
return numBytesInChunk;
}