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