1/*
2 * tkFont.c --
3 *
4 *	This file maintains a database of fonts for the Tk toolkit.
5 *	It also provides several utility procedures for measuring and
6 *	displaying text.
7 *
8 * Copyright (c) 1990-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1998 Sun Microsystems, Inc.
10 *
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 *
14 * RCS: @(#) $Id: tkFont.c,v 1.21.2.2 2007/05/31 13:39:26 das Exp $
15 */
16
17#include "tkPort.h"
18#include "tkInt.h"
19#include "tkFont.h"
20
21/*
22 * The following structure is used to keep track of all the fonts that
23 * exist in the current application.  It must be stored in the
24 * TkMainInfo for the application.
25 */
26
27typedef struct TkFontInfo {
28    Tcl_HashTable fontCache;	/* Map a string to an existing Tk_Font.
29				 * Keys are string font names, values are
30				 * TkFont pointers. */
31    Tcl_HashTable namedTable;	/* Map a name to a set of attributes for a
32				 * font, used when constructing a Tk_Font from
33				 * a named font description.  Keys are
34				 * strings, values are NamedFont pointers. */
35    TkMainInfo *mainPtr;	/* Application that owns this structure. */
36    int updatePending;		/* Non-zero when a World Changed event has
37				 * already been queued to handle a change to
38				 * a named font. */
39} TkFontInfo;
40
41/*
42 * The following data structure is used to keep track of the font attributes
43 * for each named font that has been defined.  The named font is only deleted
44 * when the last reference to it goes away.
45 */
46
47typedef struct NamedFont {
48    int refCount;		/* Number of users of named font. */
49    int deletePending;		/* Non-zero if font should be deleted when
50				 * last reference goes away. */
51    TkFontAttributes fa;	/* Desired attributes for named font. */
52} NamedFont;
53
54/*
55 * The following two structures are used to keep track of string
56 * measurement information when using the text layout facilities.
57 *
58 * A LayoutChunk represents a contiguous range of text that can be measured
59 * and displayed by low-level text calls.  In general, chunks will be
60 * delimited by newlines and tabs.  Low-level, platform-specific things
61 * like kerning and non-integer character widths may occur between the
62 * characters in a single chunk, but not between characters in different
63 * chunks.
64 *
65 * A TextLayout is a collection of LayoutChunks.  It can be displayed with
66 * respect to any origin.  It is the implementation of the Tk_TextLayout
67 * opaque token.
68 */
69
70typedef struct LayoutChunk {
71    CONST char *start;		/* Pointer to simple string to be displayed.
72				 * This is a pointer into the TkTextLayout's
73				 * string. */
74    int numBytes;		/* The number of bytes in this chunk. */
75    int numChars;		/* The number of characters in this chunk. */
76    int numDisplayChars;	/* The number of characters to display when
77				 * this chunk is displayed.  Can be less than
78				 * numChars if extra space characters were
79				 * absorbed by the end of the chunk.  This
80				 * will be < 0 if this is a chunk that is
81				 * holding a tab or newline. */
82    int x, y;			/* The origin of the first character in this
83				 * chunk with respect to the upper-left hand
84				 * corner of the TextLayout. */
85    int totalWidth;		/* Width in pixels of this chunk.  Used
86				 * when hit testing the invisible spaces at
87				 * the end of a chunk. */
88    int displayWidth;		/* Width in pixels of the displayable
89				 * characters in this chunk.  Can be less than
90				 * width if extra space characters were
91				 * absorbed by the end of the chunk. */
92} LayoutChunk;
93
94typedef struct TextLayout {
95    Tk_Font tkfont;		/* The font used when laying out the text. */
96    CONST char *string;		/* The string that was layed out. */
97    int width;			/* The maximum width of all lines in the
98				 * text layout. */
99    int numChunks;		/* Number of chunks actually used in
100				 * following array. */
101    LayoutChunk chunks[1];	/* Array of chunks.  The actual size will
102				 * be maxChunks.  THIS FIELD MUST BE THE LAST
103				 * IN THE STRUCTURE. */
104} TextLayout;
105
106/*
107 * The following structures are used as two-way maps between the values for
108 * the fields in the TkFontAttributes structure and the strings used in
109 * Tcl, when parsing both option-value format and style-list format font
110 * name strings.
111 */
112
113static TkStateMap weightMap[] = {
114    {TK_FW_NORMAL,	"normal"},
115    {TK_FW_BOLD,	"bold"},
116    {TK_FW_UNKNOWN,	NULL}
117};
118
119static TkStateMap slantMap[] = {
120    {TK_FS_ROMAN,	"roman"},
121    {TK_FS_ITALIC,	"italic"},
122    {TK_FS_UNKNOWN,	NULL}
123};
124
125static TkStateMap underlineMap[] = {
126    {1,			"underline"},
127    {0,			NULL}
128};
129
130static TkStateMap overstrikeMap[] = {
131    {1,			"overstrike"},
132    {0,			NULL}
133};
134
135/*
136 * The following structures are used when parsing XLFD's into a set of
137 * TkFontAttributes.
138 */
139
140static TkStateMap xlfdWeightMap[] = {
141    {TK_FW_NORMAL,	"normal"},
142    {TK_FW_NORMAL,	"medium"},
143    {TK_FW_NORMAL,	"book"},
144    {TK_FW_NORMAL,	"light"},
145    {TK_FW_BOLD,	"bold"},
146    {TK_FW_BOLD,	"demi"},
147    {TK_FW_BOLD,	"demibold"},
148    {TK_FW_NORMAL,	NULL}		/* Assume anything else is "normal". */
149};
150
151static TkStateMap xlfdSlantMap[] = {
152    {TK_FS_ROMAN,	"r"},
153    {TK_FS_ITALIC,	"i"},
154    {TK_FS_OBLIQUE,	"o"},
155    {TK_FS_ROMAN,	NULL}		/* Assume anything else is "roman". */
156};
157
158static TkStateMap xlfdSetwidthMap[] = {
159    {TK_SW_NORMAL,	"normal"},
160    {TK_SW_CONDENSE,	"narrow"},
161    {TK_SW_CONDENSE,	"semicondensed"},
162    {TK_SW_CONDENSE,	"condensed"},
163    {TK_SW_UNKNOWN,	NULL}
164};
165
166/*
167 * The following structure and defines specify the valid builtin options
168 * when configuring a set of font attributes.
169 */
170
171static CONST char *fontOpt[] = {
172    "-family",
173    "-size",
174    "-weight",
175    "-slant",
176    "-underline",
177    "-overstrike",
178    NULL
179};
180
181#define FONT_FAMILY	0
182#define FONT_SIZE	1
183#define FONT_WEIGHT	2
184#define FONT_SLANT	3
185#define FONT_UNDERLINE	4
186#define FONT_OVERSTRIKE	5
187#define FONT_NUMFIELDS	6
188
189/*
190 * Hardcoded font aliases.  These are used to describe (mostly) identical
191 * fonts whose names differ from platform to platform.  If the
192 * user-supplied font name matches any of the names in one of the alias
193 * lists, the other names in the alias list are also automatically tried.
194 */
195
196static char *timesAliases[] = {
197    "Times",			/* Unix. */
198    "Times New Roman",		/* Windows. */
199    "New York",			/* Mac. */
200    NULL
201};
202
203static char *helveticaAliases[] = {
204    "Helvetica",		/* Unix. */
205    "Arial",			/* Windows. */
206    "Geneva",			/* Mac. */
207    NULL
208};
209
210static char *courierAliases[] = {
211    "Courier",			/* Unix and Mac. */
212    "Courier New",		/* Windows. */
213    NULL
214};
215
216static char *minchoAliases[] = {
217    "mincho",			/* Unix. */
218    "\357\274\255\357\274\263 \346\230\216\346\234\235",
219				/* Windows (MS mincho). */
220    "\346\234\254\346\230\216\346\234\235\342\210\222\357\274\255",
221				/* Mac (honmincho-M). */
222    NULL
223};
224
225static char *gothicAliases[] = {
226    "gothic",			/* Unix. */
227    "\357\274\255\357\274\263 \343\202\264\343\202\267\343\203\203\343\202\257",
228				/* Windows (MS goshikku). */
229    "\344\270\270\343\202\264\343\202\267\343\203\203\343\202\257\342\210\222\357\274\255",
230				/* Mac (goshikku-M). */
231    NULL
232};
233
234static char *dingbatsAliases[] = {
235    "dingbats", "zapfdingbats", "itc zapfdingbats",
236				/* Unix. */
237				/* Windows. */
238    "zapf dingbats",		/* Mac. */
239    NULL
240};
241
242static char **fontAliases[] = {
243    timesAliases,
244    helveticaAliases,
245    courierAliases,
246    minchoAliases,
247    gothicAliases,
248    dingbatsAliases,
249    NULL
250};
251
252/*
253 * Hardcoded font classes.  If the character cannot be found in the base
254 * font, the classes are examined in order to see if some other similar
255 * font should be examined also.
256 */
257
258static char *systemClass[] = {
259    "fixed",				/* Unix. */
260					/* Windows. */
261    "chicago", "osaka", "sistemny",	/* Mac. */
262    NULL
263};
264
265static char *serifClass[] = {
266    "times", "palatino", "mincho",	/* All platforms. */
267    "song ti",				/* Unix. */
268    "ms serif", "simplified arabic", 	/* Windows. */
269    "latinski",				/* Mac. */
270    NULL
271};
272
273static char *sansClass[] = {
274    "helvetica", "gothic",		/* All platforms. */
275					/* Unix. */
276    "ms sans serif", "traditional arabic",
277					/* Windows. */
278    "bastion",				/* Mac. */
279    NULL
280};
281
282static char *monoClass[] = {
283    "courier", "gothic",		/* All platforms. */
284    "fangsong ti",			/* Unix. */
285    "simplified arabic fixed",		/* Windows. */
286    "monaco", "pryamoy",		/* Mac. */
287    NULL
288};
289
290static char *symbolClass[] = {
291    "symbol", "dingbats", "wingdings", NULL
292};
293
294static char **fontFallbacks[] = {
295    systemClass,
296    serifClass,
297    sansClass,
298    monoClass,
299    symbolClass,
300    NULL
301};
302
303/*
304 * Global fallbacks.  If the character could not be found in the preferred
305 * fallback list, this list is examined.  If the character still cannot be
306 * found, all font families in the system are examined.
307 */
308
309static char *globalFontClass[] = {
310    "symbol",			/* All platforms. */
311				/* Unix. */
312    "lucida sans unicode",	/* Windows. */
313    "bitstream cyberbit",	/* Windows popular CJK font */
314    "chicago",			/* Mac. */
315    NULL
316};
317
318#define GetFontAttributes(tkfont) \
319		((CONST TkFontAttributes *) &((TkFont *) (tkfont))->fa)
320
321#define GetFontMetrics(tkfont)    \
322		((CONST TkFontMetrics *) &((TkFont *) (tkfont))->fm)
323
324
325static int		ConfigAttributesObj _ANSI_ARGS_((Tcl_Interp *interp,
326			    Tk_Window tkwin, int objc, Tcl_Obj *CONST objv[],
327			    TkFontAttributes *faPtr));
328static int		CreateNamedFont _ANSI_ARGS_((Tcl_Interp *interp,
329			    Tk_Window tkwin, CONST char *name,
330			    TkFontAttributes *faPtr));
331static void		DupFontObjProc _ANSI_ARGS_((Tcl_Obj *srcObjPtr,
332			    Tcl_Obj *dupObjPtr));
333static int		FieldSpecified _ANSI_ARGS_((CONST char *field));
334static void		FreeFontObjProc _ANSI_ARGS_((Tcl_Obj *objPtr));
335static int		GetAttributeInfoObj _ANSI_ARGS_((Tcl_Interp *interp,
336			    CONST TkFontAttributes *faPtr, Tcl_Obj *objPtr));
337static LayoutChunk *	NewChunk _ANSI_ARGS_((TextLayout **layoutPtrPtr,
338			    int *maxPtr, CONST char *start, int numChars,
339			    int curX, int newX, int y));
340static int		ParseFontNameObj _ANSI_ARGS_((Tcl_Interp *interp,
341			    Tk_Window tkwin, Tcl_Obj *objPtr,
342			    TkFontAttributes *faPtr));
343static void		RecomputeWidgets _ANSI_ARGS_((TkWindow *winPtr));
344static int		SetFontFromAny _ANSI_ARGS_((Tcl_Interp *interp,
345			    Tcl_Obj *objPtr));
346static void		TheWorldHasChanged _ANSI_ARGS_((
347			    ClientData clientData));
348static void		UpdateDependentFonts _ANSI_ARGS_((TkFontInfo *fiPtr,
349			    Tk_Window tkwin, Tcl_HashEntry *namedHashPtr));
350
351/*
352 * The following structure defines the implementation of the "font" Tcl
353 * object, used for drawing. The internalRep.twoPtrValue.ptr1 field of
354 * each font object points to the TkFont structure for the font, or
355 * NULL.
356 */
357
358Tcl_ObjType tkFontObjType = {
359    "font",			/* name */
360    FreeFontObjProc,		/* freeIntRepProc */
361    DupFontObjProc,		/* dupIntRepProc */
362    NULL,			/* updateStringProc */
363    SetFontFromAny		/* setFromAnyProc */
364};
365
366
367/*
368 *---------------------------------------------------------------------------
369 *
370 * TkFontPkgInit --
371 *
372 *	This procedure is called when an application is created.  It
373 *	initializes all the structures that are used by the font
374 *	package on a per application basis.
375 *
376 * Results:
377 *	Stores a token in the mainPtr to hold information needed by this
378 *	package on a per application basis.
379 *
380 * Side effects:
381 *	Memory allocated.
382 *
383 *---------------------------------------------------------------------------
384 */
385void
386TkFontPkgInit(mainPtr)
387    TkMainInfo *mainPtr;	/* The application being created. */
388{
389    TkFontInfo *fiPtr;
390
391    fiPtr = (TkFontInfo *) ckalloc(sizeof(TkFontInfo));
392    Tcl_InitHashTable(&fiPtr->fontCache, TCL_STRING_KEYS);
393    Tcl_InitHashTable(&fiPtr->namedTable, TCL_STRING_KEYS);
394    fiPtr->mainPtr = mainPtr;
395    fiPtr->updatePending = 0;
396    mainPtr->fontInfoPtr = fiPtr;
397
398    TkpFontPkgInit(mainPtr);
399}
400
401/*
402 *---------------------------------------------------------------------------
403 *
404 * TkFontPkgFree --
405 *
406 *	This procedure is called when an application is deleted.  It
407 *	deletes all the structures that were used by the font package
408 *	for this application.
409 *
410 * Results:
411 *	None.
412 *
413 * Side effects:
414 *	Memory freed.
415 *
416 *---------------------------------------------------------------------------
417 */
418
419void
420TkFontPkgFree(mainPtr)
421    TkMainInfo *mainPtr;	/* The application being deleted. */
422{
423    TkFontInfo *fiPtr;
424    Tcl_HashEntry *hPtr, *searchPtr;
425    Tcl_HashSearch search;
426    int fontsLeft;
427
428    fiPtr = mainPtr->fontInfoPtr;
429
430    fontsLeft = 0;
431    for (searchPtr = Tcl_FirstHashEntry(&fiPtr->fontCache, &search);
432	    searchPtr != NULL;
433	    searchPtr = Tcl_NextHashEntry(&search)) {
434	fontsLeft++;
435#ifdef DEBUG_FONTS
436	fprintf(stderr, "Font %s still in cache.\n",
437		Tcl_GetHashKey(&fiPtr->fontCache, searchPtr));
438#endif
439    }
440#ifdef PURIFY
441    if (fontsLeft) {
442	panic("TkFontPkgFree: all fonts should have been freed already");
443    }
444#endif
445    Tcl_DeleteHashTable(&fiPtr->fontCache);
446
447    hPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search);
448    while (hPtr != NULL) {
449	ckfree((char *) Tcl_GetHashValue(hPtr));
450	hPtr = Tcl_NextHashEntry(&search);
451    }
452    Tcl_DeleteHashTable(&fiPtr->namedTable);
453    if (fiPtr->updatePending != 0) {
454	Tcl_CancelIdleCall(TheWorldHasChanged, (ClientData) fiPtr);
455    }
456    ckfree((char *) fiPtr);
457}
458
459/*
460 *---------------------------------------------------------------------------
461 *
462 * Tk_FontObjCmd --
463 *
464 *	This procedure is implemented to process the "font" Tcl command.
465 *	See the user documentation for details on what it does.
466 *
467 * Results:
468 *	A standard Tcl result.
469 *
470 * Side effects:
471 *	See the user documentation.
472 *
473 *----------------------------------------------------------------------
474 */
475
476int
477Tk_FontObjCmd(clientData, interp, objc, objv)
478    ClientData clientData;	/* Main window associated with interpreter. */
479    Tcl_Interp *interp;		/* Current interpreter. */
480    int objc;			/* Number of arguments. */
481    Tcl_Obj *CONST objv[];	/* Argument objects. */
482{
483    int index;
484    Tk_Window tkwin;
485    TkFontInfo *fiPtr;
486    static CONST char *optionStrings[] = {
487	"actual",	"configure",	"create",	"delete",
488	"families",	"measure",	"metrics",	"names",
489	NULL
490    };
491    enum options {
492	FONT_ACTUAL,	FONT_CONFIGURE,	FONT_CREATE,	FONT_DELETE,
493	FONT_FAMILIES,	FONT_MEASURE,	FONT_METRICS,	FONT_NAMES
494    };
495
496    tkwin = (Tk_Window) clientData;
497    fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
498
499    if (objc < 2) {
500	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?");
501	return TCL_ERROR;
502    }
503    if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
504	    &index) != TCL_OK) {
505	return TCL_ERROR;
506    }
507
508    switch ((enum options) index) {
509	case FONT_ACTUAL: {
510	    int skip, result;
511	    Tk_Font tkfont;
512	    Tcl_Obj *objPtr;
513	    CONST TkFontAttributes *faPtr;
514
515	    skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin);
516	    if (skip < 0) {
517		return TCL_ERROR;
518	    }
519	    if ((objc < 3) || (objc - skip > 4)) {
520		Tcl_WrongNumArgs(interp, 2, objv,
521			"font ?-displayof window? ?option?");
522		return TCL_ERROR;
523	    }
524	    tkfont = Tk_AllocFontFromObj(interp, tkwin, objv[2]);
525	    if (tkfont == NULL) {
526		return TCL_ERROR;
527	    }
528	    objc -= skip;
529	    objv += skip;
530	    faPtr = GetFontAttributes(tkfont);
531	    objPtr = NULL;
532	    if (objc > 3) {
533		objPtr = objv[3];
534	    }
535	    result = GetAttributeInfoObj(interp, faPtr, objPtr);
536	    Tk_FreeFont(tkfont);
537	    return result;
538	}
539	case FONT_CONFIGURE: {
540	    int result;
541	    char *string;
542	    Tcl_Obj *objPtr;
543	    NamedFont *nfPtr;
544	    Tcl_HashEntry *namedHashPtr;
545
546	    if (objc < 3) {
547		Tcl_WrongNumArgs(interp, 2, objv, "fontname ?options?");
548		return TCL_ERROR;
549	    }
550	    string = Tcl_GetString(objv[2]);
551	    namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string);
552	    nfPtr = NULL;		/* lint. */
553	    if (namedHashPtr != NULL) {
554		nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
555	    }
556	    if ((namedHashPtr == NULL) || (nfPtr->deletePending != 0)) {
557		Tcl_AppendResult(interp, "named font \"", string,
558			"\" doesn't exist", NULL);
559		return TCL_ERROR;
560	    }
561	    if (objc == 3) {
562		objPtr = NULL;
563	    } else if (objc == 4) {
564		objPtr = objv[3];
565	    } else {
566		result = ConfigAttributesObj(interp, tkwin, objc - 3,
567			objv + 3, &nfPtr->fa);
568		UpdateDependentFonts(fiPtr, tkwin, namedHashPtr);
569		return result;
570	    }
571	    return GetAttributeInfoObj(interp, &nfPtr->fa, objPtr);
572	}
573	case FONT_CREATE: {
574	    int skip, i;
575	    char *name;
576	    char buf[16 + TCL_INTEGER_SPACE];
577	    TkFontAttributes fa;
578	    Tcl_HashEntry *namedHashPtr;
579
580	    skip = 3;
581	    if (objc < 3) {
582		name = NULL;
583	    } else {
584		name = Tcl_GetString(objv[2]);
585		if (name[0] == '-') {
586		    name = NULL;
587		}
588	    }
589	    if (name == NULL) {
590		/*
591		 * No font name specified.  Generate one of the form "fontX".
592		 */
593
594		for (i = 1; ; i++) {
595		    sprintf(buf, "font%d", i);
596		    namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, buf);
597		    if (namedHashPtr == NULL) {
598			break;
599		    }
600		}
601		name = buf;
602		skip = 2;
603	    }
604	    TkInitFontAttributes(&fa);
605	    if (ConfigAttributesObj(interp, tkwin, objc - skip, objv + skip,
606		    &fa) != TCL_OK) {
607		return TCL_ERROR;
608	    }
609	    if (CreateNamedFont(interp, tkwin, name, &fa) != TCL_OK) {
610		return TCL_ERROR;
611	    }
612	    Tcl_AppendResult(interp, name, NULL);
613	    break;
614	}
615	case FONT_DELETE: {
616	    int i;
617	    char *string;
618	    NamedFont *nfPtr;
619	    Tcl_HashEntry *namedHashPtr;
620
621	    /*
622	     * Delete the named font.  If there are still widgets using this
623	     * font, then it isn't deleted right away.
624	     */
625
626	    if (objc < 3) {
627		Tcl_WrongNumArgs(interp, 2, objv, "fontname ?fontname ...?");
628		return TCL_ERROR;
629	    }
630	    for (i = 2; i < objc; i++) {
631		string = Tcl_GetString(objv[i]);
632		namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string);
633		if (namedHashPtr == NULL) {
634		    Tcl_AppendResult(interp, "named font \"", string,
635			    "\" doesn't exist", (char *) NULL);
636		    return TCL_ERROR;
637		}
638		nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
639		if (nfPtr->refCount != 0) {
640		    nfPtr->deletePending = 1;
641		} else {
642		    Tcl_DeleteHashEntry(namedHashPtr);
643		    ckfree((char *) nfPtr);
644		}
645	    }
646	    break;
647	}
648	case FONT_FAMILIES: {
649	    int skip;
650
651	    skip = TkGetDisplayOf(interp, objc - 2, objv + 2, &tkwin);
652	    if (skip < 0) {
653		return TCL_ERROR;
654	    }
655	    if (objc - skip != 2) {
656		Tcl_WrongNumArgs(interp, 2, objv, "?-displayof window?");
657		return TCL_ERROR;
658	    }
659	    TkpGetFontFamilies(interp, tkwin);
660	    break;
661	}
662	case FONT_MEASURE: {
663	    char *string;
664	    Tk_Font tkfont;
665	    int length, skip;
666	    Tcl_Obj *resultPtr;
667
668	    skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin);
669	    if (skip < 0) {
670		return TCL_ERROR;
671	    }
672	    if (objc - skip != 4) {
673		Tcl_WrongNumArgs(interp, 2, objv,
674			"font ?-displayof window? text");
675		return TCL_ERROR;
676	    }
677	    tkfont = Tk_AllocFontFromObj(interp, tkwin, objv[2]);
678	    if (tkfont == NULL) {
679		return TCL_ERROR;
680	    }
681	    string = Tcl_GetStringFromObj(objv[3 + skip], &length);
682	    resultPtr = Tcl_GetObjResult(interp);
683	    Tcl_SetIntObj(resultPtr, Tk_TextWidth(tkfont, string, length));
684	    Tk_FreeFont(tkfont);
685	    break;
686	}
687	case FONT_METRICS: {
688	    Tk_Font tkfont;
689	    int skip, index, i;
690	    CONST TkFontMetrics *fmPtr;
691	    static CONST char *switches[] = {
692		"-ascent", "-descent", "-linespace", "-fixed", NULL
693	    };
694
695	    skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin);
696	    if (skip < 0) {
697		return TCL_ERROR;
698	    }
699	    if ((objc < 3) || ((objc - skip) > 4)) {
700		Tcl_WrongNumArgs(interp, 2, objv,
701			"font ?-displayof window? ?option?");
702		return TCL_ERROR;
703	    }
704	    tkfont = Tk_AllocFontFromObj(interp, tkwin, objv[2]);
705	    if (tkfont == NULL) {
706		return TCL_ERROR;
707	    }
708	    objc -= skip;
709	    objv += skip;
710	    fmPtr = GetFontMetrics(tkfont);
711	    if (objc == 3) {
712		char buf[64 + TCL_INTEGER_SPACE * 4];
713
714		sprintf(buf, "-ascent %d -descent %d -linespace %d -fixed %d",
715			fmPtr->ascent, fmPtr->descent,
716			fmPtr->ascent + fmPtr->descent,
717			fmPtr->fixed);
718		Tcl_AppendResult(interp, buf, NULL);
719	    } else {
720		if (Tcl_GetIndexFromObj(interp, objv[3], switches,
721			"metric", 0, &index) != TCL_OK) {
722		    Tk_FreeFont(tkfont);
723		    return TCL_ERROR;
724		}
725		i = 0;			/* Needed only to prevent compiler
726					 * warning. */
727		switch (index) {
728		    case 0: i = fmPtr->ascent;			break;
729		    case 1: i = fmPtr->descent;			break;
730		    case 2: i = fmPtr->ascent + fmPtr->descent;	break;
731		    case 3: i = fmPtr->fixed;			break;
732		}
733		Tcl_SetIntObj(Tcl_GetObjResult(interp), i);
734	    }
735	    Tk_FreeFont(tkfont);
736	    break;
737	}
738	case FONT_NAMES: {
739	    char *string;
740	    NamedFont *nfPtr;
741	    Tcl_HashSearch search;
742	    Tcl_HashEntry *namedHashPtr;
743	    Tcl_Obj *strPtr, *resultPtr;
744
745	    if (objc != 2) {
746		Tcl_WrongNumArgs(interp, 1, objv, "names");
747		return TCL_ERROR;
748	    }
749	    resultPtr = Tcl_GetObjResult(interp);
750	    namedHashPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search);
751	    while (namedHashPtr != NULL) {
752		nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
753		if (nfPtr->deletePending == 0) {
754		    string = Tcl_GetHashKey(&fiPtr->namedTable, namedHashPtr);
755		    strPtr = Tcl_NewStringObj(string, -1);
756		    Tcl_ListObjAppendElement(NULL, resultPtr, strPtr);
757		}
758		namedHashPtr = Tcl_NextHashEntry(&search);
759	    }
760	    break;
761	}
762    }
763    return TCL_OK;
764}
765
766/*
767 *---------------------------------------------------------------------------
768 *
769 * UpdateDependentFonts, TheWorldHasChanged, RecomputeWidgets --
770 *
771 *	Called when the attributes of a named font changes.  Updates all
772 *	the instantiated fonts that depend on that named font and then
773 *	uses the brute force approach and prepares every widget to
774 *	recompute its geometry.
775 *
776 * Results:
777 *	None.
778 *
779 * Side effects:
780 *	Things get queued for redisplay.
781 *
782 *---------------------------------------------------------------------------
783 */
784
785static void
786UpdateDependentFonts(fiPtr, tkwin, namedHashPtr)
787    TkFontInfo *fiPtr;		/* Info about application's fonts. */
788    Tk_Window tkwin;		/* A window in the application. */
789    Tcl_HashEntry *namedHashPtr;/* The named font that is changing. */
790{
791    Tcl_HashEntry *cacheHashPtr;
792    Tcl_HashSearch search;
793    TkFont *fontPtr;
794    NamedFont *nfPtr;
795
796    nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
797    if (nfPtr->refCount == 0) {
798	/*
799	 * Well nobody's using this named font, so don't have to tell
800	 * any widgets to recompute themselves.
801	 */
802
803	return;
804    }
805
806    cacheHashPtr = Tcl_FirstHashEntry(&fiPtr->fontCache, &search);
807    while (cacheHashPtr != NULL) {
808	for (fontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr);
809		fontPtr != NULL; fontPtr = fontPtr->nextPtr) {
810	    if (fontPtr->namedHashPtr == namedHashPtr) {
811		TkpGetFontFromAttributes(fontPtr, tkwin, &nfPtr->fa);
812		if (fiPtr->updatePending == 0) {
813		    fiPtr->updatePending = 1;
814		    Tcl_DoWhenIdle(TheWorldHasChanged, (ClientData) fiPtr);
815		}
816	    }
817	}
818	cacheHashPtr = Tcl_NextHashEntry(&search);
819    }
820}
821
822static void
823TheWorldHasChanged(clientData)
824    ClientData clientData;	/* Info about application's fonts. */
825{
826    TkFontInfo *fiPtr;
827
828    fiPtr = (TkFontInfo *) clientData;
829    fiPtr->updatePending = 0;
830
831    RecomputeWidgets(fiPtr->mainPtr->winPtr);
832}
833
834static void
835RecomputeWidgets(winPtr)
836    TkWindow *winPtr;		/* Window to which command is sent. */
837{
838    Tk_ClassWorldChangedProc *proc;
839    proc = Tk_GetClassProc(winPtr->classProcsPtr, worldChangedProc);
840    if (proc != NULL) {
841	(*proc)(winPtr->instanceData);
842    }
843
844    /*
845     * Notify all the descendants of this window that the world has changed.
846     *
847     * This could be done recursively or iteratively.  The recursive version
848     * is easier to implement and understand, and typically, windows with a
849     * -font option will be leaf nodes in the widget heirarchy (buttons,
850     * labels, etc.), so the recursion depth will be shallow.
851     *
852     * However, the additional overhead of the recursive calls may become
853     * a performance problem if typical usage alters such that -font'ed widgets
854     * appear high in the heirarchy, causing deep recursion.  This could happen
855     * with text widgets, or more likely with the (not yet existant) labeled
856     * frame widget.  With these widgets it is possible, even likely, that a
857     * -font'ed widget (text or labeled frame) will not be a leaf node, but
858     * will instead have many descendants.  If this is ever found to cause
859     * a performance problem, it may be worth investigating an iterative
860     * version of the code below.
861     */
862    for (winPtr = winPtr->childList; winPtr != NULL; winPtr = winPtr->nextPtr) {
863	RecomputeWidgets(winPtr);
864    }
865}
866
867/*
868 *---------------------------------------------------------------------------
869 *
870 * CreateNamedFont --
871 *
872 *	Create the specified named font with the given attributes in the
873 *	named font table associated with the interp.
874 *
875 * Results:
876 *	Returns TCL_OK if the font was successfully created, or TCL_ERROR
877 *	if the named font already existed.  If TCL_ERROR is returned, an
878 *	error message is left in the interp's result.
879 *
880 * Side effects:
881 *	Assume there used to exist a named font by the specified name, and
882 *	that the named font had been deleted, but there were still some
883 *	widgets using the named font at the time it was deleted.  If a
884 *	new named font is created with the same name, all those widgets
885 *	that were using the old named font will be redisplayed using
886 *	the new named font's attributes.
887 *
888 *---------------------------------------------------------------------------
889 */
890
891static int
892CreateNamedFont(interp, tkwin, name, faPtr)
893    Tcl_Interp *interp;		/* Interp for error return. */
894    Tk_Window tkwin;		/* A window associated with interp. */
895    CONST char *name;		/* Name for the new named font. */
896    TkFontAttributes *faPtr;	/* Attributes for the new named font. */
897{
898    TkFontInfo *fiPtr;
899    Tcl_HashEntry *namedHashPtr;
900    int new;
901    NamedFont *nfPtr;
902
903    fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
904
905    namedHashPtr = Tcl_CreateHashEntry(&fiPtr->namedTable, name, &new);
906
907    if (new == 0) {
908	nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
909	if (nfPtr->deletePending == 0) {
910	    Tcl_ResetResult(interp);
911	    Tcl_AppendResult(interp, "named font \"", name,
912		    "\" already exists", (char *) NULL);
913	    return TCL_ERROR;
914	}
915
916	/*
917	 * Recreating a named font with the same name as a previous
918	 * named font.  Some widgets were still using that named
919	 * font, so they need to get redisplayed.
920	 */
921
922	nfPtr->fa = *faPtr;
923	nfPtr->deletePending = 0;
924	UpdateDependentFonts(fiPtr, tkwin, namedHashPtr);
925	return TCL_OK;
926    }
927
928    nfPtr = (NamedFont *) ckalloc(sizeof(NamedFont));
929    nfPtr->deletePending = 0;
930    Tcl_SetHashValue(namedHashPtr, nfPtr);
931    nfPtr->fa = *faPtr;
932    nfPtr->refCount = 0;
933    nfPtr->deletePending = 0;
934    return TCL_OK;
935}
936
937/*
938 *---------------------------------------------------------------------------
939 *
940 * Tk_GetFont --
941 *
942 *	Given a string description of a font, map the description to a
943 *	corresponding Tk_Font that represents the font.
944 *
945 * Results:
946 *	The return value is token for the font, or NULL if an error
947 *	prevented the font from being created.  If NULL is returned, an
948 *	error message will be left in the interp's result.
949 *
950 * Side effects:
951 *	The font is added to an internal database with a reference
952 *	count.  For each call to this procedure, there should eventually
953 *	be a call to Tk_FreeFont() or Tk_FreeFontFromObj() so that the
954 *	database is cleaned up when fonts aren't in use anymore.
955 *
956 *---------------------------------------------------------------------------
957 */
958
959Tk_Font
960Tk_GetFont(interp, tkwin, string)
961    Tcl_Interp *interp;		/* Interp for database and error return, or
962				 * NULL for no error messages. */
963    Tk_Window tkwin;		/* For display on which font will be used. */
964    CONST char *string;		/* String describing font, as: named font,
965				 * native format, or parseable string. */
966{
967    Tk_Font tkfont;
968    Tcl_Obj *strPtr;
969
970    strPtr = Tcl_NewStringObj((char *) string, -1);
971    Tcl_IncrRefCount(strPtr);
972    tkfont = Tk_AllocFontFromObj(interp, tkwin, strPtr);
973    Tcl_DecrRefCount(strPtr);
974    return tkfont;
975}
976
977/*
978 *---------------------------------------------------------------------------
979 *
980 * Tk_AllocFontFromObj --
981 *
982 *	Given a string description of a font, map the description to a
983 *	corresponding Tk_Font that represents the font.
984 *
985 * Results:
986 *	The return value is token for the font, or NULL if an error
987 *	prevented the font from being created.  If NULL is returned, an
988 *	error message will be left in interp's result object (if non-NULL).
989 *
990 * Side effects:
991 * 	The font is added to an internal database with a reference
992 *	count.  For each call to this procedure, there should eventually
993 *	be a call to Tk_FreeFont() or Tk_FreeFontFromObj() so that the
994 *	database is cleaned up when fonts aren't in use anymore.
995 *
996 *---------------------------------------------------------------------------
997 */
998
999Tk_Font
1000Tk_AllocFontFromObj(interp, tkwin, objPtr)
1001    Tcl_Interp *interp;		/* Interp for database and error return. */
1002    Tk_Window tkwin;		/* For screen on which font will be used. */
1003    Tcl_Obj *objPtr;		/* Object describing font, as: named font,
1004				 * native format, or parseable string. */
1005{
1006    TkFontInfo *fiPtr;
1007    Tcl_HashEntry *cacheHashPtr, *namedHashPtr;
1008    TkFont *fontPtr, *firstFontPtr, *oldFontPtr;
1009    int new, descent;
1010    NamedFont *nfPtr;
1011
1012    fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
1013    if (objPtr->typePtr != &tkFontObjType) {
1014	SetFontFromAny(interp, objPtr);
1015    }
1016
1017    oldFontPtr = (TkFont *) objPtr->internalRep.twoPtrValue.ptr1;
1018
1019    if (oldFontPtr != NULL) {
1020	if (oldFontPtr->resourceRefCount == 0) {
1021	    /*
1022	     * This is a stale reference: it refers to a TkFont that's
1023	     * no longer in use.  Clear the reference.
1024	     */
1025
1026	    FreeFontObjProc(objPtr);
1027	    oldFontPtr = NULL;
1028	} else if (Tk_Screen(tkwin) == oldFontPtr->screen) {
1029	    oldFontPtr->resourceRefCount++;
1030	    return (Tk_Font) oldFontPtr;
1031	}
1032    }
1033
1034    /*
1035     * Next, search the list of fonts that have the name we want, to see
1036     * if one of them is for the right screen.
1037     */
1038
1039    new = 0;
1040    if (oldFontPtr != NULL) {
1041	cacheHashPtr = oldFontPtr->cacheHashPtr;
1042	FreeFontObjProc(objPtr);
1043    } else {
1044	cacheHashPtr = Tcl_CreateHashEntry(&fiPtr->fontCache,
1045		Tcl_GetString(objPtr), &new);
1046    }
1047    firstFontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr);
1048    for (fontPtr = firstFontPtr; (fontPtr != NULL);
1049	    fontPtr = fontPtr->nextPtr) {
1050	if (Tk_Screen(tkwin) == fontPtr->screen) {
1051	    fontPtr->resourceRefCount++;
1052	    fontPtr->objRefCount++;
1053	    objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) fontPtr;
1054	    return (Tk_Font) fontPtr;
1055	}
1056    }
1057
1058    /*
1059     * The desired font isn't in the table.  Make a new one.
1060     */
1061
1062    namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable,
1063	    Tcl_GetString(objPtr));
1064    if (namedHashPtr != NULL) {
1065	/*
1066	 * Construct a font based on a named font.
1067	 */
1068
1069	nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
1070	nfPtr->refCount++;
1071
1072	fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &nfPtr->fa);
1073    } else {
1074	/*
1075	 * Native font?
1076	 */
1077
1078	fontPtr = TkpGetNativeFont(tkwin, Tcl_GetString(objPtr));
1079	if (fontPtr == NULL) {
1080	    TkFontAttributes fa;
1081	    Tcl_Obj *dupObjPtr = Tcl_DuplicateObj(objPtr);
1082
1083	    if (ParseFontNameObj(interp, tkwin, dupObjPtr, &fa) != TCL_OK) {
1084		if (new) {
1085		    Tcl_DeleteHashEntry(cacheHashPtr);
1086		}
1087		Tcl_DecrRefCount(dupObjPtr);
1088		return NULL;
1089	    }
1090	    Tcl_DecrRefCount(dupObjPtr);
1091
1092	    /*
1093	     * String contained the attributes inline.
1094	     */
1095
1096	    fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &fa);
1097	}
1098    }
1099
1100    fontPtr->resourceRefCount = 1;
1101    fontPtr->objRefCount = 1;
1102    fontPtr->cacheHashPtr = cacheHashPtr;
1103    fontPtr->namedHashPtr = namedHashPtr;
1104    fontPtr->screen = Tk_Screen(tkwin);
1105    fontPtr->nextPtr = firstFontPtr;
1106    Tcl_SetHashValue(cacheHashPtr, fontPtr);
1107
1108    Tk_MeasureChars((Tk_Font) fontPtr, "0", 1, -1, 0, &fontPtr->tabWidth);
1109    if (fontPtr->tabWidth == 0) {
1110	fontPtr->tabWidth = fontPtr->fm.maxWidth;
1111    }
1112    fontPtr->tabWidth *= 8;
1113
1114    /*
1115     * Make sure the tab width isn't zero (some fonts may not have enough
1116     * information to set a reasonable tab width).
1117     */
1118
1119    if (fontPtr->tabWidth == 0) {
1120	fontPtr->tabWidth = 1;
1121    }
1122
1123    /*
1124     * Get information used for drawing underlines in generic code on a
1125     * non-underlined font.
1126     */
1127
1128    descent = fontPtr->fm.descent;
1129    fontPtr->underlinePos = descent / 2;
1130    fontPtr->underlineHeight = TkFontGetPixels(tkwin, fontPtr->fa.size) / 10;
1131    if (fontPtr->underlineHeight == 0) {
1132	fontPtr->underlineHeight = 1;
1133    }
1134    if (fontPtr->underlinePos + fontPtr->underlineHeight > descent) {
1135	/*
1136	 * If this set of values would cause the bottom of the underline
1137	 * bar to stick below the descent of the font, jack the underline
1138	 * up a bit higher.
1139	 */
1140
1141	fontPtr->underlineHeight = descent - fontPtr->underlinePos;
1142	if (fontPtr->underlineHeight == 0) {
1143	    fontPtr->underlinePos--;
1144	    fontPtr->underlineHeight = 1;
1145	}
1146    }
1147
1148    objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) fontPtr;
1149    return (Tk_Font) fontPtr;
1150}
1151
1152/*
1153 *----------------------------------------------------------------------
1154 *
1155 * Tk_GetFontFromObj --
1156 *
1157 *	Find the font that corresponds to a given object.  The font must
1158 *	have already been created by Tk_GetFont or Tk_AllocFontFromObj.
1159 *
1160 * Results:
1161 *	The return value is a token for the font that matches objPtr
1162 *	and is suitable for use in tkwin.
1163 *
1164 * Side effects:
1165 *	If the object is not already a font ref, the conversion will free
1166 *	any old internal representation.
1167 *
1168 *----------------------------------------------------------------------
1169 */
1170
1171Tk_Font
1172Tk_GetFontFromObj(tkwin, objPtr)
1173    Tk_Window tkwin;		/* The window that the font will be used in. */
1174    Tcl_Obj *objPtr;		/* The object from which to get the font. */
1175{
1176    TkFontInfo *fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
1177    TkFont *fontPtr;
1178    Tcl_HashEntry *hashPtr;
1179
1180    if (objPtr->typePtr != &tkFontObjType) {
1181	SetFontFromAny((Tcl_Interp *) NULL, objPtr);
1182    }
1183
1184    fontPtr = (TkFont *) objPtr->internalRep.twoPtrValue.ptr1;
1185
1186    if (fontPtr != NULL) {
1187	if (fontPtr->resourceRefCount == 0) {
1188	    /*
1189	     * This is a stale reference: it refers to a TkFont that's
1190	     * no longer in use.  Clear the reference.
1191	     */
1192
1193	    FreeFontObjProc(objPtr);
1194	    fontPtr = NULL;
1195	} else if (Tk_Screen(tkwin) == fontPtr->screen) {
1196	    return (Tk_Font) fontPtr;
1197	}
1198    }
1199
1200    /*
1201     * Next, search the list of fonts that have the name we want, to see
1202     * if one of them is for the right screen.
1203     */
1204
1205    if (fontPtr != NULL) {
1206	hashPtr = fontPtr->cacheHashPtr;
1207	FreeFontObjProc(objPtr);
1208    } else {
1209	hashPtr = Tcl_FindHashEntry(&fiPtr->fontCache, Tcl_GetString(objPtr));
1210    }
1211    if (hashPtr != NULL) {
1212	for (fontPtr = (TkFont *) Tcl_GetHashValue(hashPtr); fontPtr != NULL;
1213		fontPtr = fontPtr->nextPtr) {
1214	    if (Tk_Screen(tkwin) == fontPtr->screen) {
1215		fontPtr->objRefCount++;
1216		objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) fontPtr;
1217		return (Tk_Font) fontPtr;
1218	    }
1219	}
1220    }
1221
1222    panic("Tk_GetFontFromObj called with non-existent font!");
1223    return NULL;
1224}
1225
1226/*
1227 *----------------------------------------------------------------------
1228 *
1229 * SetFontFromAny --
1230 *
1231 *	Convert the internal representation of a Tcl object to the
1232 *	font internal form.
1233 *
1234 * Results:
1235 *	Always returns TCL_OK.
1236 *
1237 * Side effects:
1238 *	The object is left with its typePtr pointing to tkFontObjType.
1239 *	The TkFont pointer is NULL.
1240 *
1241 *----------------------------------------------------------------------
1242 */
1243
1244static int
1245SetFontFromAny(interp, objPtr)
1246    Tcl_Interp *interp;		/* Used for error reporting if not NULL. */
1247    Tcl_Obj *objPtr;		/* The object to convert. */
1248{
1249    Tcl_ObjType *typePtr;
1250
1251    /*
1252     * Free the old internalRep before setting the new one.
1253     */
1254
1255    Tcl_GetString(objPtr);
1256    typePtr = objPtr->typePtr;
1257    if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
1258	(*typePtr->freeIntRepProc)(objPtr);
1259    }
1260    objPtr->typePtr = &tkFontObjType;
1261    objPtr->internalRep.twoPtrValue.ptr1 = NULL;
1262
1263    return TCL_OK;
1264}
1265
1266/*
1267 *---------------------------------------------------------------------------
1268 *
1269 * Tk_NameOfFont --
1270 *
1271 *	Given a font, return a textual string identifying it.
1272 *
1273 * Results:
1274 *	The return value is the description that was passed to
1275 *	Tk_GetFont() to create the font.  The storage for the returned
1276 *	string is only guaranteed to persist until the font is deleted.
1277 *	The caller should not modify this string.
1278 *
1279 * Side effects:
1280 *	None.
1281 *
1282 *---------------------------------------------------------------------------
1283 */
1284
1285CONST char *
1286Tk_NameOfFont(tkfont)
1287    Tk_Font tkfont;		/* Font whose name is desired. */
1288{
1289    TkFont *fontPtr;
1290
1291    fontPtr = (TkFont *) tkfont;
1292    return fontPtr->cacheHashPtr->key.string;
1293}
1294
1295/*
1296 *---------------------------------------------------------------------------
1297 *
1298 * Tk_FreeFont --
1299 *
1300 *	Called to release a font allocated by Tk_GetFont().
1301 *
1302 * Results:
1303 *	None.
1304 *
1305 * Side effects:
1306 *	The reference count associated with font is decremented, and
1307 *	only deallocated when no one is using it.
1308 *
1309 *---------------------------------------------------------------------------
1310 */
1311
1312void
1313Tk_FreeFont(tkfont)
1314    Tk_Font tkfont;		/* Font to be released. */
1315{
1316    TkFont *fontPtr, *prevPtr;
1317    NamedFont *nfPtr;
1318
1319    if (tkfont == NULL) {
1320	return;
1321    }
1322    fontPtr = (TkFont *) tkfont;
1323    fontPtr->resourceRefCount--;
1324    if (fontPtr->resourceRefCount > 0) {
1325	return;
1326    }
1327    if (fontPtr->namedHashPtr != NULL) {
1328	/*
1329	 * This font derived from a named font.  Reduce the reference
1330	 * count on the named font and free it if no-one else is
1331	 * using it.
1332	 */
1333
1334	nfPtr = (NamedFont *) Tcl_GetHashValue(fontPtr->namedHashPtr);
1335	nfPtr->refCount--;
1336	if ((nfPtr->refCount == 0) && (nfPtr->deletePending != 0)) {
1337	    Tcl_DeleteHashEntry(fontPtr->namedHashPtr);
1338	    ckfree((char *) nfPtr);
1339	}
1340    }
1341
1342    prevPtr = (TkFont *) Tcl_GetHashValue(fontPtr->cacheHashPtr);
1343    if (prevPtr == fontPtr) {
1344	if (fontPtr->nextPtr == NULL) {
1345	    Tcl_DeleteHashEntry(fontPtr->cacheHashPtr);
1346	} else  {
1347	    Tcl_SetHashValue(fontPtr->cacheHashPtr, fontPtr->nextPtr);
1348	}
1349    } else {
1350	while (prevPtr->nextPtr != fontPtr) {
1351	    prevPtr = prevPtr->nextPtr;
1352	}
1353	prevPtr->nextPtr = fontPtr->nextPtr;
1354    }
1355
1356    TkpDeleteFont(fontPtr);
1357    if (fontPtr->objRefCount == 0) {
1358	ckfree((char *) fontPtr);
1359    }
1360}
1361
1362/*
1363 *---------------------------------------------------------------------------
1364 *
1365 * Tk_FreeFontFromObj --
1366 *
1367 *	Called to release a font inside a Tcl_Obj *. Decrements the refCount
1368 *	of the font and removes it from the hash tables if necessary.
1369 *
1370 * Results:
1371 *	None.
1372 *
1373 * Side effects:
1374 *	The reference count associated with font is decremented, and
1375 *	only deallocated when no one is using it.
1376 *
1377 *---------------------------------------------------------------------------
1378 */
1379
1380void
1381Tk_FreeFontFromObj(tkwin, objPtr)
1382    Tk_Window tkwin;		/* The window this font lives in. Needed
1383				 * for the screen value. */
1384    Tcl_Obj *objPtr;		/* The Tcl_Obj * to be freed. */
1385{
1386    Tk_FreeFont(Tk_GetFontFromObj(tkwin, objPtr));
1387}
1388
1389/*
1390 *---------------------------------------------------------------------------
1391 *
1392 * FreeFontObjProc --
1393 *
1394 *	This proc is called to release an object reference to a font.
1395 *	Called when the object's internal rep is released or when
1396 *	the cached fontPtr needs to be changed.
1397 *
1398 * Results:
1399 *	None.
1400 *
1401 * Side effects:
1402 *	The object reference count is decremented. When both it
1403 *	and the hash ref count go to zero, the font's resources
1404 *	are released.
1405 *
1406 *---------------------------------------------------------------------------
1407 */
1408
1409static void
1410FreeFontObjProc(objPtr)
1411    Tcl_Obj *objPtr;		/* The object we are releasing. */
1412{
1413    TkFont *fontPtr = (TkFont *) objPtr->internalRep.twoPtrValue.ptr1;
1414
1415    if (fontPtr != NULL) {
1416	fontPtr->objRefCount--;
1417	if ((fontPtr->resourceRefCount == 0) && (fontPtr->objRefCount == 0)) {
1418	    ckfree((char *) fontPtr);
1419	    objPtr->internalRep.twoPtrValue.ptr1 = NULL;
1420	}
1421    }
1422}
1423
1424/*
1425 *---------------------------------------------------------------------------
1426 *
1427 * DupFontObjProc --
1428 *
1429 *	When a cached font object is duplicated, this is called to
1430 *	update the internal reps.
1431 *
1432 * Results:
1433 *	None.
1434 *
1435 * Side effects:
1436 *	The font's objRefCount is incremented and the internal rep
1437 *	of the copy is set to point to it.
1438 *
1439 *---------------------------------------------------------------------------
1440 */
1441
1442static void
1443DupFontObjProc(srcObjPtr, dupObjPtr)
1444    Tcl_Obj *srcObjPtr;		/* The object we are copying from. */
1445    Tcl_Obj *dupObjPtr;		/* The object we are copying to. */
1446{
1447    TkFont *fontPtr = (TkFont *) srcObjPtr->internalRep.twoPtrValue.ptr1;
1448
1449    dupObjPtr->typePtr = srcObjPtr->typePtr;
1450    dupObjPtr->internalRep.twoPtrValue.ptr1 = (VOID *) fontPtr;
1451
1452    if (fontPtr != NULL) {
1453	fontPtr->objRefCount++;
1454    }
1455}
1456
1457/*
1458 *---------------------------------------------------------------------------
1459 *
1460 * Tk_FontId --
1461 *
1462 *	Given a font, return an opaque handle that should be selected
1463 *	into the XGCValues structure in order to get the constructed
1464 *	gc to use this font.  This procedure would go away if the
1465 *	XGCValues structure were replaced with a TkGCValues structure.
1466 *
1467 * Results:
1468 *	As above.
1469 *
1470 * Side effects:
1471 *	None.
1472 *
1473 *---------------------------------------------------------------------------
1474 */
1475
1476Font
1477Tk_FontId(tkfont)
1478    Tk_Font tkfont;	/* Font that is going to be selected into GC. */
1479{
1480    TkFont *fontPtr;
1481
1482    fontPtr = (TkFont *) tkfont;
1483    return fontPtr->fid;
1484}
1485
1486/*
1487 *---------------------------------------------------------------------------
1488 *
1489 * Tk_GetFontMetrics --
1490 *
1491 *	Returns overall ascent and descent metrics for the given font.
1492 *	These values can be used to space multiple lines of text and
1493 *	to align the baselines of text in different fonts.
1494 *
1495 * Results:
1496 *	If *heightPtr is non-NULL, it is filled with the overall height
1497 *	of the font, which is the sum of the ascent and descent.
1498 *	If *ascentPtr or *descentPtr is non-NULL, they are filled with
1499 *	the ascent and/or descent information for the font.
1500 *
1501 * Side effects:
1502 *	None.
1503 *
1504 *---------------------------------------------------------------------------
1505 */
1506void
1507Tk_GetFontMetrics(tkfont, fmPtr)
1508    Tk_Font tkfont;		/* Font in which metrics are calculated. */
1509    Tk_FontMetrics *fmPtr;	/* Pointer to structure in which font
1510				 * metrics for tkfont will be stored. */
1511{
1512    TkFont *fontPtr;
1513
1514    fontPtr = (TkFont *) tkfont;
1515    fmPtr->ascent = fontPtr->fm.ascent;
1516    fmPtr->descent = fontPtr->fm.descent;
1517    fmPtr->linespace = fontPtr->fm.ascent + fontPtr->fm.descent;
1518}
1519
1520/*
1521 *---------------------------------------------------------------------------
1522 *
1523 * Tk_PostscriptFontName --
1524 *
1525 *	Given a Tk_Font, return the name of the corresponding Postscript
1526 *	font.
1527 *
1528 * Results:
1529 *	The return value is the pointsize of the given Tk_Font.
1530 *	The name of the Postscript font is appended to dsPtr.
1531 *
1532 * Side effects:
1533 *	If the font does not exist on the printer, the print job will
1534 *	fail at print time.  Given a "reasonable" Postscript printer,
1535 *	the following Tk_Font font families should print correctly:
1536 *
1537 *	    Avant Garde, Arial, Bookman, Courier, Courier New, Geneva,
1538 *	    Helvetica, Monaco, New Century Schoolbook, New York,
1539 *	    Palatino, Symbol, Times, Times New Roman, Zapf Chancery,
1540 *	    and Zapf Dingbats.
1541 *
1542 *	Any other Tk_Font font families may not print correctly
1543 *	because the computed Postscript font name may be incorrect.
1544 *
1545 *---------------------------------------------------------------------------
1546 */
1547
1548int
1549Tk_PostscriptFontName(tkfont, dsPtr)
1550    Tk_Font tkfont;		/* Font in which text will be printed. */
1551    Tcl_DString *dsPtr;		/* Pointer to an initialized Tcl_DString to
1552				 * which the name of the Postscript font that
1553				 * corresponds to tkfont will be appended. */
1554{
1555    TkFont *fontPtr;
1556    Tk_Uid family, weightString, slantString;
1557    char *src, *dest;
1558    int upper, len;
1559
1560    len = Tcl_DStringLength(dsPtr);
1561    fontPtr = (TkFont *) tkfont;
1562
1563    /*
1564     * Convert the case-insensitive Tk_Font family name to the
1565     * case-sensitive Postscript family name.  Take out any spaces and
1566     * capitalize the first letter of each word.
1567     */
1568
1569    family = fontPtr->fa.family;
1570    if (strncasecmp(family, "itc ", 4) == 0) {
1571	family = family + 4;
1572    }
1573    if ((strcasecmp(family, "Arial") == 0)
1574	    || (strcasecmp(family, "Geneva") == 0)) {
1575	family = "Helvetica";
1576    } else if ((strcasecmp(family, "Times New Roman") == 0)
1577	    || (strcasecmp(family, "New York") == 0)) {
1578	family = "Times";
1579    } else if ((strcasecmp(family, "Courier New") == 0)
1580	    || (strcasecmp(family, "Monaco") == 0)) {
1581	family = "Courier";
1582    } else if (strcasecmp(family, "AvantGarde") == 0) {
1583	family = "AvantGarde";
1584    } else if (strcasecmp(family, "ZapfChancery") == 0) {
1585	family = "ZapfChancery";
1586    } else if (strcasecmp(family, "ZapfDingbats") == 0) {
1587	family = "ZapfDingbats";
1588    } else {
1589	Tcl_UniChar ch;
1590
1591	/*
1592	 * Inline, capitalize the first letter of each word, lowercase the
1593	 * rest of the letters in each word, and then take out the spaces
1594	 * between the words.  This may make the DString shorter, which is
1595	 * safe to do.
1596	 */
1597
1598	Tcl_DStringAppend(dsPtr, family, -1);
1599
1600	src = dest = Tcl_DStringValue(dsPtr) + len;
1601	upper = 1;
1602	for (; *src != '\0'; ) {
1603	    while (isspace(UCHAR(*src))) { /* INTL: ISO space */
1604		src++;
1605		upper = 1;
1606	    }
1607	    src += Tcl_UtfToUniChar(src, &ch);
1608	    if (upper) {
1609		ch = Tcl_UniCharToUpper(ch);
1610		upper = 0;
1611	    } else {
1612	        ch = Tcl_UniCharToLower(ch);
1613	    }
1614	    dest += Tcl_UniCharToUtf(ch, dest);
1615	}
1616	*dest = '\0';
1617	Tcl_DStringSetLength(dsPtr, dest - Tcl_DStringValue(dsPtr));
1618	family = Tcl_DStringValue(dsPtr) + len;
1619    }
1620    if (family != Tcl_DStringValue(dsPtr) + len) {
1621	Tcl_DStringAppend(dsPtr, family, -1);
1622	family = Tcl_DStringValue(dsPtr) + len;
1623    }
1624
1625    if (strcasecmp(family, "NewCenturySchoolbook") == 0) {
1626	Tcl_DStringSetLength(dsPtr, len);
1627	Tcl_DStringAppend(dsPtr, "NewCenturySchlbk", -1);
1628	family = Tcl_DStringValue(dsPtr) + len;
1629    }
1630
1631    /*
1632     * Get the string to use for the weight.
1633     */
1634
1635    weightString = NULL;
1636    if (fontPtr->fa.weight == TK_FW_NORMAL) {
1637	if (strcmp(family, "Bookman") == 0) {
1638	    weightString = "Light";
1639	} else if (strcmp(family, "AvantGarde") == 0) {
1640	    weightString = "Book";
1641	} else if (strcmp(family, "ZapfChancery") == 0) {
1642	    weightString = "Medium";
1643	}
1644    } else {
1645	if ((strcmp(family, "Bookman") == 0)
1646		|| (strcmp(family, "AvantGarde") == 0)) {
1647	    weightString = "Demi";
1648	} else {
1649	    weightString = "Bold";
1650	}
1651    }
1652
1653    /*
1654     * Get the string to use for the slant.
1655     */
1656
1657    slantString = NULL;
1658    if (fontPtr->fa.slant == TK_FS_ROMAN) {
1659	;
1660    } else {
1661	if ((strcmp(family, "Helvetica") == 0)
1662		|| (strcmp(family, "Courier") == 0)
1663		|| (strcmp(family, "AvantGarde") == 0)) {
1664	    slantString = "Oblique";
1665	} else {
1666	    slantString = "Italic";
1667	}
1668    }
1669
1670    /*
1671     * The string "Roman" needs to be added to some fonts that are not bold
1672     * and not italic.
1673     */
1674
1675    if ((slantString == NULL) && (weightString == NULL)) {
1676	if ((strcmp(family, "Times") == 0)
1677		|| (strcmp(family, "NewCenturySchlbk") == 0)
1678		|| (strcmp(family, "Palatino") == 0)) {
1679	    Tcl_DStringAppend(dsPtr, "-Roman", -1);
1680	}
1681    } else {
1682	Tcl_DStringAppend(dsPtr, "-", -1);
1683	if (weightString != NULL) {
1684	    Tcl_DStringAppend(dsPtr, weightString, -1);
1685	}
1686	if (slantString != NULL) {
1687	    Tcl_DStringAppend(dsPtr, slantString, -1);
1688	}
1689    }
1690
1691    return fontPtr->fa.size;
1692}
1693
1694/*
1695 *---------------------------------------------------------------------------
1696 *
1697 * Tk_TextWidth --
1698 *
1699 *	A wrapper function for the more complicated interface of
1700 *	Tk_MeasureChars.  Computes how much space the given
1701 *	simple string needs.
1702 *
1703 * Results:
1704 *	The return value is the width (in pixels) of the given string.
1705 *
1706 * Side effects:
1707 *	None.
1708 *
1709 *---------------------------------------------------------------------------
1710 */
1711
1712int
1713Tk_TextWidth(tkfont, string, numBytes)
1714    Tk_Font tkfont;		/* Font in which text will be measured. */
1715    CONST char *string;		/* String whose width will be computed. */
1716    int numBytes;		/* Number of bytes to consider from
1717				 * string, or < 0 for strlen(). */
1718{
1719    int width;
1720
1721    if (numBytes < 0) {
1722	numBytes = strlen(string);
1723    }
1724    Tk_MeasureChars(tkfont, string, numBytes, -1, 0, &width);
1725    return width;
1726}
1727
1728/*
1729 *---------------------------------------------------------------------------
1730 *
1731 * Tk_UnderlineChars --
1732 *
1733 *	This procedure draws an underline for a given range of characters
1734 *	in a given string.  It doesn't draw the characters (which are
1735 *	assumed to have been displayed previously); it just draws the
1736 *	underline.  This procedure would mainly be used to quickly
1737 *	underline a few characters without having to construct an
1738 *	underlined font.  To produce properly underlined text, the
1739 *	appropriate underlined font should be constructed and used.
1740 *
1741 * Results:
1742 *	None.
1743 *
1744 * Side effects:
1745 *	Information gets displayed in "drawable".
1746 *
1747 *----------------------------------------------------------------------
1748 */
1749
1750void
1751Tk_UnderlineChars(display, drawable, gc, tkfont, string, x, y, firstByte,
1752	lastByte)
1753    Display *display;		/* Display on which to draw. */
1754    Drawable drawable;		/* Window or pixmap in which to draw. */
1755    GC gc;			/* Graphics context for actually drawing
1756				 * line. */
1757    Tk_Font tkfont;		/* Font used in GC;  must have been allocated
1758				 * by Tk_GetFont().  Used for character
1759				 * dimensions, etc. */
1760    CONST char *string;		/* String containing characters to be
1761				 * underlined or overstruck. */
1762    int x, y;			/* Coordinates at which first character of
1763				 * string is drawn. */
1764    int firstByte;		/* Index of first byte of first character. */
1765    int lastByte;		/* Index of first byte after the last
1766				 * character. */
1767{
1768    TkFont *fontPtr;
1769    int startX, endX;
1770
1771    fontPtr = (TkFont *) tkfont;
1772
1773    Tk_MeasureChars(tkfont, string, firstByte, -1, 0, &startX);
1774    Tk_MeasureChars(tkfont, string, lastByte, -1, 0, &endX);
1775
1776    XFillRectangle(display, drawable, gc, x + startX,
1777	    y + fontPtr->underlinePos, (unsigned int) (endX - startX),
1778	    (unsigned int) fontPtr->underlineHeight);
1779}
1780
1781/*
1782 *---------------------------------------------------------------------------
1783 *
1784 * Tk_ComputeTextLayout --
1785 *
1786 *	Computes the amount of screen space needed to display a
1787 *	multi-line, justified string of text.  Records all the
1788 *	measurements that were done to determine to size and
1789 *	positioning of the individual lines of text; this information
1790 *	can be used by the Tk_DrawTextLayout() procedure to
1791 *	display the text quickly (without remeasuring it).
1792 *
1793 *	This procedure is useful for simple widgets that want to
1794 *	display single-font, multi-line text and want Tk to handle the
1795 *	details.
1796 *
1797 * Results:
1798 *	The return value is a Tk_TextLayout token that holds the
1799 *	measurement information for the given string.  The token is
1800 *	only valid for the given string.  If the string is freed,
1801 *	the token is no longer valid and must also be freed.  To free
1802 *	the token, call Tk_FreeTextLayout().
1803 *
1804 *	The dimensions of the screen area needed to display the text
1805 *	are stored in *widthPtr and *heightPtr.
1806 *
1807 * Side effects:
1808 *	Memory is allocated to hold the measurement information.
1809 *
1810 *---------------------------------------------------------------------------
1811 */
1812
1813Tk_TextLayout
1814Tk_ComputeTextLayout(tkfont, string, numChars, wrapLength, justify, flags,
1815	widthPtr, heightPtr)
1816    Tk_Font tkfont;		/* Font that will be used to display text. */
1817    CONST char *string;		/* String whose dimensions are to be
1818				 * computed. */
1819    int numChars;		/* Number of characters to consider from
1820				 * string, or < 0 for strlen(). */
1821    int wrapLength;		/* Longest permissible line length, in
1822				 * pixels.  <= 0 means no automatic wrapping:
1823				 * just let lines get as long as needed. */
1824    Tk_Justify justify;		/* How to justify lines. */
1825    int flags;			/* Flag bits OR-ed together.
1826				 * TK_IGNORE_TABS means that tab characters
1827				 * should not be expanded.  TK_IGNORE_NEWLINES
1828				 * means that newline characters should not
1829				 * cause a line break. */
1830    int *widthPtr;		/* Filled with width of string. */
1831    int *heightPtr;		/* Filled with height of string. */
1832{
1833    TkFont *fontPtr;
1834    CONST char *start, *end, *special;
1835    int n, y, bytesThisChunk, maxChunks;
1836    int baseline, height, curX, newX, maxWidth;
1837    TextLayout *layoutPtr;
1838    LayoutChunk *chunkPtr;
1839    CONST TkFontMetrics *fmPtr;
1840    Tcl_DString lineBuffer;
1841    int *lineLengths;
1842    int curLine, layoutHeight;
1843
1844    Tcl_DStringInit(&lineBuffer);
1845
1846    fontPtr = (TkFont *) tkfont;
1847    if ((fontPtr == NULL) || (string == NULL)) {
1848	if (widthPtr != NULL) {
1849	    *widthPtr = 0;
1850	}
1851	if (heightPtr != NULL) {
1852	    *heightPtr = 0;
1853	}
1854	return NULL;
1855    }
1856
1857    fmPtr = &fontPtr->fm;
1858
1859    height = fmPtr->ascent + fmPtr->descent;
1860
1861    if (numChars < 0) {
1862	numChars = Tcl_NumUtfChars(string, -1);
1863    }
1864    if (wrapLength == 0) {
1865	wrapLength = -1;
1866    }
1867
1868    maxChunks = 1;
1869
1870    layoutPtr = (TextLayout *) ckalloc(sizeof(TextLayout)
1871	    + (maxChunks - 1) * sizeof(LayoutChunk));
1872    layoutPtr->tkfont	    = tkfont;
1873    layoutPtr->string	    = string;
1874    layoutPtr->numChunks    = 0;
1875
1876    baseline = fmPtr->ascent;
1877    maxWidth = 0;
1878
1879    /*
1880     * Divide the string up into simple strings and measure each string.
1881     */
1882
1883    curX = 0;
1884
1885    end = Tcl_UtfAtIndex(string, numChars);
1886    special = string;
1887
1888    flags &= TK_IGNORE_TABS | TK_IGNORE_NEWLINES;
1889    flags |= TK_WHOLE_WORDS | TK_AT_LEAST_ONE;
1890    for (start = string; start < end; ) {
1891	if (start >= special) {
1892	    /*
1893	     * Find the next special character in the string.
1894	     *
1895	     * INTL: Note that it is safe to increment by byte, because we are
1896	     * looking for 7-bit characters that will appear unchanged in
1897	     * UTF-8.  At some point we may need to support the full Unicode
1898	     * whitespace set.
1899	     */
1900
1901	    for (special = start; special < end; special++) {
1902		if (!(flags & TK_IGNORE_NEWLINES)) {
1903		    if ((*special == '\n') || (*special == '\r')) {
1904			break;
1905		    }
1906		}
1907		if (!(flags & TK_IGNORE_TABS)) {
1908		    if (*special == '\t') {
1909			break;
1910		    }
1911		}
1912	    }
1913	}
1914
1915	/*
1916	 * Special points at the next special character (or the end of the
1917	 * string).  Process characters between start and special.
1918	 */
1919
1920	chunkPtr = NULL;
1921	if (start < special) {
1922	    bytesThisChunk = Tk_MeasureChars(tkfont, start, special - start,
1923		    wrapLength - curX, flags, &newX);
1924	    newX += curX;
1925	    flags &= ~TK_AT_LEAST_ONE;
1926	    if (bytesThisChunk > 0) {
1927		chunkPtr = NewChunk(&layoutPtr, &maxChunks, start,
1928			bytesThisChunk, curX, newX, baseline);
1929
1930		start += bytesThisChunk;
1931		curX = newX;
1932	    }
1933	}
1934
1935	if ((start == special) && (special < end)) {
1936	    /*
1937	     * Handle the special character.
1938	     *
1939	     * INTL: Special will be pointing at a 7-bit character so we
1940	     * can safely treat it as a single byte.
1941	     */
1942
1943	    chunkPtr = NULL;
1944	    if (*special == '\t') {
1945		newX = curX + fontPtr->tabWidth;
1946		newX -= newX % fontPtr->tabWidth;
1947		NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX,
1948			baseline)->numDisplayChars = -1;
1949		start++;
1950		if ((start < end) &&
1951			((wrapLength <= 0) || (newX <= wrapLength))) {
1952		    /*
1953		     * More chars can still fit on this line.
1954		     */
1955
1956		    curX = newX;
1957		    flags &= ~TK_AT_LEAST_ONE;
1958		    continue;
1959		}
1960	    } else {
1961		NewChunk(&layoutPtr, &maxChunks, start, 1, curX, curX,
1962			baseline)->numDisplayChars = -1;
1963		start++;
1964		goto wrapLine;
1965	    }
1966	}
1967
1968	/*
1969	 * No more characters are going to go on this line, either because
1970	 * no more characters can fit or there are no more characters left.
1971	 * Consume all extra spaces at end of line.
1972	 */
1973
1974	while ((start < end) && isspace(UCHAR(*start))) { /* INTL: ISO space */
1975	    if (!(flags & TK_IGNORE_NEWLINES)) {
1976		if ((*start == '\n') || (*start == '\r')) {
1977		    break;
1978		}
1979	    }
1980	    if (!(flags & TK_IGNORE_TABS)) {
1981		if (*start == '\t') {
1982		    break;
1983		}
1984	    }
1985	    start++;
1986	}
1987	if (chunkPtr != NULL) {
1988	    CONST char *end;
1989
1990	    /*
1991	     * Append all the extra spaces on this line to the end of the
1992	     * last text chunk.  This is a little tricky because we are
1993	     * switching back and forth between characters and bytes.
1994	     */
1995
1996	    end = chunkPtr->start + chunkPtr->numBytes;
1997	    bytesThisChunk = start - end;
1998	    if (bytesThisChunk > 0) {
1999		bytesThisChunk = Tk_MeasureChars(tkfont, end, bytesThisChunk,
2000			-1, 0, &chunkPtr->totalWidth);
2001		chunkPtr->numBytes += bytesThisChunk;
2002		chunkPtr->numChars += Tcl_NumUtfChars(end, bytesThisChunk);
2003		chunkPtr->totalWidth += curX;
2004	    }
2005	}
2006
2007        wrapLine:
2008	flags |= TK_AT_LEAST_ONE;
2009
2010	/*
2011	 * Save current line length, then move current position to start of
2012	 * next line.
2013	 */
2014
2015	if (curX > maxWidth) {
2016	    maxWidth = curX;
2017	}
2018
2019	/*
2020	 * Remember width of this line, so that all chunks on this line
2021	 * can be centered or right justified, if necessary.
2022	 */
2023
2024	Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
2025
2026	curX = 0;
2027	baseline += height;
2028    }
2029
2030    /*
2031     * If last line ends with a newline, then we need to make a 0 width
2032     * chunk on the next line.  Otherwise "Hello" and "Hello\n" are the
2033     * same height.
2034     */
2035
2036    if ((layoutPtr->numChunks > 0) && ((flags & TK_IGNORE_NEWLINES) == 0)) {
2037	if (layoutPtr->chunks[layoutPtr->numChunks - 1].start[0] == '\n') {
2038	    chunkPtr = NewChunk(&layoutPtr, &maxChunks, start, 0, curX,
2039		    curX, baseline);
2040	    chunkPtr->numDisplayChars = -1;
2041	    Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
2042	    baseline += height;
2043	}
2044    }
2045
2046    layoutPtr->width = maxWidth;
2047    layoutHeight = baseline - fmPtr->ascent;
2048    if (layoutPtr->numChunks == 0) {
2049	layoutHeight = height;
2050
2051	/*
2052	 * This fake chunk is used by the other procedures so that they can
2053	 * pretend that there is a chunk with no chars in it, which makes
2054	 * the coding simpler.
2055	 */
2056
2057	layoutPtr->numChunks = 1;
2058	layoutPtr->chunks[0].start		= string;
2059	layoutPtr->chunks[0].numBytes		= 0;
2060	layoutPtr->chunks[0].numChars		= 0;
2061	layoutPtr->chunks[0].numDisplayChars	= -1;
2062	layoutPtr->chunks[0].x			= 0;
2063	layoutPtr->chunks[0].y			= fmPtr->ascent;
2064	layoutPtr->chunks[0].totalWidth		= 0;
2065	layoutPtr->chunks[0].displayWidth	= 0;
2066    } else {
2067	/*
2068	 * Using maximum line length, shift all the chunks so that the lines
2069	 * are all justified correctly.
2070	 */
2071
2072	curLine = 0;
2073	chunkPtr = layoutPtr->chunks;
2074	y = chunkPtr->y;
2075	lineLengths = (int *) Tcl_DStringValue(&lineBuffer);
2076	for (n = 0; n < layoutPtr->numChunks; n++) {
2077	    int extra;
2078
2079	    if (chunkPtr->y != y) {
2080		curLine++;
2081		y = chunkPtr->y;
2082	    }
2083	    extra = maxWidth - lineLengths[curLine];
2084	    if (justify == TK_JUSTIFY_CENTER) {
2085		chunkPtr->x += extra / 2;
2086	    } else if (justify == TK_JUSTIFY_RIGHT) {
2087		chunkPtr->x += extra;
2088	    }
2089	    chunkPtr++;
2090	}
2091    }
2092
2093    if (widthPtr != NULL) {
2094	*widthPtr = layoutPtr->width;
2095    }
2096    if (heightPtr != NULL) {
2097	*heightPtr = layoutHeight;
2098    }
2099    Tcl_DStringFree(&lineBuffer);
2100
2101    return (Tk_TextLayout) layoutPtr;
2102}
2103
2104/*
2105 *---------------------------------------------------------------------------
2106 *
2107 * Tk_FreeTextLayout --
2108 *
2109 *	This procedure is called to release the storage associated with
2110 *	a Tk_TextLayout when it is no longer needed.
2111 *
2112 * Results:
2113 *	None.
2114 *
2115 * Side effects:
2116 *	Memory is freed.
2117 *
2118 *---------------------------------------------------------------------------
2119 */
2120
2121void
2122Tk_FreeTextLayout(textLayout)
2123    Tk_TextLayout textLayout;	/* The text layout to be released. */
2124{
2125    TextLayout *layoutPtr;
2126
2127    layoutPtr = (TextLayout *) textLayout;
2128    if (layoutPtr != NULL) {
2129	ckfree((char *) layoutPtr);
2130    }
2131}
2132
2133/*
2134 *---------------------------------------------------------------------------
2135 *
2136 * Tk_DrawTextLayout --
2137 *
2138 *	Use the information in the Tk_TextLayout token to display a
2139 *	multi-line, justified string of text.
2140 *
2141 *	This procedure is useful for simple widgets that need to
2142 *	display single-font, multi-line text and want Tk to handle
2143 *	the details.
2144 *
2145 * Results:
2146 *	None.
2147 *
2148 * Side effects:
2149 *	Text drawn on the screen.
2150 *
2151 *---------------------------------------------------------------------------
2152 */
2153
2154void
2155Tk_DrawTextLayout(display, drawable, gc, layout, x, y, firstChar, lastChar)
2156    Display *display;		/* Display on which to draw. */
2157    Drawable drawable;		/* Window or pixmap in which to draw. */
2158    GC gc;			/* Graphics context to use for drawing text. */
2159    Tk_TextLayout layout;	/* Layout information, from a previous call
2160				 * to Tk_ComputeTextLayout(). */
2161    int x, y;			/* Upper-left hand corner of rectangle in
2162				 * which to draw (pixels). */
2163    int firstChar;		/* The index of the first character to draw
2164				 * from the given text item.  0 specfies the
2165				 * beginning. */
2166    int lastChar;		/* The index just after the last character
2167				 * to draw from the given text item.  A number
2168				 * < 0 means to draw all characters. */
2169{
2170    TextLayout *layoutPtr;
2171    int i, numDisplayChars, drawX;
2172    CONST char *firstByte;
2173    CONST char *lastByte;
2174    LayoutChunk *chunkPtr;
2175
2176    layoutPtr = (TextLayout *) layout;
2177    if (layoutPtr == NULL) {
2178	return;
2179    }
2180
2181    if (lastChar < 0) {
2182	lastChar = 100000000;
2183    }
2184    chunkPtr = layoutPtr->chunks;
2185    for (i = 0; i < layoutPtr->numChunks; i++) {
2186	numDisplayChars = chunkPtr->numDisplayChars;
2187	if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) {
2188	    if (firstChar <= 0) {
2189		drawX = 0;
2190		firstChar = 0;
2191		firstByte = chunkPtr->start;
2192	    } else {
2193		firstByte = Tcl_UtfAtIndex(chunkPtr->start, firstChar);
2194		Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start,
2195			firstByte - chunkPtr->start, -1, 0, &drawX);
2196	    }
2197	    if (lastChar < numDisplayChars) {
2198		numDisplayChars = lastChar;
2199	    }
2200	    lastByte = Tcl_UtfAtIndex(chunkPtr->start, numDisplayChars);
2201	    Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont,
2202		    firstByte, lastByte - firstByte,
2203		    x + chunkPtr->x + drawX, y + chunkPtr->y);
2204	}
2205	firstChar -= chunkPtr->numChars;
2206	lastChar -= chunkPtr->numChars;
2207	if (lastChar <= 0) {
2208	    break;
2209	}
2210	chunkPtr++;
2211    }
2212}
2213
2214/*
2215 *---------------------------------------------------------------------------
2216 *
2217 * Tk_UnderlineTextLayout --
2218 *
2219 *	Use the information in the Tk_TextLayout token to display an
2220 *	underline below an individual character.  This procedure does
2221 *	not draw the text, just the underline.
2222 *
2223 *	This procedure is useful for simple widgets that need to
2224 *	display single-font, multi-line text with an individual
2225 *	character underlined and want Tk to handle the details.
2226 *	To display larger amounts of underlined text, construct
2227 *	and use an underlined font.
2228 *
2229 * Results:
2230 *	None.
2231 *
2232 * Side effects:
2233 *	Underline drawn on the screen.
2234 *
2235 *---------------------------------------------------------------------------
2236 */
2237
2238void
2239Tk_UnderlineTextLayout(display, drawable, gc, layout, x, y, underline)
2240    Display *display;		/* Display on which to draw. */
2241    Drawable drawable;		/* Window or pixmap in which to draw. */
2242    GC gc;			/* Graphics context to use for drawing text. */
2243    Tk_TextLayout layout;	/* Layout information, from a previous call
2244				 * to Tk_ComputeTextLayout(). */
2245    int x, y;			/* Upper-left hand corner of rectangle in
2246				 * which to draw (pixels). */
2247    int underline;		/* Index of the single character to
2248				 * underline, or -1 for no underline. */
2249{
2250    TextLayout *layoutPtr;
2251    TkFont *fontPtr;
2252    int xx, yy, width, height;
2253
2254    if ((Tk_CharBbox(layout, underline, &xx, &yy, &width, &height) != 0)
2255	    && (width != 0)) {
2256	layoutPtr = (TextLayout *) layout;
2257	fontPtr = (TkFont *) layoutPtr->tkfont;
2258
2259	XFillRectangle(display, drawable, gc, x + xx,
2260		y + yy + fontPtr->fm.ascent + fontPtr->underlinePos,
2261		(unsigned int) width, (unsigned int) fontPtr->underlineHeight);
2262    }
2263}
2264
2265/*
2266 *---------------------------------------------------------------------------
2267 *
2268 * Tk_PointToChar --
2269 *
2270 *	Use the information in the Tk_TextLayout token to determine the
2271 *	character closest to the given point.  The point must be
2272 *	specified with respect to the upper-left hand corner of the
2273 *	text layout, which is considered to be located at (0, 0).
2274 *
2275 *	Any point whose y-value is less that 0 will be considered closest
2276 *	to the first character in the text layout; any point whose y-value
2277 *	is greater than the height of the text layout will be considered
2278 *	closest to the last character in the text layout.
2279 *
2280 *	Any point whose x-value is less than 0 will be considered closest
2281 *	to the first character on that line; any point whose x-value is
2282 *	greater than the width of the text layout will be considered
2283 *	closest to the last character on that line.
2284 *
2285 * Results:
2286 *	The return value is the index of the character that was
2287 *	closest to the point.  Given a text layout with no characters,
2288 *	the value 0 will always be returned, referring to a hypothetical
2289 *	zero-width placeholder character.
2290 *
2291 * Side effects:
2292 *	None.
2293 *
2294 *---------------------------------------------------------------------------
2295 */
2296
2297int
2298Tk_PointToChar(layout, x, y)
2299    Tk_TextLayout layout;	/* Layout information, from a previous call
2300				 * to Tk_ComputeTextLayout(). */
2301    int x, y;			/* Coordinates of point to check, with
2302				 * respect to the upper-left corner of the
2303				 * text layout. */
2304{
2305    TextLayout *layoutPtr;
2306    LayoutChunk *chunkPtr, *lastPtr;
2307    TkFont *fontPtr;
2308    int i, n, dummy, baseline, pos, numChars;
2309
2310    if (y < 0) {
2311	/*
2312	 * Point lies above any line in this layout.  Return the index of
2313	 * the first char.
2314	 */
2315
2316	return 0;
2317    }
2318
2319    /*
2320     * Find which line contains the point.
2321     */
2322
2323    layoutPtr = (TextLayout *) layout;
2324    fontPtr = (TkFont *) layoutPtr->tkfont;
2325    lastPtr = chunkPtr = layoutPtr->chunks;
2326    numChars = 0;
2327    for (i = 0; i < layoutPtr->numChunks; i++) {
2328	baseline = chunkPtr->y;
2329	if (y < baseline + fontPtr->fm.descent) {
2330	    if (x < chunkPtr->x) {
2331		/*
2332		 * Point is to the left of all chunks on this line.  Return
2333		 * the index of the first character on this line.
2334		 */
2335
2336		return numChars;
2337	    }
2338	    if (x >= layoutPtr->width) {
2339		/*
2340		 * If point lies off right side of the text layout, return
2341		 * the last char in the last chunk on this line.  Without
2342		 * this, it might return the index of the first char that
2343		 * was located outside of the text layout.
2344		 */
2345
2346		x = INT_MAX;
2347	    }
2348
2349	    /*
2350	     * Examine all chunks on this line to see which one contains
2351	     * the specified point.
2352	     */
2353
2354	    lastPtr = chunkPtr;
2355	    while ((i < layoutPtr->numChunks) && (chunkPtr->y == baseline))  {
2356		if (x < chunkPtr->x + chunkPtr->totalWidth) {
2357		    /*
2358		     * Point falls on one of the characters in this chunk.
2359		     */
2360
2361		    if (chunkPtr->numDisplayChars < 0) {
2362			/*
2363			 * This is a special chunk that encapsulates a single
2364			 * tab or newline char.
2365			 */
2366
2367			return numChars;
2368		    }
2369		    n = Tk_MeasureChars((Tk_Font) fontPtr, chunkPtr->start,
2370			    chunkPtr->numBytes, x - chunkPtr->x,
2371			    0, &dummy);
2372		    return numChars + Tcl_NumUtfChars(chunkPtr->start, n);
2373		}
2374		numChars += chunkPtr->numChars;
2375		lastPtr = chunkPtr;
2376		chunkPtr++;
2377		i++;
2378	    }
2379
2380	    /*
2381	     * Point is to the right of all chars in all the chunks on this
2382	     * line.  Return the index just past the last char in the last
2383	     * chunk on this line.
2384	     */
2385
2386	    pos = numChars;
2387	    if (i < layoutPtr->numChunks) {
2388		pos--;
2389	    }
2390	    return pos;
2391	}
2392	numChars += chunkPtr->numChars;
2393	lastPtr = chunkPtr;
2394	chunkPtr++;
2395    }
2396
2397    /*
2398     * Point lies below any line in this text layout.  Return the index
2399     * just past the last char.
2400     */
2401
2402    return (lastPtr->start + lastPtr->numChars) - layoutPtr->string;
2403}
2404
2405/*
2406 *---------------------------------------------------------------------------
2407 *
2408 * Tk_CharBbox --
2409 *
2410 *	Use the information in the Tk_TextLayout token to return the
2411 *	bounding box for the character specified by index.
2412 *
2413 *	The width of the bounding box is the advance width of the
2414 *	character, and does not include and left- or right-bearing.
2415 *	Any character that extends partially outside of the
2416 *	text layout is considered to be truncated at the edge.  Any
2417 *	character which is located completely outside of the text
2418 *	layout is considered to be zero-width and pegged against
2419 *	the edge.
2420 *
2421 *	The height of the bounding box is the line height for this font,
2422 *	extending from the top of the ascent to the bottom of the
2423 *	descent.  Information about the actual height of the individual
2424 *	letter is not available.
2425 *
2426 *	A text layout that contains no characters is considered to
2427 *	contain a single zero-width placeholder character.
2428 *
2429 * Results:
2430 *	The return value is 0 if the index did not specify a character
2431 *	in the text layout, or non-zero otherwise.  In that case,
2432 *	*bbox is filled with the bounding box of the character.
2433 *
2434 * Side effects:
2435 *	None.
2436 *
2437 *---------------------------------------------------------------------------
2438 */
2439
2440int
2441Tk_CharBbox(layout, index, xPtr, yPtr, widthPtr, heightPtr)
2442    Tk_TextLayout layout;   /* Layout information, from a previous call to
2443			     * Tk_ComputeTextLayout(). */
2444    int index;		    /* The index of the character whose bbox is
2445			     * desired. */
2446    int *xPtr, *yPtr;	    /* Filled with the upper-left hand corner, in
2447			     * pixels, of the bounding box for the character
2448			     * specified by index, if non-NULL. */
2449    int *widthPtr, *heightPtr;
2450			    /* Filled with the width and height of the
2451			     * bounding box for the character specified by
2452			     * index, if non-NULL. */
2453{
2454    TextLayout *layoutPtr;
2455    LayoutChunk *chunkPtr;
2456    int i, x, w;
2457    Tk_Font tkfont;
2458    TkFont *fontPtr;
2459    CONST char *end;
2460
2461    if (index < 0) {
2462	return 0;
2463    }
2464
2465    layoutPtr = (TextLayout *) layout;
2466    chunkPtr = layoutPtr->chunks;
2467    tkfont = layoutPtr->tkfont;
2468    fontPtr = (TkFont *) tkfont;
2469
2470    for (i = 0; i < layoutPtr->numChunks; i++) {
2471	if (chunkPtr->numDisplayChars < 0) {
2472	    if (index == 0) {
2473		x = chunkPtr->x;
2474		w = chunkPtr->totalWidth;
2475		goto check;
2476	    }
2477	} else if (index < chunkPtr->numChars) {
2478	    end = Tcl_UtfAtIndex(chunkPtr->start, index);
2479	    if (xPtr != NULL) {
2480		Tk_MeasureChars(tkfont, chunkPtr->start,
2481			end -  chunkPtr->start, -1, 0, &x);
2482		x += chunkPtr->x;
2483	    }
2484	    if (widthPtr != NULL) {
2485		Tk_MeasureChars(tkfont, end, Tcl_UtfNext(end) - end,
2486			-1, 0, &w);
2487	    }
2488	    goto check;
2489	}
2490	index -= chunkPtr->numChars;
2491	chunkPtr++;
2492    }
2493    if (index == 0) {
2494	/*
2495	 * Special case to get location just past last char in layout.
2496	 */
2497
2498	chunkPtr--;
2499	x = chunkPtr->x + chunkPtr->totalWidth;
2500	w = 0;
2501    } else {
2502	return 0;
2503    }
2504
2505    /*
2506     * Ensure that the bbox lies within the text layout.  This forces all
2507     * chars that extend off the right edge of the text layout to have
2508     * truncated widths, and all chars that are completely off the right
2509     * edge of the text layout to peg to the edge and have 0 width.
2510     */
2511    check:
2512    if (yPtr != NULL) {
2513	*yPtr = chunkPtr->y - fontPtr->fm.ascent;
2514    }
2515    if (heightPtr != NULL) {
2516	*heightPtr = fontPtr->fm.ascent + fontPtr->fm.descent;
2517    }
2518
2519    if (x > layoutPtr->width) {
2520	x = layoutPtr->width;
2521    }
2522    if (xPtr != NULL) {
2523	*xPtr = x;
2524    }
2525    if (widthPtr != NULL) {
2526	if (x + w > layoutPtr->width) {
2527	    w = layoutPtr->width - x;
2528	}
2529	*widthPtr = w;
2530    }
2531
2532    return 1;
2533}
2534
2535/*
2536 *---------------------------------------------------------------------------
2537 *
2538 * Tk_DistanceToTextLayout --
2539 *
2540 *	Computes the distance in pixels from the given point to the
2541 *	given text layout.  Non-displaying space characters that occur
2542 *	at the end of individual lines in the text layout are ignored
2543 *	for hit detection purposes.
2544 *
2545 * Results:
2546 *	The return value is 0 if the point (x, y) is inside the text
2547 *	layout.  If the point isn't inside the text layout then the
2548 *	return value is the distance in pixels from the point to the
2549 *	text item.
2550 *
2551 * Side effects:
2552 *	None.
2553 *
2554 *---------------------------------------------------------------------------
2555 */
2556
2557int
2558Tk_DistanceToTextLayout(layout, x, y)
2559    Tk_TextLayout layout;	/* Layout information, from a previous call
2560				 * to Tk_ComputeTextLayout(). */
2561    int x, y;			/* Coordinates of point to check, with
2562				 * respect to the upper-left corner of the
2563				 * text layout (in pixels). */
2564{
2565    int i, x1, x2, y1, y2, xDiff, yDiff, dist, minDist, ascent, descent;
2566    LayoutChunk *chunkPtr;
2567    TextLayout *layoutPtr;
2568    TkFont *fontPtr;
2569
2570    layoutPtr = (TextLayout *) layout;
2571    fontPtr = (TkFont *) layoutPtr->tkfont;
2572    ascent = fontPtr->fm.ascent;
2573    descent = fontPtr->fm.descent;
2574
2575    minDist = 0;
2576    chunkPtr = layoutPtr->chunks;
2577    for (i = 0; i < layoutPtr->numChunks; i++) {
2578	if (chunkPtr->start[0] == '\n') {
2579	    /*
2580	     * Newline characters are not counted when computing distance
2581	     * (but tab characters would still be considered).
2582	     */
2583
2584	    chunkPtr++;
2585	    continue;
2586	}
2587
2588	x1 = chunkPtr->x;
2589	y1 = chunkPtr->y - ascent;
2590	x2 = chunkPtr->x + chunkPtr->displayWidth;
2591	y2 = chunkPtr->y + descent;
2592
2593	if (x < x1) {
2594	    xDiff = x1 - x;
2595	} else if (x >= x2) {
2596	    xDiff = x - x2 + 1;
2597	} else {
2598	    xDiff = 0;
2599	}
2600
2601	if (y < y1) {
2602	    yDiff = y1 - y;
2603	} else if (y >= y2) {
2604	    yDiff = y - y2 + 1;
2605	} else {
2606	    yDiff = 0;
2607	}
2608	if ((xDiff == 0) && (yDiff == 0)) {
2609	    return 0;
2610	}
2611	dist = (int) hypot((double) xDiff, (double) yDiff);
2612	if ((dist < minDist) || (minDist == 0)) {
2613	    minDist = dist;
2614	}
2615	chunkPtr++;
2616    }
2617    return minDist;
2618}
2619
2620/*
2621 *---------------------------------------------------------------------------
2622 *
2623 * Tk_IntersectTextLayout --
2624 *
2625 *	Determines whether a text layout lies entirely inside,
2626 *	entirely outside, or overlaps a given rectangle.  Non-displaying
2627 *	space characters that occur at the end of individual lines in
2628 *	the text layout are ignored for intersection calculations.
2629 *
2630 * Results:
2631 *	The return value is -1 if the text layout is entirely outside of
2632 *	the rectangle, 0 if it overlaps, and 1 if it is entirely inside
2633 *	of the rectangle.
2634 *
2635 * Side effects:
2636 *	None.
2637 *
2638 *---------------------------------------------------------------------------
2639 */
2640
2641int
2642Tk_IntersectTextLayout(layout, x, y, width, height)
2643    Tk_TextLayout layout;	/* Layout information, from a previous call
2644				 * to Tk_ComputeTextLayout(). */
2645    int x, y;			/* Upper-left hand corner, in pixels, of
2646				 * rectangular area to compare with text
2647				 * layout.  Coordinates are with respect to
2648				 * the upper-left hand corner of the text
2649				 * layout itself. */
2650    int width, height;		/* The width and height of the above
2651				 * rectangular area, in pixels. */
2652{
2653    int result, i, x1, y1, x2, y2;
2654    TextLayout *layoutPtr;
2655    LayoutChunk *chunkPtr;
2656    TkFont *fontPtr;
2657    int left, top, right, bottom;
2658
2659    /*
2660     * Scan the chunks one at a time, seeing whether each is entirely in,
2661     * entirely out, or overlapping the rectangle.  If an overlap is
2662     * detected, return immediately; otherwise wait until all chunks have
2663     * been processed and see if they were all inside or all outside.
2664     */
2665
2666    layoutPtr = (TextLayout *) layout;
2667    chunkPtr = layoutPtr->chunks;
2668    fontPtr = (TkFont *) layoutPtr->tkfont;
2669
2670    left    = x;
2671    top	    = y;
2672    right   = x + width;
2673    bottom  = y + height;
2674
2675    result = 0;
2676    for (i = 0; i < layoutPtr->numChunks; i++) {
2677	if (chunkPtr->start[0] == '\n') {
2678	    /*
2679	     * Newline characters are not counted when computing area
2680	     * intersection (but tab characters would still be considered).
2681	     */
2682
2683	    chunkPtr++;
2684	    continue;
2685	}
2686
2687	x1 = chunkPtr->x;
2688	y1 = chunkPtr->y - fontPtr->fm.ascent;
2689	x2 = chunkPtr->x + chunkPtr->displayWidth;
2690	y2 = chunkPtr->y + fontPtr->fm.descent;
2691
2692	if ((right < x1) || (left >= x2)
2693		|| (bottom < y1) || (top >= y2)) {
2694	    if (result == 1) {
2695		return 0;
2696	    }
2697	    result = -1;
2698	} else if ((x1 < left) || (x2 >= right)
2699		|| (y1 < top) || (y2 >= bottom)) {
2700	    return 0;
2701	} else if (result == -1) {
2702	    return 0;
2703	} else {
2704	    result = 1;
2705	}
2706	chunkPtr++;
2707    }
2708    return result;
2709}
2710
2711/*
2712 *---------------------------------------------------------------------------
2713 *
2714 * Tk_TextLayoutToPostscript --
2715 *
2716 *	Outputs the contents of a text layout in Postscript format.
2717 *	The set of lines in the text layout will be rendered by the user
2718 *	supplied Postscript function.  The function should be of the form:
2719 *
2720 *	    justify x y string  function  --
2721 *
2722 *	Justify is -1, 0, or 1, depending on whether the following string
2723 *	should be left, center, or right justified, x and y is the
2724 *	location for the origin of the string, string is the sequence
2725 *	of characters to be printed, and function is the name of the
2726 *	caller-provided function; the function should leave nothing
2727 *	on the stack.
2728 *
2729 *	The meaning of the origin of the string (x and y) depends on
2730 *	the justification.  For left justification, x is where the
2731 *	left edge of the string should appear.  For center justification,
2732 *	x is where the center of the string should appear.  And for right
2733 *	justification, x is where the right edge of the string should
2734 *	appear.  This behavior is necessary because, for example, right
2735 *	justified text on the screen is justified with screen metrics.
2736 *	The same string needs to be justified with printer metrics on
2737 *	the printer to appear in the correct place with respect to other
2738 *	similarly justified strings.  In all circumstances, y is the
2739 *	location of the baseline for the string.
2740 *
2741 * Results:
2742 *	The interp's result is modified to hold the Postscript code that
2743 *	will render the text layout.
2744 *
2745 * Side effects:
2746 *	None.
2747 *
2748 *---------------------------------------------------------------------------
2749 */
2750
2751void
2752Tk_TextLayoutToPostscript(interp, layout)
2753    Tcl_Interp *interp;		/* Filled with Postscript code. */
2754    Tk_TextLayout layout;	/* The layout to be rendered. */
2755{
2756#define MAXUSE 128
2757    char buf[MAXUSE+30];
2758    LayoutChunk *chunkPtr;
2759    int i, j, used, c, baseline;
2760    Tcl_UniChar ch;
2761    CONST char *p, *last_p,*glyphname;
2762    TextLayout *layoutPtr;
2763    char uindex[5]="\0\0\0\0";
2764    char one_char[5];
2765    int charsize;
2766    int bytecount=0;
2767
2768    layoutPtr = (TextLayout *) layout;
2769    chunkPtr = layoutPtr->chunks;
2770    baseline = chunkPtr->y;
2771    used = 0;
2772    buf[used++] = '[';
2773    buf[used++] = '(';
2774    for (i = 0; i < layoutPtr->numChunks; i++) {
2775	if (baseline != chunkPtr->y) {
2776	    buf[used++] = ')';
2777	    buf[used++] = ']';
2778	    buf[used++] = '\n';
2779	    buf[used++] = '[';
2780	    buf[used++] = '(';
2781	    baseline = chunkPtr->y;
2782	}
2783	if (chunkPtr->numDisplayChars <= 0) {
2784	    if (chunkPtr->start[0] == '\t') {
2785		buf[used++] = '\\';
2786		buf[used++] = 't';
2787	    }
2788	} else {
2789	    p = chunkPtr->start;
2790	    for (j = 0; j < chunkPtr->numDisplayChars; j++) {
2791		/*
2792		 * INTL: For now we just treat the characters as binary
2793		 * data and display the lower byte.  Eventually this should
2794		 * be revised to handle international postscript fonts.
2795		 */
2796		last_p=p;
2797		p +=(charsize= Tcl_UtfToUniChar(p,&ch));
2798		Tcl_UtfToExternal(interp,NULL,last_p,charsize,0,NULL,one_char,4,
2799			NULL,&bytecount,NULL);
2800                if (bytecount == 1) {
2801		    c = UCHAR(one_char[0]);
2802		    /* c = UCHAR( ch & 0xFF) */;
2803		    if ((c == '(') || (c == ')') || (c == '\\') || (c < 0x20)
2804			    || (c >= UCHAR(0x7f))) {
2805			/*
2806			 * Tricky point:  the "03" is necessary in the sprintf
2807			 * below, so that a full three digits of octal are
2808			 * always generated.  Without the "03", a number
2809			 * following this sequence could be interpreted by
2810			 * Postscript as part of this sequence.
2811			 */
2812
2813			sprintf(buf + used, "\\%03o", c);
2814			used += 4;
2815		    } else {
2816			buf[used++] = c;
2817		    }
2818		} else {
2819		    /* This character doesn't belong to system character set.
2820		     * So, we must use full glyph name */
2821		    sprintf(uindex,"%04X",ch); /* endianness? */
2822		    if ((glyphname = Tcl_GetVar2( interp , "::tk::psglyphs",uindex,0))) {
2823			if (used > 0 && buf [used-1] == '(')
2824			    --used;
2825			else
2826			    buf[used++] = ')';
2827			buf[used++] = '/';
2828			while( (*glyphname) && (used < (MAXUSE+27)))
2829			    buf[used++] = *glyphname++ ;
2830			buf[used++] = '(';
2831		    }
2832
2833		}
2834		if (used >= MAXUSE) {
2835		    buf[used] = '\0';
2836		    Tcl_AppendResult(interp, buf, (char *) NULL);
2837		    used = 0;
2838		}
2839	    }
2840	}
2841	if (used >= MAXUSE) {
2842	    /*
2843	     * If there are a whole bunch of returns or tabs in a row,
2844	     * then buf[] could get filled up.
2845	     */
2846
2847	    buf[used] = '\0';
2848	    Tcl_AppendResult(interp, buf, (char *) NULL);
2849	    used = 0;
2850	}
2851	chunkPtr++;
2852    }
2853    buf[used++] = ')';
2854    buf[used++] = ']';
2855    buf[used++] = '\n';
2856    buf[used] = '\0';
2857    Tcl_AppendResult(interp, buf, (char *) NULL);
2858}
2859
2860/*
2861 *---------------------------------------------------------------------------
2862 *
2863 * ConfigAttributesObj --
2864 *
2865 *	Process command line options to fill in fields of a properly
2866 *	initialized font attributes structure.
2867 *
2868 * Results:
2869 *	A standard Tcl return value.  If TCL_ERROR is returned, an
2870 *	error message will be left in interp's result object (if non-NULL).
2871 *
2872 * Side effects:
2873 *	The fields of the font attributes structure get filled in with
2874 *	information from argc/argv.  If an error occurs while parsing,
2875 *	the font attributes structure will contain all modifications
2876 *	specified in the command line options up to the point of the
2877 *	error.
2878 *
2879 *---------------------------------------------------------------------------
2880 */
2881
2882static int
2883ConfigAttributesObj(interp, tkwin, objc, objv, faPtr)
2884    Tcl_Interp *interp;		/* Interp for error return, or NULL. */
2885    Tk_Window tkwin;		/* For display on which font will be used. */
2886    int objc;			/* Number of elements in argv. */
2887    Tcl_Obj *CONST objv[];	/* Command line options. */
2888    TkFontAttributes *faPtr;	/* Font attributes structure whose fields
2889				 * are to be modified.  Structure must already
2890				 * be properly initialized. */
2891{
2892    int i, n, index;
2893    Tcl_Obj *optionPtr, *valuePtr;
2894    char *value;
2895
2896    for (i = 0; i < objc; i += 2) {
2897	optionPtr = objv[i];
2898	valuePtr = objv[i + 1];
2899
2900	if (Tcl_GetIndexFromObj(interp, optionPtr, fontOpt, "option", 1,
2901		&index) != TCL_OK) {
2902	    return TCL_ERROR;
2903	}
2904	if ((i+2 >= objc) && (objc & 1)) {
2905	    /*
2906	     * This test occurs after Tcl_GetIndexFromObj() so that
2907	     * "font create xyz -xyz" will return the error message
2908	     * that "-xyz" is a bad option, rather than that the value
2909	     * for "-xyz" is missing.
2910	     */
2911
2912	    if (interp != NULL) {
2913		Tcl_AppendResult(interp, "value for \"",
2914			Tcl_GetString(optionPtr), "\" option missing",
2915			(char *) NULL);
2916	    }
2917	    return TCL_ERROR;
2918	}
2919
2920	switch (index) {
2921	    case FONT_FAMILY: {
2922		value = Tcl_GetString(valuePtr);
2923		faPtr->family = Tk_GetUid(value);
2924		break;
2925	    }
2926	    case FONT_SIZE: {
2927		if (Tcl_GetIntFromObj(interp, valuePtr, &n) != TCL_OK) {
2928		    return TCL_ERROR;
2929		}
2930		faPtr->size = n;
2931		break;
2932	    }
2933	    case FONT_WEIGHT: {
2934		n = TkFindStateNumObj(interp, optionPtr, weightMap, valuePtr);
2935		if (n == TK_FW_UNKNOWN) {
2936		    return TCL_ERROR;
2937		}
2938		faPtr->weight = n;
2939		break;
2940	    }
2941	    case FONT_SLANT: {
2942		n = TkFindStateNumObj(interp, optionPtr, slantMap, valuePtr);
2943		if (n == TK_FS_UNKNOWN) {
2944		    return TCL_ERROR;
2945		}
2946		faPtr->slant = n;
2947		break;
2948	    }
2949	    case FONT_UNDERLINE: {
2950		if (Tcl_GetBooleanFromObj(interp, valuePtr, &n) != TCL_OK) {
2951		    return TCL_ERROR;
2952		}
2953		faPtr->underline = n;
2954		break;
2955	    }
2956	    case FONT_OVERSTRIKE: {
2957		if (Tcl_GetBooleanFromObj(interp, valuePtr, &n) != TCL_OK) {
2958		    return TCL_ERROR;
2959		}
2960		faPtr->overstrike = n;
2961		break;
2962	    }
2963	}
2964    }
2965    return TCL_OK;
2966}
2967
2968/*
2969 *---------------------------------------------------------------------------
2970 *
2971 * GetAttributeInfoObj --
2972 *
2973 *	Return information about the font attributes as a Tcl list.
2974 *
2975 * Results:
2976 *	The return value is TCL_OK if the objPtr was non-NULL and
2977 *	specified a valid font attribute, TCL_ERROR otherwise.  If TCL_OK
2978 *	is returned, the interp's result object is modified to hold a
2979 *	description of either the current value of a single option, or a
2980 *	list of all options and their current values for the given font
2981 *	attributes.  If TCL_ERROR is returned, the interp's result is
2982 *	set to an error message describing that the objPtr did not refer
2983 *	to a valid option.
2984 *
2985 * Side effects:
2986 *	None.
2987 *
2988 *---------------------------------------------------------------------------
2989 */
2990
2991static int
2992GetAttributeInfoObj(interp, faPtr, objPtr)
2993    Tcl_Interp *interp;		  	/* Interp to hold result. */
2994    CONST TkFontAttributes *faPtr;	/* The font attributes to inspect. */
2995    Tcl_Obj *objPtr;		  	/* If non-NULL, indicates the single
2996					 * option whose value is to be
2997					 * returned. Otherwise information is
2998					 * returned for all options. */
2999{
3000    int i, index, start, end;
3001    CONST char *str;
3002    Tcl_Obj *optionPtr, *valuePtr, *resultPtr;
3003
3004    resultPtr = Tcl_GetObjResult(interp);
3005
3006    start = 0;
3007    end = FONT_NUMFIELDS;
3008    if (objPtr != NULL) {
3009	if (Tcl_GetIndexFromObj(interp, objPtr, fontOpt, "option", TCL_EXACT,
3010		&index) != TCL_OK) {
3011	    return TCL_ERROR;
3012	}
3013	start = index;
3014	end = index + 1;
3015    }
3016
3017    valuePtr = NULL;
3018    for (i = start; i < end; i++) {
3019	switch (i) {
3020	    case FONT_FAMILY:
3021		str = faPtr->family;
3022		valuePtr = Tcl_NewStringObj(str, ((str == NULL) ? 0 : -1));
3023		break;
3024
3025	    case FONT_SIZE:
3026		valuePtr = Tcl_NewIntObj(faPtr->size);
3027		break;
3028
3029	    case FONT_WEIGHT:
3030		str = TkFindStateString(weightMap, faPtr->weight);
3031		valuePtr = Tcl_NewStringObj(str, -1);
3032		break;
3033
3034	    case FONT_SLANT:
3035		str = TkFindStateString(slantMap, faPtr->slant);
3036		valuePtr = Tcl_NewStringObj(str, -1);
3037		break;
3038
3039	    case FONT_UNDERLINE:
3040		valuePtr = Tcl_NewBooleanObj(faPtr->underline);
3041		break;
3042
3043	    case FONT_OVERSTRIKE:
3044		valuePtr = Tcl_NewBooleanObj(faPtr->overstrike);
3045		break;
3046	}
3047	if (objPtr != NULL) {
3048	    Tcl_SetObjResult(interp, valuePtr);
3049	    return TCL_OK;
3050	}
3051	optionPtr = Tcl_NewStringObj(fontOpt[i], -1);
3052	Tcl_ListObjAppendElement(NULL, resultPtr, optionPtr);
3053	Tcl_ListObjAppendElement(NULL, resultPtr, valuePtr);
3054    }
3055    return TCL_OK;
3056}
3057
3058/*
3059 *---------------------------------------------------------------------------
3060 *
3061 * ParseFontNameObj --
3062 *
3063 *	Converts a object into a set of font attributes that can be used
3064 *	to construct a font.
3065 *
3066 *	The string rep of the object can be one of the following forms:
3067 *		XLFD (see X documentation)
3068 *		"family [size] [style1 [style2 ...]"
3069 *		"-option value [-option value ...]"
3070 *
3071 * Results:
3072 *	The return value is TCL_ERROR if the object was syntactically
3073 *	invalid.  In that case an error message is left in interp's
3074 *	result object.  Otherwise, fills the font attribute buffer with
3075 *	the values parsed from the string and returns TCL_OK;
3076 *
3077 * Side effects:
3078 *	None.
3079 *
3080 *---------------------------------------------------------------------------
3081 */
3082
3083static int
3084ParseFontNameObj(interp, tkwin, objPtr, faPtr)
3085    Tcl_Interp *interp;		/* Interp for error return, or NULL if no
3086				 * error messages are to be generated. */
3087    Tk_Window tkwin;		/* For display on which font is used. */
3088    Tcl_Obj *objPtr;		/* Parseable font description object. */
3089    TkFontAttributes *faPtr;	/* Filled with attributes parsed from font
3090				 * name.  Any attributes that were not
3091				 * specified in font name are filled with
3092				 * default values. */
3093{
3094    char *dash;
3095    int objc, result, i, n;
3096    Tcl_Obj **objv;
3097    char *string;
3098
3099    TkInitFontAttributes(faPtr);
3100
3101    string = Tcl_GetString(objPtr);
3102    if (*string == '-') {
3103	/*
3104	 * This may be an XLFD or an "-option value" string.
3105	 *
3106	 * If the string begins with "-*" or a "-foundry-family-*" pattern,
3107	 * then consider it an XLFD.
3108	 */
3109
3110	if (string[1] == '*') {
3111	    goto xlfd;
3112	}
3113	dash = strchr(string + 1, '-');
3114	if ((dash != NULL)
3115		&& (!isspace(UCHAR(dash[-1])))) { /* INTL: ISO space */
3116	    goto xlfd;
3117	}
3118
3119	if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
3120	    return TCL_ERROR;
3121	}
3122
3123	return ConfigAttributesObj(interp, tkwin, objc, objv, faPtr);
3124    }
3125
3126    if (*string == '*') {
3127	/*
3128	 * This is appears to be an XLFD.  Under Unix, all valid XLFDs were
3129	 * already handled by TkpGetNativeFont.  If we are here, either we
3130	 * have something that initially looks like an XLFD but isn't or we
3131	 * have encountered an XLFD on Windows or Mac.
3132	 */
3133
3134    xlfd:
3135	result = TkFontParseXLFD(string, faPtr, NULL);
3136	if (result == TCL_OK) {
3137	    return TCL_OK;
3138	}
3139    }
3140
3141    /*
3142     * Wasn't an XLFD or "-option value" string.  Try it as a
3143     * "font size style" list.
3144     */
3145
3146    if ((Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv) != TCL_OK)
3147	    || (objc < 1)) {
3148	if (interp != NULL) {
3149	    Tcl_AppendResult(interp, "font \"", string, "\" doesn't exist",
3150		    (char *) NULL);
3151	}
3152	return TCL_ERROR;
3153    }
3154
3155    faPtr->family = Tk_GetUid(Tcl_GetString(objv[0]));
3156    if (objc > 1) {
3157	if (Tcl_GetIntFromObj(interp, objv[1], &n) != TCL_OK) {
3158	    return TCL_ERROR;
3159	}
3160	faPtr->size = n;
3161    }
3162
3163    i = 2;
3164    if (objc == 3) {
3165	if (Tcl_ListObjGetElements(interp, objv[2], &objc, &objv) != TCL_OK) {
3166	    return TCL_ERROR;
3167	}
3168	i = 0;
3169    }
3170    for ( ; i < objc; i++) {
3171	n = TkFindStateNumObj(NULL, NULL, weightMap, objv[i]);
3172	if (n != TK_FW_UNKNOWN) {
3173	    faPtr->weight = n;
3174	    continue;
3175	}
3176	n = TkFindStateNumObj(NULL, NULL, slantMap, objv[i]);
3177	if (n != TK_FS_UNKNOWN) {
3178	    faPtr->slant = n;
3179	    continue;
3180	}
3181	n = TkFindStateNumObj(NULL, NULL, underlineMap, objv[i]);
3182	if (n != 0) {
3183	    faPtr->underline = n;
3184	    continue;
3185	}
3186	n = TkFindStateNumObj(NULL, NULL, overstrikeMap, objv[i]);
3187	if (n != 0) {
3188	    faPtr->overstrike = n;
3189	    continue;
3190	}
3191
3192	/*
3193	 * Unknown style.
3194	 */
3195
3196	if (interp != NULL) {
3197	    Tcl_AppendResult(interp, "unknown font style \"",
3198		    Tcl_GetString(objv[i]), "\"", (char *) NULL);
3199	}
3200	return TCL_ERROR;
3201    }
3202    return TCL_OK;
3203}
3204
3205/*
3206 *---------------------------------------------------------------------------
3207 *
3208 * NewChunk --
3209 *
3210 *	Helper function for Tk_ComputeTextLayout().  Encapsulates a
3211 *	measured set of characters in a chunk that can be quickly
3212 *	drawn.
3213 *
3214 * Results:
3215 *	A pointer to the new chunk in the text layout.
3216 *
3217 * Side effects:
3218 *	The text layout is reallocated to hold more chunks as necessary.
3219 *
3220 *	Currently, Tk_ComputeTextLayout() stores contiguous ranges of
3221 *	"normal" characters in a chunk, along with individual tab
3222 *	and newline chars in their own chunks.  All characters in the
3223 *	text layout are accounted for.
3224 *
3225 *---------------------------------------------------------------------------
3226 */
3227static LayoutChunk *
3228NewChunk(layoutPtrPtr, maxPtr, start, numBytes, curX, newX, y)
3229    TextLayout **layoutPtrPtr;
3230    int *maxPtr;
3231    CONST char *start;
3232    int numBytes;
3233    int curX;
3234    int newX;
3235    int y;
3236{
3237    TextLayout *layoutPtr;
3238    LayoutChunk *chunkPtr;
3239    int maxChunks, numChars;
3240    size_t s;
3241
3242    layoutPtr = *layoutPtrPtr;
3243    maxChunks = *maxPtr;
3244    if (layoutPtr->numChunks == maxChunks) {
3245	maxChunks *= 2;
3246	s = sizeof(TextLayout) + ((maxChunks - 1) * sizeof(LayoutChunk));
3247	layoutPtr = (TextLayout *) ckrealloc((char *) layoutPtr, s);
3248
3249	*layoutPtrPtr = layoutPtr;
3250	*maxPtr = maxChunks;
3251    }
3252    numChars = Tcl_NumUtfChars(start, numBytes);
3253    chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks];
3254    chunkPtr->start		= start;
3255    chunkPtr->numBytes		= numBytes;
3256    chunkPtr->numChars		= numChars;
3257    chunkPtr->numDisplayChars	= numChars;
3258    chunkPtr->x			= curX;
3259    chunkPtr->y			= y;
3260    chunkPtr->totalWidth	= newX - curX;
3261    chunkPtr->displayWidth	= newX - curX;
3262    layoutPtr->numChunks++;
3263
3264    return chunkPtr;
3265}
3266
3267/*
3268 *---------------------------------------------------------------------------
3269 *
3270 * TkFontParseXLFD --
3271 *
3272 *	Break up a fully specified XLFD into a set of font attributes.
3273 *
3274 * Results:
3275 *	Return value is TCL_ERROR if string was not a fully specified XLFD.
3276 *	Otherwise, fills font attribute buffer with the values parsed
3277 *	from the XLFD and returns TCL_OK.
3278 *
3279 * Side effects:
3280 *	None.
3281 *
3282 *---------------------------------------------------------------------------
3283 */
3284
3285int
3286TkFontParseXLFD(string, faPtr, xaPtr)
3287    CONST char *string;		/* Parseable font description string. */
3288    TkFontAttributes *faPtr;	/* Filled with attributes parsed from font
3289				 * name.  Any attributes that were not
3290				 * specified in font name are filled with
3291				 * default values. */
3292    TkXLFDAttributes *xaPtr;	/* Filled with X-specific attributes parsed
3293				 * from font name.  Any attributes that were
3294				 * not specified in font name are filled with
3295				 * default values.  May be NULL if such
3296				 * information is not desired. */
3297{
3298    char *src;
3299    CONST char *str;
3300    int i, j;
3301    char *field[XLFD_NUMFIELDS + 2];
3302    Tcl_DString ds;
3303    TkXLFDAttributes xa;
3304
3305    if (xaPtr == NULL) {
3306	xaPtr = &xa;
3307    }
3308    TkInitFontAttributes(faPtr);
3309    TkInitXLFDAttributes(xaPtr);
3310
3311    memset(field, '\0', sizeof(field));
3312
3313    str = string;
3314    if (*str == '-') {
3315	str++;
3316    }
3317
3318    Tcl_DStringInit(&ds);
3319    Tcl_DStringAppend(&ds, (char *) str, -1);
3320    src = Tcl_DStringValue(&ds);
3321
3322    field[0] = src;
3323    for (i = 0; *src != '\0'; src++) {
3324	if (!(*src & 0x80)
3325		&& Tcl_UniCharIsUpper(UCHAR(*src))) {
3326	    *src = (char) Tcl_UniCharToLower(UCHAR(*src));
3327	}
3328	if (*src == '-') {
3329	    i++;
3330	    if (i == XLFD_NUMFIELDS) {
3331		continue;
3332	    }
3333	    *src = '\0';
3334	    field[i] = src + 1;
3335	    if (i > XLFD_NUMFIELDS) {
3336		break;
3337	    }
3338	}
3339    }
3340
3341    /*
3342     * An XLFD of the form -adobe-times-medium-r-*-12-*-* is pretty common,
3343     * but it is (strictly) malformed, because the first * is eliding both
3344     * the Setwidth and the Addstyle fields.  If the Addstyle field is a
3345     * number, then assume the above incorrect form was used and shift all
3346     * the rest of the fields right by one, so the number gets interpreted
3347     * as a pixelsize.  This fix is so that we don't get a million reports
3348     * that "it works under X (as a native font name), but gives a syntax
3349     * error under Windows (as a parsed set of attributes)".
3350     */
3351
3352    if ((i > XLFD_ADD_STYLE) && (FieldSpecified(field[XLFD_ADD_STYLE]))) {
3353	if (atoi(field[XLFD_ADD_STYLE]) != 0) {
3354	    for (j = XLFD_NUMFIELDS - 1; j >= XLFD_ADD_STYLE; j--) {
3355		field[j + 1] = field[j];
3356	    }
3357	    field[XLFD_ADD_STYLE] = NULL;
3358	    i++;
3359	}
3360    }
3361
3362    /*
3363     * Bail if we don't have enough of the fields (up to pointsize).
3364     */
3365
3366    if (i < XLFD_FAMILY) {
3367	Tcl_DStringFree(&ds);
3368	return TCL_ERROR;
3369    }
3370
3371    if (FieldSpecified(field[XLFD_FOUNDRY])) {
3372	xaPtr->foundry = Tk_GetUid(field[XLFD_FOUNDRY]);
3373    }
3374
3375    if (FieldSpecified(field[XLFD_FAMILY])) {
3376	faPtr->family = Tk_GetUid(field[XLFD_FAMILY]);
3377    }
3378    if (FieldSpecified(field[XLFD_WEIGHT])) {
3379	faPtr->weight = TkFindStateNum(NULL, NULL, xlfdWeightMap,
3380		field[XLFD_WEIGHT]);
3381    }
3382    if (FieldSpecified(field[XLFD_SLANT])) {
3383	xaPtr->slant = TkFindStateNum(NULL, NULL, xlfdSlantMap,
3384		field[XLFD_SLANT]);
3385	if (xaPtr->slant == TK_FS_ROMAN) {
3386	    faPtr->slant = TK_FS_ROMAN;
3387	} else {
3388	    faPtr->slant = TK_FS_ITALIC;
3389	}
3390    }
3391    if (FieldSpecified(field[XLFD_SETWIDTH])) {
3392	xaPtr->setwidth = TkFindStateNum(NULL, NULL, xlfdSetwidthMap,
3393		field[XLFD_SETWIDTH]);
3394    }
3395
3396    /* XLFD_ADD_STYLE ignored. */
3397
3398    /*
3399     * Pointsize in tenths of a point, but treat it as tenths of a pixel
3400     * for historical compatibility.
3401     */
3402
3403    faPtr->size = 12;
3404
3405    if (FieldSpecified(field[XLFD_POINT_SIZE])) {
3406	if (field[XLFD_POINT_SIZE][0] == '[') {
3407	    /*
3408	     * Some X fonts have the point size specified as follows:
3409	     *
3410	     *	    [ N1 N2 N3 N4 ]
3411	     *
3412	     * where N1 is the point size (in points, not decipoints!), and
3413	     * N2, N3, and N4 are some additional numbers that I don't know
3414	     * the purpose of, so I ignore them.
3415	     */
3416
3417	    faPtr->size = atoi(field[XLFD_POINT_SIZE] + 1);
3418	} else if (Tcl_GetInt(NULL, field[XLFD_POINT_SIZE],
3419		&faPtr->size) == TCL_OK) {
3420	    faPtr->size /= 10;
3421	} else {
3422	    return TCL_ERROR;
3423	}
3424    }
3425
3426    /*
3427     * Pixel height of font.  If specified, overrides pointsize.
3428     */
3429
3430    if (FieldSpecified(field[XLFD_PIXEL_SIZE])) {
3431	if (field[XLFD_PIXEL_SIZE][0] == '[') {
3432	    /*
3433	     * Some X fonts have the pixel size specified as follows:
3434	     *
3435	     *	    [ N1 N2 N3 N4 ]
3436	     *
3437	     * where N1 is the pixel size, and where N2, N3, and N4
3438	     * are some additional numbers that I don't know
3439	     * the purpose of, so I ignore them.
3440	     */
3441
3442	    faPtr->size = atoi(field[XLFD_PIXEL_SIZE] + 1);
3443	} else if (Tcl_GetInt(NULL, field[XLFD_PIXEL_SIZE],
3444		&faPtr->size) != TCL_OK) {
3445	    return TCL_ERROR;
3446	}
3447    }
3448
3449    faPtr->size = -faPtr->size;
3450
3451    /* XLFD_RESOLUTION_X ignored. */
3452
3453    /* XLFD_RESOLUTION_Y ignored. */
3454
3455    /* XLFD_SPACING ignored. */
3456
3457    /* XLFD_AVERAGE_WIDTH ignored. */
3458
3459    if (FieldSpecified(field[XLFD_CHARSET])) {
3460	xaPtr->charset = Tk_GetUid(field[XLFD_CHARSET]);
3461    } else {
3462	xaPtr->charset = Tk_GetUid("iso8859-1");
3463    }
3464    Tcl_DStringFree(&ds);
3465    return TCL_OK;
3466}
3467
3468/*
3469 *---------------------------------------------------------------------------
3470 *
3471 * FieldSpecified --
3472 *
3473 *	Helper function for TkParseXLFD().  Determines if a field in the
3474 *	XLFD was set to a non-null, non-don't-care value.
3475 *
3476 * Results:
3477 *	The return value is 0 if the field in the XLFD was not set and
3478 *	should be ignored, non-zero otherwise.
3479 *
3480 * Side effects:
3481 *	None.
3482 *
3483 *---------------------------------------------------------------------------
3484 */
3485
3486static int
3487FieldSpecified(field)
3488    CONST char *field;	/* The field of the XLFD to check.  Strictly
3489			 * speaking, only when the string is "*" does it mean
3490			 * don't-care.  However, an unspecified or question
3491			 * mark is also interpreted as don't-care. */
3492{
3493    char ch;
3494
3495    if (field == NULL) {
3496	return 0;
3497    }
3498    ch = field[0];
3499    return (ch != '*' && ch != '?');
3500}
3501
3502/*
3503 *---------------------------------------------------------------------------
3504 *
3505 * TkFontGetPixels --
3506 *
3507 *	Given a font size specification (as described in the TkFontAttributes
3508 *	structure) return the number of pixels it represents.
3509 *
3510 * Results:
3511 *	As above.
3512 *
3513 * Side effects:
3514 *	None.
3515 *
3516 *---------------------------------------------------------------------------
3517 */
3518
3519int
3520TkFontGetPixels(tkwin, size)
3521    Tk_Window tkwin;		/* For point->pixel conversion factor. */
3522    int size;			/* Font size. */
3523{
3524    double d;
3525
3526    if (size < 0) {
3527	return -size;
3528    }
3529
3530    d = size * 25.4 / 72.0;
3531    d *= WidthOfScreen(Tk_Screen(tkwin));
3532    d /= WidthMMOfScreen(Tk_Screen(tkwin));
3533    return (int) (d + 0.5);
3534}
3535
3536/*
3537 *---------------------------------------------------------------------------
3538 *
3539 * TkFontGetPoints --
3540 *
3541 *	Given a font size specification (as described in the TkFontAttributes
3542 *	structure) return the number of points it represents.
3543 *
3544 * Results:
3545 *	As above.
3546 *
3547 * Side effects:
3548 *	None.
3549 *
3550 *---------------------------------------------------------------------------
3551 */
3552
3553int
3554TkFontGetPoints(tkwin, size)
3555    Tk_Window tkwin;		/* For pixel->point conversion factor. */
3556    int size;			/* Font size. */
3557{
3558    double d;
3559
3560    if (size >= 0) {
3561	return size;
3562    }
3563
3564    d = -size * 72.0 / 25.4;
3565    d *= WidthMMOfScreen(Tk_Screen(tkwin));
3566    d /= WidthOfScreen(Tk_Screen(tkwin));
3567    return (int) (d + 0.5);
3568}
3569
3570/*
3571 *-------------------------------------------------------------------------
3572 *
3573 * TkFontGetAliasList --
3574 *
3575 *	Given a font name, find the list of all aliases for that font
3576 *	name.  One of the names in this list will probably be the name
3577 *	that this platform expects when asking for the font.
3578 *
3579 * Results:
3580 *	As above.  The return value is NULL if the font name has no
3581 *	aliases.
3582 *
3583 * Side effects:
3584 *	None.
3585 *
3586 *-------------------------------------------------------------------------
3587 */
3588
3589char **
3590TkFontGetAliasList(faceName)
3591    CONST char *faceName;	/* Font name to test for aliases. */
3592{
3593    int i, j;
3594
3595    for (i = 0; fontAliases[i] != NULL; i++) {
3596	for (j = 0; fontAliases[i][j] != NULL; j++) {
3597	    if (strcasecmp(faceName, fontAliases[i][j]) == 0) {
3598		return fontAliases[i];
3599	    }
3600	}
3601    }
3602    return NULL;
3603}
3604
3605/*
3606 *-------------------------------------------------------------------------
3607 *
3608 * TkFontGetFallbacks --
3609 *
3610 *	Get the list of font fallbacks that the platform-specific code
3611 *	can use to try to find the closest matching font the name
3612 *	requested.
3613 *
3614 * Results:
3615 *	As above.
3616 *
3617 * Side effects:
3618 *	None.
3619 *
3620 *-------------------------------------------------------------------------
3621 */
3622
3623char ***
3624TkFontGetFallbacks()
3625{
3626    return fontFallbacks;
3627}
3628
3629/*
3630 *-------------------------------------------------------------------------
3631 *
3632 * TkFontGetGlobalClass --
3633 *
3634 *	Get the list of fonts to try if the requested font name does not
3635 *	exist and no fallbacks for that font name could be used either.
3636 *	The names in this list are considered preferred over all the other
3637 *	font names in the system when looking for a last-ditch fallback.
3638 *
3639 * Results:
3640 *	As above.
3641 *
3642 * Side effects:
3643 *	None.
3644 *
3645 *-------------------------------------------------------------------------
3646 */
3647
3648char **
3649TkFontGetGlobalClass()
3650{
3651    return globalFontClass;
3652}
3653
3654/*
3655 *-------------------------------------------------------------------------
3656 *
3657 * TkFontGetSymbolClass --
3658 *
3659 *	Get the list of fonts that are symbolic; used if the operating
3660 *	system cannot apriori identify symbolic fonts on its own.
3661 *
3662 * Results:
3663 *	As above.
3664 *
3665 * Side effects:
3666 *	None.
3667 *
3668 *-------------------------------------------------------------------------
3669 */
3670
3671char **
3672TkFontGetSymbolClass()
3673{
3674    return symbolClass;
3675}
3676
3677/*
3678 *----------------------------------------------------------------------
3679 *
3680 * TkDebugFont --
3681 *
3682 *	This procedure returns debugging information about a font.
3683 *
3684 * Results:
3685 *	The return value is a list with one sublist for each TkFont
3686 *	corresponding to "name".  Each sublist has two elements that
3687 *	contain the resourceRefCount and objRefCount fields from the
3688 *	TkFont structure.
3689 *
3690 * Side effects:
3691 *	None.
3692 *
3693 *----------------------------------------------------------------------
3694 */
3695
3696Tcl_Obj *
3697TkDebugFont(tkwin, name)
3698    Tk_Window tkwin;		/* The window in which the font will be
3699				 * used (not currently used). */
3700    char *name;			/* Name of the desired color. */
3701{
3702    TkFont *fontPtr;
3703    Tcl_HashEntry *hashPtr;
3704    Tcl_Obj *resultPtr, *objPtr;
3705
3706    resultPtr = Tcl_NewObj();
3707    hashPtr = Tcl_FindHashEntry(
3708	    &((TkWindow *) tkwin)->mainPtr->fontInfoPtr->fontCache, name);
3709    if (hashPtr != NULL) {
3710	fontPtr = (TkFont *) Tcl_GetHashValue(hashPtr);
3711	if (fontPtr == NULL) {
3712	    panic("TkDebugFont found empty hash table entry");
3713	}
3714	for ( ; (fontPtr != NULL); fontPtr = fontPtr->nextPtr) {
3715	    objPtr = Tcl_NewObj();
3716	    Tcl_ListObjAppendElement(NULL, objPtr,
3717		    Tcl_NewIntObj(fontPtr->resourceRefCount));
3718	    Tcl_ListObjAppendElement(NULL, objPtr,
3719		    Tcl_NewIntObj(fontPtr->objRefCount));
3720	    Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
3721	}
3722    }
3723    return resultPtr;
3724}
3725
3726/*
3727 *----------------------------------------------------------------------
3728 *
3729 * TkFontGetFirstTextLayout --
3730 *
3731 *	This procedure returns the first chunk of a Tk_TextLayout,
3732 *	i.e. until the first font change on the first line (or the
3733 *	whole first line if there is no such font change).
3734 *
3735 * Results:
3736 *	The return value is the byte length of the chunk, the chunk
3737 *	itself is copied into dst and its Tk_Font into font.
3738 *
3739 * Side effects:
3740 *	None.
3741 *
3742 *----------------------------------------------------------------------
3743 */
3744
3745int
3746TkFontGetFirstTextLayout(
3747    Tk_TextLayout layout,	/* Layout information, from a previous call
3748				 * to Tk_ComputeTextLayout(). */
3749    Tk_Font * font,
3750    char    * dst)
3751{
3752    TextLayout  *layoutPtr;
3753    LayoutChunk *chunkPtr;
3754    int numBytesInChunk;
3755
3756    layoutPtr = (TextLayout *)layout;
3757    if ((layoutPtr==NULL)
3758            || (layoutPtr->numChunks==0)
3759            || (layoutPtr->chunks->numDisplayChars <= 0)) {
3760        dst[0] = '\0';
3761        return 0;
3762    }
3763    chunkPtr = layoutPtr->chunks;
3764    numBytesInChunk = chunkPtr->numBytes;
3765    strncpy(dst, chunkPtr->start, (size_t) numBytesInChunk);
3766    *font = layoutPtr->tkfont;
3767    return numBytesInChunk;
3768}
3769