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