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