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