1 /*
2  * tkFont.c --
3  *
4  *	This file maintains a database of looked-up fonts for the Tk
5  *	toolkit, in order to avoid round-trips to the server to map
6  *	font names to XFontStructs.  It also provides several utility
7  *	procedures for measuring and displaying text.
8  *
9  * Copyright (c) 1990-1994 The Regents of the University of California.
10  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
11  *
12  * See the file "license.terms" for information on usage and redistribution
13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14  *
15  * SCCS: @(#) tkFont.c 1.38 96/02/15 18:53:31
16  */
17 
18 #include "tkInt.h"
19 
20 /*
21  * This module caches extra information about fonts in addition to
22  * what X already provides.  The extra information is used by the
23  * TkMeasureChars procedure, and consists of two parts:  a type and
24  * a width.  The type is one of the following:
25  *
26  * NORMAL:		Standard character.
27  * TAB:			Tab character:  output enough space to
28  *			get to next tab stop.
29  * NEWLINE:		Newline character:  don't output anything more
30  *			on this line (character has infinite width).
31  * REPLACE:		This character doesn't print:  instead of
32  *			displaying character, display a replacement
33  *			sequence like "\n" (for those characters where
34  *			ANSI C defines such a sequence) or a sequence
35  *			of the form "\xdd" where dd is the hex equivalent
36  *			of the character.
37  * SKIP:		Don't display anything for this character.  This
38  *			is only used where the font doesn't contain
39  *			all the characters needed to generate
40  *			replacement sequences.
41  * The width gives the total width of the displayed character or
42  * sequence:  for replacement sequences, it gives the width of the
43  * sequence.
44  */
45 
46 #define NORMAL		1
47 #define TAB		2
48 #define NEWLINE		3
49 #define REPLACE		4
50 #define SKIP		5
51 
52 /*
53  * One of the following data structures exists for each font that is
54  * currently active.  The structure is indexed with two hash tables,
55  * one based on font name and one based on XFontStruct address.
56  */
57 
58 typedef struct {
59     XFontStruct *fontStructPtr;	/* X information about font. */
60     Display *display;		/* Display to which font belongs. */
61     int refCount;		/* Number of active uses of this font. */
62     char *types;		/* Malloc'ed array giving types of all
63 				 * chars in the font (may be NULL). */
64     unsigned char *widths;	/* Malloc'ed array giving widths of all
65 				 * chars in the font (may be NULL). */
66     int tabWidth;		/* Width of tabs in this font. */
67     Tcl_HashEntry *nameHashPtr;	/* Entry in name-based hash table (needed
68 				 * when deleting this structure). */
69 } TkFont;
70 
71 /*
72  * Hash table for name -> TkFont mapping, and key structure used to
73  * index into that table:
74  */
75 
76 static Tcl_HashTable nameTable;
77 typedef struct {
78     Tk_Uid name;		/* Name of font. */
79     Display *display;		/* Display for which font is valid. */
80 } NameKey;
81 
82 /*
83  * Hash table for font struct -> TkFont mapping. This table is
84  * indexed by the XFontStruct address.
85  */
86 
87 static Tcl_HashTable fontTable;
88 
89 static int initialized = 0;	/* 0 means static structures haven't been
90 				 * initialized yet. */
91 
92 /*
93  * To speed up TkMeasureChars, the variables below keep the last
94  * mapping from (XFontStruct *) to (TkFont *).
95  */
96 
97 static TkFont *lastFontPtr = NULL;
98 static XFontStruct *lastFontStructPtr = NULL;
99 
100 /*
101  * Characters used when displaying control sequences.
102  */
103 
104 static char hexChars[] = "0123456789abcdefxtnvr\\";
105 
106 /*
107  * The following table maps some control characters to sequences
108  * like '\n' rather than '\x10'.  A zero entry in the table means
109  * no such mapping exists, and the table only maps characters
110  * less than 0x10.
111  */
112 
113 static char mapChars[] = {
114     0, 0, 0, 0, 0, 0, 0,
115     'a', 'b', 't', 'n', 'v', 'f', 'r',
116     0
117 };
118 
119 /*
120  * Forward declarations for procedures defined in this file:
121  */
122 
123 static void		FontInit _ANSI_ARGS_((void));
124 static void		SetFontMetrics _ANSI_ARGS_((TkFont *fontPtr));
125 
126 /*
127  *----------------------------------------------------------------------
128  *
129  * Tk_GetFontStruct --
130  *
131  *	Given a string name for a font, map the name to an XFontStruct
132  *	describing the font.
133  *
134  * Results:
135  *	The return value is normally a pointer to the font description
136  *	for the desired font.  If an error occurs in mapping the string
137  *	to a font, then an error message will be left in interp->result
138  *	and NULL will be returned.
139  *
140  * Side effects:
141  *	The font is added to an internal database with a reference count.
142  *	For each call to this procedure, there should eventually be a call
143  *	to Tk_FreeFontStruct, so that the database is cleaned up when fonts
144  *	aren't in use anymore.
145  *
146  *----------------------------------------------------------------------
147  */
148 
149 XFontStruct *
Tk_GetFontStruct(interp,tkwin,name)150 Tk_GetFontStruct(interp, tkwin, name)
151     Tcl_Interp *interp;		/* Place to leave error message if
152 				 * font can't be found. */
153     Tk_Window tkwin;		/* Window in which font will be used. */
154     Tk_Uid name;		/* Name of font (in form suitable for
155 				 * passing to XLoadQueryFont). */
156 {
157     NameKey nameKey;
158     Tcl_HashEntry *nameHashPtr, *fontHashPtr;
159     int new;
160     register TkFont *fontPtr;
161     XFontStruct *fontStructPtr;
162 
163     if (!initialized) {
164 	FontInit();
165     }
166 
167     /*
168      * First, check to see if there's already a mapping for this font
169      * name.
170      */
171 
172     nameKey.name = name;
173     nameKey.display = Tk_Display(tkwin);
174     nameHashPtr = Tcl_CreateHashEntry(&nameTable, (char *) &nameKey, &new);
175     if (!new) {
176 	fontPtr = (TkFont *) Tcl_GetHashValue(nameHashPtr);
177 	fontPtr->refCount++;
178 	return fontPtr->fontStructPtr;
179     }
180 
181     /*
182      * The name isn't currently known.  Map from the name to a font, and
183      * add a new structure to the database.
184      */
185 
186     fontStructPtr = XLoadQueryFont(nameKey.display, name);
187     if (fontStructPtr == NULL) {
188 	Tcl_DeleteHashEntry(nameHashPtr);
189 	Tcl_AppendResult(interp, "font \"", name, "\" doesn't exist",
190 		(char *) NULL);
191 	return NULL;
192     }
193     fontPtr = (TkFont *) ckalloc(sizeof(TkFont));
194     fontPtr->display = nameKey.display;
195     fontPtr->fontStructPtr = fontStructPtr;
196     fontPtr->refCount = 1;
197     fontPtr->types = NULL;
198     fontPtr->widths = NULL;
199     fontPtr->nameHashPtr = nameHashPtr;
200     fontHashPtr = Tcl_CreateHashEntry(&fontTable, (char *) fontStructPtr, &new);
201     if (!new) {
202 	panic("XFontStruct already registered in Tk_GetFontStruct");
203     }
204     Tcl_SetHashValue(nameHashPtr, fontPtr);
205     Tcl_SetHashValue(fontHashPtr, fontPtr);
206     return fontPtr->fontStructPtr;
207 }
208 
209 /*
210  *--------------------------------------------------------------
211  *
212  * Tk_NameOfFontStruct --
213  *
214  *	Given a font, return a textual string identifying it.
215  *
216  * Results:
217  *	If font was created by Tk_GetFontStruct, then the return
218  *	value is the "string" that was used to create it.
219  *	Otherwise the return value is a string giving the X
220  *	identifier for the font.  The storage for the returned
221  *	string is only guaranteed to persist up until the next
222  *	call to this procedure.
223  *
224  * Side effects:
225  *	None.
226  *
227  *--------------------------------------------------------------
228  */
229 
230 char *
Tk_NameOfFontStruct(fontStructPtr)231 Tk_NameOfFontStruct(fontStructPtr)
232     XFontStruct *fontStructPtr;		/* Font whose name is desired. */
233 {
234     Tcl_HashEntry *fontHashPtr;
235     TkFont *fontPtr;
236     void *ptr;
237     static char string[20];
238 
239     if (!initialized) {
240 	printid:
241 	sprintf(string, "font id 0x%x", (unsigned int) fontStructPtr->fid);
242 	return string;
243     }
244     fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
245     if (fontHashPtr == NULL) {
246 	goto printid;
247     }
248     fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
249     ptr = fontPtr->nameHashPtr->key.words;
250     return ((NameKey *) ptr)->name;
251 }
252 
253 /*
254  *----------------------------------------------------------------------
255  *
256  * Tk_FreeFontStruct --
257  *
258  *	This procedure is called to release a font allocated by
259  *	Tk_GetFontStruct.
260  *
261  * Results:
262  *	None.
263  *
264  * Side effects:
265  *	The reference count associated with font is decremented, and
266  *	the font is officially deallocated if no-one is using it
267  *	anymore.
268  *
269  *----------------------------------------------------------------------
270  */
271 
272 void
Tk_FreeFontStruct(fontStructPtr)273 Tk_FreeFontStruct(fontStructPtr)
274     XFontStruct *fontStructPtr;	/* Font to be released. */
275 {
276     Tcl_HashEntry *fontHashPtr;
277     register TkFont *fontPtr;
278 
279     if (!initialized) {
280 	panic("Tk_FreeFontStruct called before Tk_GetFontStruct");
281     }
282 
283     fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
284     if (fontHashPtr == NULL) {
285 	panic("Tk_FreeFontStruct received unknown font argument");
286     }
287     fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
288     fontPtr->refCount--;
289     if (fontPtr->refCount == 0) {
290 	/*
291 	 * We really should call Tk_FreeXId below to release the font's
292 	 * resource identifier, but this seems to cause problems on
293 	 * many X servers (as of 5/1/94) where the font resource isn't
294 	 * really released, which can cause the wrong font to be used
295 	 * later on.  So, don't release the resource id after all, even
296 	 * though this results in an id leak.
297 	 *
298 	 * Tk_FreeXId(fontPtr->display, (XID) fontPtr->fontStructPtr->fid);
299 	 */
300 
301 	XFreeFont(fontPtr->display, fontPtr->fontStructPtr);
302 	Tcl_DeleteHashEntry(fontPtr->nameHashPtr);
303 	Tcl_DeleteHashEntry(fontHashPtr);
304 	if (fontPtr->types != NULL) {
305 	    ckfree(fontPtr->types);
306 	}
307 	if (fontPtr->widths != NULL) {
308 	    ckfree((char *) fontPtr->widths);
309 	}
310 	ckfree((char *) fontPtr);
311 	lastFontStructPtr = NULL;
312     }
313 }
314 
315 /*
316  *----------------------------------------------------------------------
317  *
318  * FontInit --
319  *
320  *	Initialize the structure used for font management.
321  *
322  * Results:
323  *	None.
324  *
325  * Side effects:
326  *	Read the code.
327  *
328  *----------------------------------------------------------------------
329  */
330 
331 static void
FontInit()332 FontInit()
333 {
334     initialized = 1;
335     Tcl_InitHashTable(&nameTable, sizeof(NameKey)/sizeof(int));
336     Tcl_InitHashTable(&fontTable, TCL_ONE_WORD_KEYS);
337 }
338 
339 /*
340  *--------------------------------------------------------------
341  *
342  * SetFontMetrics --
343  *
344  *	This procedure is called to fill in the "widths" and "types"
345  *	arrays for a font.
346  *
347  * Results:
348  *	None.
349  *
350  * Side effects:
351  *	FontPtr gets modified to hold font metric information.
352  *
353  *--------------------------------------------------------------
354  */
355 
356 static void
SetFontMetrics(fontPtr)357 SetFontMetrics(fontPtr)
358     register TkFont *fontPtr;		/* Font structure in which to
359 					 * set metrics. */
360 {
361     int i, replaceOK;
362     register XFontStruct *fontStructPtr = fontPtr->fontStructPtr;
363     char *p;
364 
365     /*
366      * Pass 1: initialize the arrays.
367      */
368 
369     fontPtr->types = (char *) ckalloc(256);
370     fontPtr->widths = (unsigned char *) ckalloc(256);
371     for (i = 0; i < 256; i++) {
372 	fontPtr->types[i] = REPLACE;
373     }
374 
375     /*
376      * Pass 2:  for all characters that exist in the font and are
377      * not control characters, fill in the type and width
378      * information.
379      */
380 
381     for (i = 0; i < 256;  i++) {
382 	if ((i == 0177) || (i < fontStructPtr->min_char_or_byte2)
383 		|| (i > fontStructPtr->max_char_or_byte2)) {
384 	    continue;
385 	}
386 	fontPtr->types[i] = NORMAL;
387 	if (fontStructPtr->per_char == NULL) {
388 	    fontPtr->widths[i] = fontStructPtr->min_bounds.width;
389 	} else {
390 	    fontPtr->widths[i] = fontStructPtr->per_char[i
391 		    - fontStructPtr->min_char_or_byte2].width;
392 	}
393     }
394 
395     /*
396      * Pass 3: fill in information for characters that have to
397      * be replaced with  "\xhh" or "\n" strings.  If the font doesn't
398      * have the characters needed for this, then just use the
399      * font's default character.
400      */
401 
402     replaceOK = 1;
403     for (p = hexChars; *p != 0; p++) {
404 	if (fontPtr->types[*p] != NORMAL) {
405 	    replaceOK = 0;
406 	    break;
407 	}
408     }
409     for (i = 0; i < 256; i++) {
410 	if (fontPtr->types[i] != REPLACE) {
411 	    continue;
412 	}
413 	if (replaceOK) {
414 	    if ((i < sizeof(mapChars)) && (mapChars[i] != 0)) {
415 		fontPtr->widths[i] = fontPtr->widths['\\']
416 			+ fontPtr->widths[mapChars[i]];
417 	    } else {
418 		fontPtr->widths[i] = fontPtr->widths['\\']
419 			+ fontPtr->widths['x']
420 			+ fontPtr->widths[hexChars[i & 0xf]]
421 			+ fontPtr->widths[hexChars[(i>>4) & 0xf]];
422 	    }
423 	} else {
424 	    fontPtr->types[i] = SKIP;
425 	    fontPtr->widths[i] = 0;
426 	}
427     }
428 
429     /*
430      * Lastly, fill in special information for newline and tab.
431      */
432 
433     fontPtr->types['\n'] = NEWLINE;
434     fontPtr->types['\t'] = TAB;
435     fontPtr->widths['\t'] = 0;
436     if (fontPtr->types['0'] == NORMAL) {
437 	fontPtr->tabWidth = 8*fontPtr->widths['0'];
438     } else {
439 	fontPtr->tabWidth = 8*fontStructPtr->max_bounds.width;
440     }
441 
442     /*
443      * Make sure the tab width isn't zero (some fonts may not have enough
444      * information to set a reasonable tab width).
445      */
446 
447     if (fontPtr->tabWidth == 0) {
448 	fontPtr->tabWidth = 1;
449     }
450 }
451 
452 /*
453  *--------------------------------------------------------------
454  *
455  * TkMeasureChars --
456  *
457  *	Measure the number of characters from a string that
458  *	will fit in a given horizontal span.  The measurement
459  *	is done under the assumption that TkDisplayChars will
460  *	be used to actually display the characters.
461  *
462  * Results:
463  *	The return value is the number of characters from source
464  *	that fit in the span given by startX and maxX.  *nextXPtr
465  *	is filled in with the x-coordinate at which the first
466  *	character that didn't fit would be drawn, if it were to
467  *	be drawn.
468  *
469  * Side effects:
470  *	None.
471  *
472  *--------------------------------------------------------------
473  */
474 
475 int
TkMeasureChars(fontStructPtr,source,maxChars,startX,maxX,tabOrigin,flags,nextXPtr)476 TkMeasureChars(fontStructPtr, source, maxChars, startX, maxX,
477 	tabOrigin, flags, nextXPtr)
478     XFontStruct *fontStructPtr;	/* Font in which to draw characters. */
479     char *source;		/* Characters to be displayed.  Need not
480 				 * be NULL-terminated. */
481     int maxChars;		/* Maximum # of characters to consider from
482 				 * source. */
483     int startX;			/* X-position at which first character will
484 				 * be drawn. */
485     int maxX;			/* Don't consider any character that would
486 				 * cross this x-position. */
487     int tabOrigin;		/* X-location that serves as "origin" for
488 				 * tab stops. */
489     int flags;			/* Various flag bits OR-ed together.
490 				 * TK_WHOLE_WORDS means stop on a word boundary
491 				 * (just before a space character) if
492 				 * possible.  TK_AT_LEAST_ONE means always
493 				 * return a value of at least one, even
494 				 * if the character doesn't fit.
495 				 * TK_PARTIAL_OK means it's OK to display only
496 				 * a part of the last character in the line.
497 				 * TK_NEWLINES_NOT_SPECIAL means that newlines
498 				 * are treated just like other control chars:
499 				 * they don't terminate the line.
500 				 * TK_IGNORE_TABS means give all tabs zero
501 				 * width. */
502     int *nextXPtr;		/* Return x-position of terminating
503 				 * character here. */
504 {
505     register TkFont *fontPtr;
506     register char *p;		/* Current character. */
507     register int c;
508     char *term;			/* Pointer to most recent character that
509 				 * may legally be a terminating character. */
510     int termX;			/* X-position just after term. */
511     int curX;			/* X-position corresponding to p. */
512     int newX;			/* X-position corresponding to p+1. */
513     int type;
514     int rem;
515 
516     /*
517      * Find the TkFont structure for this font, and make sure its
518      * font metrics exist.
519      */
520 
521     if (lastFontStructPtr == fontStructPtr) {
522 	fontPtr = lastFontPtr;
523     } else {
524 	Tcl_HashEntry *fontHashPtr;
525 
526 	if (!initialized) {
527 	    badArg:
528 	    panic("TkMeasureChars received unknown font argument");
529 	}
530 
531 	fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
532 	if (fontHashPtr == NULL) {
533 	    goto badArg;
534 	}
535 	fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
536 	lastFontStructPtr = fontPtr->fontStructPtr;
537 	lastFontPtr = fontPtr;
538     }
539     if (fontPtr->types == NULL) {
540 	SetFontMetrics(fontPtr);
541     }
542 
543     /*
544      * Scan the input string one character at a time, until a character
545      * is found that crosses maxX.
546      */
547 
548     newX = curX = startX;
549     termX = 0;		/* Not needed, but eliminates compiler warning. */
550     term = source;
551     for (p = source, c = *p & 0xff; maxChars > 0; p++, maxChars--) {
552 	type = fontPtr->types[c];
553 	if ((type == NORMAL) || (type == REPLACE)) {
554 	    newX += fontPtr->widths[c];
555 	} else if (type == TAB) {
556 	    if (!(flags & TK_IGNORE_TABS)) {
557 		newX += fontPtr->tabWidth;
558 		rem = (newX - tabOrigin) % fontPtr->tabWidth;
559 		if (rem < 0) {
560 		    rem += fontPtr->tabWidth;
561 		}
562 		newX -= rem;
563 	    }
564 	} else if (type == NEWLINE) {
565 	    if (flags & TK_NEWLINES_NOT_SPECIAL) {
566 		newX += fontPtr->widths[c];
567 	    } else {
568 		break;
569 	    }
570 	} else if (type != SKIP) {
571 	    panic("Unknown type %d in TkMeasureChars", type);
572 	}
573 	if (newX > maxX) {
574 	    break;
575 	}
576 	if (maxChars > 1) {
577 	    c = p[1] & 0xff;
578 	} else {
579 	    /*
580 	     * Can't look at next character: it could be in uninitialized
581 	     * memory.
582 	     */
583 
584 	    c = 0;
585 	}
586 	if (isspace(UCHAR(c)) || (c == 0)) {
587 	    term = p+1;
588 	    termX = newX;
589 	}
590 	curX = newX;
591     }
592 
593     /*
594      * P points to the first character that doesn't fit in the desired
595      * span.  Use the flags to figure out what to return.
596      */
597 
598     if ((flags & TK_PARTIAL_OK) && (curX < maxX)) {
599 	curX = newX;
600 	p++;
601     }
602     if ((flags & TK_AT_LEAST_ONE) && (term == source) && (maxChars > 0)
603 	     && !isspace(UCHAR(*term))) {
604 	term = p;
605 	termX = curX;
606 	if (term == source) {
607 	    term++;
608 	    termX = newX;
609 	}
610     } else if ((maxChars == 0) || !(flags & TK_WHOLE_WORDS)) {
611 	term = p;
612 	termX = curX;
613     }
614     *nextXPtr = termX;
615     return term-source;
616 }
617 
618 /*
619  *--------------------------------------------------------------
620  *
621  * TkDisplayChars --
622  *
623  *	Draw a string of characters on the screen, converting
624  *	tabs to the right number of spaces and control characters
625  *	to sequences of the form "\xhh" where hh are two hex
626  *	digits.
627  *
628  * Results:
629  *	None.
630  *
631  * Side effects:
632  *	Information gets drawn on the screen.
633  *
634  *--------------------------------------------------------------
635  */
636 
637 void
TkDisplayChars(display,drawable,gc,fontStructPtr,string,numChars,x,y,tabOrigin,flags)638 TkDisplayChars(display, drawable, gc, fontStructPtr, string, numChars,
639 	x, y, tabOrigin, flags)
640     Display *display;		/* Display on which to draw. */
641     Drawable drawable;		/* Window or pixmap in which to draw. */
642     GC gc;			/* Graphics context for actually drawing
643 				 * characters. */
644     XFontStruct *fontStructPtr;	/* Font used in GC;  must have been allocated
645 				 * by Tk_GetFontStruct.  Used to compute sizes
646 				 * of tabs, etc. */
647     char *string;		/* Characters to be displayed. */
648     int numChars;		/* Number of characters to display from
649 				 * string. */
650     int x, y;			/* Coordinates at which to draw string. */
651     int tabOrigin;		/* X-location that serves as "origin" for
652 				 * tab stops. */
653     int flags;			/* Flags to control display.  Only
654 				 * TK_NEWLINES_NOT_SPECIAL and TK_IGNORE_TABS
655 				 * are supported right now.  See
656 				 * TkMeasureChars for information about it. */
657 {
658     register TkFont *fontPtr;
659     register char *p;		/* Current character being scanned. */
660     register int c;
661     int type;
662     char *start;		/* First character waiting to be displayed. */
663     int startX;			/* X-coordinate corresponding to start. */
664     int curX;			/* X-coordinate corresponding to p. */
665     char replace[10];
666     int rem;
667 
668     /*
669      * Find the TkFont structure for this font, and make sure its
670      * font metrics exist.
671      */
672 
673     if (lastFontStructPtr == fontStructPtr) {
674 	fontPtr = lastFontPtr;
675     } else {
676 	Tcl_HashEntry *fontHashPtr;
677 
678 	if (!initialized) {
679 	    badArg:
680 	    panic("TkDisplayChars received unknown font argument");
681 	}
682 
683 	fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
684 	if (fontHashPtr == NULL) {
685 	    goto badArg;
686 	}
687 	fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
688 	lastFontStructPtr = fontPtr->fontStructPtr;
689 	lastFontPtr = fontPtr;
690     }
691     if (fontPtr->types == NULL) {
692 	SetFontMetrics(fontPtr);
693     }
694 
695     /*
696      * Scan the string one character at a time.  Display control
697      * characters immediately, but delay displaying normal characters
698      * in order to pass many characters to the server all together.
699      */
700 
701     startX = curX = x;
702     start = string;
703     for (p = string; numChars > 0; numChars--, p++) {
704 	c = *p & 0xff;
705 	type = fontPtr->types[c];
706 	if (type == NORMAL) {
707 	    curX += fontPtr->widths[c];
708 	    continue;
709 	}
710 	if (p != start) {
711 	    XDrawString(display, drawable, gc, startX, y, start, p - start);
712 	    startX = curX;
713 	}
714 	if (type == TAB) {
715 	    if (!(flags & TK_IGNORE_TABS)) {
716 		curX += fontPtr->tabWidth;
717 		rem = (curX - tabOrigin) % fontPtr->tabWidth;
718 		if (rem < 0) {
719 		    rem += fontPtr->tabWidth;
720 		}
721 		curX -= rem;
722 	    }
723 	} else if (type == REPLACE ||
724 		(type == NEWLINE && flags & TK_NEWLINES_NOT_SPECIAL)) {
725 	    if ((c < sizeof(mapChars)) && (mapChars[c] != 0)) {
726 		replace[0] = '\\';
727 	        replace[1] = mapChars[c];
728 	        XDrawString(display, drawable, gc, startX, y, replace, 2);
729 	        curX += fontPtr->widths[replace[0]]
730 		        + fontPtr->widths[replace[1]];
731 	    } else {
732 		replace[0] = '\\';
733 	        replace[1] = 'x';
734 	        replace[2] = hexChars[(c >> 4) & 0xf];
735 	        replace[3] = hexChars[c & 0xf];
736 	        XDrawString(display, drawable, gc, startX, y, replace, 4);
737 	        curX += fontPtr->widths[replace[0]]
738 		        + fontPtr->widths[replace[1]]
739 		        + fontPtr->widths[replace[2]]
740 		        + fontPtr->widths[replace[3]];
741   	    }
742 	} else if (type == NEWLINE) {
743   	    y += fontStructPtr->ascent + fontStructPtr->descent;
744 	    curX = x;
745 	} else if (type != SKIP) {
746 	    panic("Unknown type %d in TkDisplayChars", type);
747 	}
748 	startX = curX;
749 	start = p+1;
750     }
751 
752     /*
753      * At the very end, there may be one last batch of normal characters
754      * to display.
755      */
756 
757     if (p != start) {
758 	XDrawString(display, drawable, gc, startX, y, start, p - start);
759     }
760 }
761 
762 /*
763  *----------------------------------------------------------------------
764  *
765  * TkUnderlineChars --
766  *
767  *	This procedure draws an underline for a given range of characters
768  *	in a given string, using appropriate information for the string's
769  *	font.  It doesn't draw the characters (which are assumed to have
770  *	been displayed previously);  it just draws the underline.
771  *
772  * Results:
773  *	None.
774  *
775  * Side effects:
776  *	Information gets displayed in "drawable".
777  *
778  *----------------------------------------------------------------------
779  */
780 
781 void
TkUnderlineChars(display,drawable,gc,fontStructPtr,string,x,y,tabOrigin,flags,firstChar,lastChar)782 TkUnderlineChars(display, drawable, gc, fontStructPtr, string, x, y,
783 	tabOrigin, flags, firstChar, lastChar)
784     Display *display;		/* Display on which to draw. */
785     Drawable drawable;		/* Window or pixmap in which to draw. */
786     GC gc;			/* Graphics context for actually drawing
787 				 * underline. */
788     XFontStruct *fontStructPtr;	/* Font used in GC;  must have been allocated
789 				 * by Tk_GetFontStruct.  Used to character
790 				 * dimensions, etc. */
791     char *string;		/* String containing characters to be
792 				 * underlined. */
793     int x, y;			/* Coordinates at which first character of
794 				 * string is drawn. */
795     int tabOrigin;		/* X-location that serves as "origin" for
796 				 * tab stops. */
797     int flags;			/* Flags that were passed to TkDisplayChars. */
798     int firstChar;		/* Index of first character to underline. */
799     int lastChar;		/* Index of last character to underline. */
800 {
801     int xUnder, yUnder, width, height;
802     unsigned long value;
803 
804     /*
805      * First compute the vertical span of the underline, using font
806      * properties if they exist.
807      */
808 
809     if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_POSITION, &value)) {
810 	yUnder = y + value;
811     } else {
812 	yUnder = y + fontStructPtr->max_bounds.descent/2;
813     }
814     if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_THICKNESS, &value)) {
815 	height = value;
816     } else {
817 	height = 2;
818     }
819 
820     /*
821      * Now compute the horizontal span of the underline.
822      */
823 
824     TkMeasureChars(fontStructPtr, string, firstChar, x, (int) 1000000,
825 	    tabOrigin, flags, &xUnder);
826     TkMeasureChars(fontStructPtr, string+firstChar, lastChar+1-firstChar,
827 	    xUnder, (int) 1000000, tabOrigin, flags, &width);
828     width -= xUnder;
829 
830     XFillRectangle(display, drawable, gc, xUnder, yUnder,
831 	    (unsigned int) width, (unsigned int) height);
832 }
833 
834 /*
835  *----------------------------------------------------------------------
836  *
837  * TkComputeTextGeometry --
838  *
839  *	This procedure computes the amount of screen space needed to
840  *	display a multi-line string of text.
841  *
842  * Results:
843  *	There is no return value.  The dimensions of the screen area
844  *	needed to display the text are returned in *widthPtr, and *heightPtr.
845  *
846  * Side effects:
847  *	None.
848  *
849  *----------------------------------------------------------------------
850  */
851 
852 void
TkComputeTextGeometry(fontStructPtr,string,numChars,wrapLength,widthPtr,heightPtr)853 TkComputeTextGeometry(fontStructPtr, string, numChars, wrapLength,
854 	widthPtr, heightPtr)
855     XFontStruct *fontStructPtr;	/* Font that will be used to display text. */
856     char *string;		/* String whose dimensions are to be
857 				 * computed. */
858     int numChars;		/* Number of characters to consider from
859 				 * string. */
860     int wrapLength;		/* Longest permissible line length, in
861 				 * pixels.  <= 0 means no automatic wrapping:
862 				 * just let lines get as long as needed. */
863     int *widthPtr;		/* Store width of string here. */
864     int *heightPtr;		/* Store height of string here. */
865 {
866     int thisWidth, maxWidth, numLines;
867     char *p;
868 
869     if (wrapLength <= 0) {
870 	wrapLength = INT_MAX;
871     }
872     maxWidth = 0;
873     for (numLines = 1, p = string; (p - string) < numChars; numLines++) {
874 	p += TkMeasureChars(fontStructPtr, p, numChars - (p - string), 0,
875 		wrapLength, 0, TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &thisWidth);
876 	if (thisWidth > maxWidth) {
877 	    maxWidth = thisWidth;
878 	}
879 	if (*p == 0) {
880 	    break;
881 	}
882 
883 	/*
884 	 * If the character that didn't fit in this line was a white
885 	 * space character then skip it.
886 	 */
887 
888 	if (isspace(UCHAR(*p))) {
889 	    p++;
890 	}
891     }
892     *widthPtr = maxWidth;
893     *heightPtr = numLines * (fontStructPtr->ascent + fontStructPtr->descent);
894 }
895 
896 /*
897  *----------------------------------------------------------------------
898  *
899  * TkDisplayText --
900  *
901  *	Display a text string on one or more lines.
902  *
903  * Results:
904  *	None.
905  *
906  * Side effects:
907  *	The text given by "string" gets displayed at the given location
908  *	in the given drawable with the given font etc.
909  *
910  *----------------------------------------------------------------------
911  */
912 
913 void
TkDisplayText(display,drawable,fontStructPtr,string,numChars,x,y,length,justify,underline,gc)914 TkDisplayText(display, drawable, fontStructPtr, string, numChars, x, y,
915 	length, justify, underline, gc)
916     Display *display;		/* X display to use for drawing text. */
917     Drawable drawable;		/* Window or pixmap in which to draw the
918 				 * text. */
919     XFontStruct *fontStructPtr;	/* Font that determines geometry of text
920 				 * (should be same as font in gc). */
921     char *string;		/* String to display;  may contain embedded
922 				 * newlines. */
923     int numChars;		/* Number of characters to use from string. */
924     int x, y;			/* Pixel coordinates within drawable of
925 				 * upper left corner of display area. */
926     int length;			/* Line length in pixels;  used to compute
927 				 * word wrap points and also for
928 				 * justification.   Must be > 0. */
929     Tk_Justify justify;		/* How to justify lines. */
930     int underline;		/* Index of character to underline, or < 0
931 				 * for no underlining. */
932     GC gc;			/* Graphics context to use for drawing text. */
933 {
934     char *p;
935     int charsThisLine, lengthThisLine, xThisLine;
936 
937     /*
938      * Work through the string one line at a time.  Display each line
939      * in four steps:
940      *     1. Compute the line's length.
941      *     2. Figure out where to display the line for justification.
942      *     3. Display the line.
943      *     4. Underline one character if needed.
944      */
945 
946     y += fontStructPtr->ascent;
947     for (p = string; numChars > 0; ) {
948 	charsThisLine = TkMeasureChars(fontStructPtr, p, numChars, 0, length,
949 		0, TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &lengthThisLine);
950 	if (justify == TK_JUSTIFY_LEFT) {
951 	    xThisLine = x;
952 	} else if (justify == TK_JUSTIFY_CENTER) {
953 	    xThisLine = x + (length - lengthThisLine)/2;
954 	} else {
955 	    xThisLine = x + (length - lengthThisLine);
956 	}
957 	TkDisplayChars(display, drawable, gc, fontStructPtr, p, charsThisLine,
958 		xThisLine, y, xThisLine, 0);
959 	if ((underline >= 0) && (underline < charsThisLine)) {
960 	    TkUnderlineChars(display, drawable, gc, fontStructPtr, p,
961 		    xThisLine, y, xThisLine, 0, underline, underline);
962 	}
963 	p += charsThisLine;
964 	numChars -= charsThisLine;
965 	underline -= charsThisLine;
966 	y += fontStructPtr->ascent + fontStructPtr->descent;
967 
968 	/*
969 	 * If the character that didn't fit was a space character, skip it.
970 	 */
971 
972 	if (isspace(UCHAR(*p))) {
973 	    p++;
974 	    numChars--;
975 	    underline--;
976 	}
977     }
978 }
979