]>
git.zerfleddert.de Git - micropolis/blob - src/tk/tkfont.c
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.
8 * Copyright 1990 Regents of the University of California
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that the above copyright
12 * notice appear in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
19 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkFont.c,v 1.21 92/06/15 14:00:19 ouster Exp $ SPRITE (Berkeley)";
26 * This module caches extra information about fonts in addition to
27 * what X already provides. The extra information is used by the
28 * TkMeasureChars procedure, and consists of two parts: a type and
29 * a width. The type is one of the following:
31 * NORMAL: Standard character.
32 * TAB: Tab character: output enough space to
33 * get to next tab stop.
34 * NEWLINE: Newline character: don't output anything more
35 * on this line (character has infinite width).
36 * REPLACE: This character doesn't print: instead of
37 * displaying character, display a replacement
38 * sequence of the form "\xdd" where dd is the
39 * hex equivalent of the character.
40 * SKIP: Don't display anything for this character. This
41 * is only used where the font doesn't contain
42 * all the characters needed to generate
43 * replacement sequences.
44 * The width gives the total width of the displayed character or
45 * sequence: for replacement sequences, it gives the width of the
56 * One of the following data structures exists for each font that is
57 * currently active. The structure is indexed with two hash tables,
58 * one based on font name and one based on XFontStruct address.
62 XFontStruct
*fontStructPtr
; /* X information about font. */
63 Display
*display
; /* Display to which font belongs. */
64 int refCount
; /* Number of active uses of this font. */
65 char *types
; /* Malloc'ed array giving types of all
66 * chars in the font (may be NULL). */
67 unsigned char *widths
; /* Malloc'ed array giving widths of all
68 * chars in the font (may be NULL). */
69 int tabWidth
; /* Width of tabs in this font. */
70 Tcl_HashEntry
*nameHashPtr
; /* Entry in name-based hash table (needed
71 * when deleting this structure). */
75 * Hash table for name -> TkFont mapping, and key structure used to
76 * index into that table:
79 static Tcl_HashTable nameTable
;
81 Tk_Uid name
; /* Name of font. */
82 Display
*display
; /* Display for which font is valid. */
86 * Hash table for font struct -> TkFont mapping. This table is
87 * indexed by the XFontStruct address.
90 static Tcl_HashTable fontTable
;
92 static int initialized
= 0; /* 0 means static structures haven't been
96 * To speed up TkMeasureChars, the variables below keep the last
97 * mapping from (XFontStruct *) to (TkFont *).
100 static TkFont
*lastFontPtr
= NULL
;
101 static XFontStruct
*lastFontStructPtr
= NULL
;
104 * Characters used when displaying control sequences as their
108 static char hexChars
[] = "0123456789abcdefx\\";
111 * Forward declarations for procedures defined in this file:
114 static void FontInit
_ANSI_ARGS_((void));
115 static void SetFontMetrics
_ANSI_ARGS_((TkFont
*fontPtr
));
118 *----------------------------------------------------------------------
120 * Tk_GetFontStruct --
122 * Given a string name for a font, map the name to an XFontStruct
123 * describing the font.
126 * The return value is normally a pointer to the font description
127 * for the desired font. If an error occurs in mapping the string
128 * to a font, then an error message will be left in interp->result
129 * and NULL will be returned.
132 * The font is added to an internal database with a reference count.
133 * For each call to this procedure, there should eventually be a call
134 * to Tk_FreeFontStruct, so that the database is cleaned up when fonts
135 * aren't in use anymore.
137 *----------------------------------------------------------------------
141 Tk_GetFontStruct(interp
, tkwin
, name
)
142 Tcl_Interp
*interp
; /* Place to leave error message if
143 * font can't be found. */
144 Tk_Window tkwin
; /* Window in which font will be used. */
145 Tk_Uid name
; /* Name of font (in form suitable for
146 * passing to XLoadQueryFont). */
149 Tcl_HashEntry
*nameHashPtr
, *fontHashPtr
;
151 register TkFont
*fontPtr
;
152 XFontStruct
*fontStructPtr
;
159 * First, check to see if there's already a mapping for this font
164 nameKey
.display
= Tk_Display(tkwin
);
165 nameHashPtr
= Tcl_CreateHashEntry(&nameTable
, (char *) &nameKey
, &new);
167 fontPtr
= (TkFont
*) Tcl_GetHashValue(nameHashPtr
);
169 return fontPtr
->fontStructPtr
;
173 * The name isn't currently known. Map from the name to a font, and
174 * add a new structure to the database.
177 fontStructPtr
= XLoadQueryFont(nameKey
.display
, name
);
178 if (fontStructPtr
== NULL
) {
179 Tcl_DeleteHashEntry(nameHashPtr
);
180 Tcl_AppendResult(interp
, "font \"", name
, "\" doesn't exist",
184 fontPtr
= (TkFont
*) ckalloc(sizeof(TkFont
));
185 fontPtr
->display
= nameKey
.display
;
186 fontPtr
->fontStructPtr
= fontStructPtr
;
187 fontPtr
->refCount
= 1;
188 fontPtr
->types
= NULL
;
189 fontPtr
->widths
= NULL
;
190 fontPtr
->nameHashPtr
= nameHashPtr
;
191 fontHashPtr
= Tcl_CreateHashEntry(&fontTable
, (char *) fontStructPtr
, &new);
193 panic("XFontStruct already registered in Tk_GetFontStruct");
195 Tcl_SetHashValue(nameHashPtr
, fontPtr
);
196 Tcl_SetHashValue(fontHashPtr
, fontPtr
);
197 return fontPtr
->fontStructPtr
;
201 *--------------------------------------------------------------
203 * Tk_NameOfFontStruct --
205 * Given a font, return a textual string identifying it.
208 * If font was created by Tk_GetFontStruct, then the return
209 * value is the "string" that was used to create it.
210 * Otherwise the return value is a string giving the X
211 * identifier for the font. The storage for the returned
212 * string is only guaranteed to persist up until the next
213 * call to this procedure.
218 *--------------------------------------------------------------
222 Tk_NameOfFontStruct(fontStructPtr
)
223 XFontStruct
*fontStructPtr
; /* Font whose name is desired. */
225 Tcl_HashEntry
*fontHashPtr
;
227 static char string
[20];
231 sprintf(string
, "font id 0x%x", fontStructPtr
->fid
);
234 fontHashPtr
= Tcl_FindHashEntry(&fontTable
, (char *) fontStructPtr
);
235 if (fontHashPtr
== NULL
) {
238 fontPtr
= (TkFont
*) Tcl_GetHashValue(fontHashPtr
);
239 return ((NameKey
*) fontPtr
->nameHashPtr
->key
.words
)->name
;
243 *----------------------------------------------------------------------
245 * Tk_FreeFontStruct --
247 * This procedure is called to release a font allocated by
254 * The reference count associated with font is decremented, and
255 * the font is officially deallocated if no-one is using it
258 *----------------------------------------------------------------------
262 Tk_FreeFontStruct(fontStructPtr
)
263 XFontStruct
*fontStructPtr
; /* Font to be released. */
265 Tcl_HashEntry
*fontHashPtr
;
266 register TkFont
*fontPtr
;
269 panic("Tk_FreeFontStruct called before Tk_GetFontStruct");
272 fontHashPtr
= Tcl_FindHashEntry(&fontTable
, (char *) fontStructPtr
);
273 if (fontHashPtr
== NULL
) {
274 panic("Tk_FreeFontStruct received unknown font argument");
276 fontPtr
= (TkFont
*) Tcl_GetHashValue(fontHashPtr
);
278 if (fontPtr
->refCount
== 0) {
279 XFreeFont(fontPtr
->display
, fontPtr
->fontStructPtr
);
280 Tcl_DeleteHashEntry(fontPtr
->nameHashPtr
);
281 Tcl_DeleteHashEntry(fontHashPtr
);
282 if (fontPtr
->types
!= NULL
) {
283 ckfree(fontPtr
->types
);
285 if (fontPtr
->widths
!= NULL
) {
286 ckfree((char *) fontPtr
->widths
);
288 ckfree((char *) fontPtr
);
289 lastFontStructPtr
= NULL
;
294 *----------------------------------------------------------------------
298 * Initialize the structure used for font management.
306 *----------------------------------------------------------------------
313 Tcl_InitHashTable(&nameTable
, sizeof(NameKey
)/sizeof(int));
314 Tcl_InitHashTable(&fontTable
, TCL_ONE_WORD_KEYS
);
318 *--------------------------------------------------------------
322 * This procedure is called to fill in the "widths" and "types"
329 * FontPtr gets modified to hold font metric information.
331 *--------------------------------------------------------------
335 SetFontMetrics(fontPtr
)
336 register TkFont
*fontPtr
; /* Font structure in which to
339 int i
, replaceOK
, baseWidth
;
340 register XFontStruct
*fontStructPtr
= fontPtr
->fontStructPtr
;
344 * Pass 1: initialize the arrays.
347 fontPtr
->types
= (char *) ckalloc(256);
348 fontPtr
->widths
= (unsigned char *) ckalloc(256);
349 for (i
= 0; i
< 256; i
++) {
350 fontPtr
->types
[i
] = REPLACE
;
354 * Pass 2: for all characters that exist in the font and are
355 * not control characters, fill in the type and width
359 for (i
= ' '; i
< 256; i
++) {
360 if ((i
== 0177) || (i
< fontStructPtr
->min_char_or_byte2
)
361 || (i
> fontStructPtr
->max_char_or_byte2
)) {
364 fontPtr
->types
[i
] = NORMAL
;
365 if (fontStructPtr
->per_char
== NULL
) {
366 fontPtr
->widths
[i
] = fontStructPtr
->min_bounds
.width
;
368 fontPtr
->widths
[i
] = fontStructPtr
->per_char
[i
369 - fontStructPtr
->min_char_or_byte2
].width
;
374 * Pass 3: fill in information for characters that have to
375 * be replaced with "\xhh" strings. If the font doesn't
376 * have the characters needed for this, then just use the
377 * font's default character.
381 baseWidth
= fontPtr
->widths
['\\'] + fontPtr
->widths
['x'];
382 for (p
= hexChars
; *p
!= 0; p
++) {
383 if (fontPtr
->types
[*p
] != NORMAL
) {
388 for (i
= 0; i
< 256; i
++) {
389 if (fontPtr
->types
[i
] != REPLACE
) {
393 fontPtr
->widths
[i
] = baseWidth
394 + fontPtr
->widths
[hexChars
[i
& 0xf]]
395 + fontPtr
->widths
[hexChars
[(i
>>4) & 0xf]];
397 fontPtr
->types
[i
] = SKIP
;
398 fontPtr
->widths
[i
] = 0;
403 * Lastly, fill in special information for newline and tab.
406 fontPtr
->types
['\n'] = NEWLINE
;
407 fontPtr
->widths
['\n'] = 0;
408 fontPtr
->types
['\t'] = TAB
;
409 fontPtr
->widths
['\t'] = 0;
410 if (fontPtr
->types
['0'] == NORMAL
) {
411 fontPtr
->tabWidth
= 8*fontPtr
->widths
['0'];
413 fontPtr
->tabWidth
= 8*fontStructPtr
->max_bounds
.width
;
417 * Make sure the tab width isn't zero (some fonts may not have enough
418 * information to set a reasonable tab width).
421 if (fontPtr
->tabWidth
== 0) {
422 fontPtr
->tabWidth
= 1;
427 *--------------------------------------------------------------
431 * Measure the number of characters from a string that
432 * will fit in a given horizontal span. The measurement
433 * is done under the assumption that TkDisplayChars will
434 * be used to actually display the characters.
437 * The return value is the number of characters from source
438 * that fit in the span given by startX and maxX. *nextXPtr
439 * is filled in with the x-coordinate at which the first
440 * character that didn't fit would be drawn, if it were to
446 *--------------------------------------------------------------
450 TkMeasureChars(fontStructPtr
, source
, maxChars
, startX
, maxX
, flags
, nextXPtr
)
451 XFontStruct
*fontStructPtr
; /* Font in which to draw characters. */
452 char *source
; /* Characters to be displayed. Need not
453 * be NULL-terminated. */
454 int maxChars
; /* Maximum # of characters to consider from
456 int startX
; /* X-position at which first character will
458 int maxX
; /* Don't consider any character that would
459 * cross this x-position. */
460 int flags
; /* Various flag bits OR-ed together.
461 * TK_WHOLE_WORDS means stop on a word boundary
462 * (just before a space character) if
463 * possible. TK_AT_LEAST_ONE means always
464 * return a value of at least one, even
465 * if the character doesn't fit.
466 * TK_PARTIAL_OK means it's OK to display only
467 * a part of the last character in the line.
468 * TK_NEWLINES_NOT_SPECIAL means that newlines
469 * are treated just like other control chars:
470 * they don't terminate the line,*/
471 int *nextXPtr
; /* Return x-position of terminating
474 register TkFont
*fontPtr
;
475 register char *p
; /* Current character. */
477 char *term
; /* Pointer to most recent character that
478 * may legally be a terminating character. */
479 int termX
; /* X-position just after term. */
480 int curX
; /* X-position corresponding to p. */
481 int newX
; /* X-position corresponding to p+1. */
485 * Find the TkFont structure for this font, and make sure its
486 * font metrics exist.
489 if (lastFontStructPtr
== fontStructPtr
) {
490 fontPtr
= lastFontPtr
;
492 Tcl_HashEntry
*fontHashPtr
;
496 panic("TkMeasureChars received unknown font argument");
499 fontHashPtr
= Tcl_FindHashEntry(&fontTable
, (char *) fontStructPtr
);
500 if (fontHashPtr
== NULL
) {
503 fontPtr
= (TkFont
*) Tcl_GetHashValue(fontHashPtr
);
504 lastFontStructPtr
= fontPtr
->fontStructPtr
;
505 lastFontPtr
= fontPtr
;
507 if (fontPtr
->types
== NULL
) {
508 SetFontMetrics(fontPtr
);
512 * Scan the input string one character at a time, until a character
513 * is found that crosses maxX.
516 newX
= curX
= startX
;
517 termX
= 0; /* Not needed, but eliminates compiler warning. */
519 for (p
= source
, c
= *p
& 0xff; maxChars
> 0; p
++, maxChars
--) {
520 type
= fontPtr
->types
[c
];
521 if (type
== NORMAL
) {
522 newX
+= fontPtr
->widths
[c
];
523 } else if (type
== TAB
) {
524 newX
+= fontPtr
->tabWidth
;
525 newX
-= newX
% fontPtr
->tabWidth
;
526 } else if (type
== REPLACE
) {
528 newX
+= fontPtr
->widths
['\\'] + fontPtr
->widths
['x']
529 + fontPtr
->widths
[hexChars
[(c
>> 4) & 0xf]]
530 + fontPtr
->widths
[hexChars
[c
& 0xf]];
531 } else if (type
== NEWLINE
) {
532 if (flags
& TK_NEWLINES_NOT_SPECIAL
) {
536 } else if (type
!= SKIP
) {
537 panic("Unknown type %d in TkMeasureChars", type
);
543 if (isspace(c
) || (c
== 0)) {
551 * P points to the first character that doesn't fit in the desired
552 * span. Use the flags to figure out what to return.
555 if ((flags
& TK_PARTIAL_OK
) && (curX
< maxX
)) {
559 if ((flags
& TK_AT_LEAST_ONE
) && (term
== source
) && (maxChars
> 0)
563 if (term
== source
) {
567 } else if ((maxChars
== 0) || !(flags
& TK_WHOLE_WORDS
)) {
576 *--------------------------------------------------------------
580 * Draw a string of characters on the screen, converting
581 * tabs to the right number of spaces and control characters
582 * to sequences of the form "\xhh" where hh are two hex
589 * Information gets drawn on the screen.
591 *--------------------------------------------------------------
595 TkDisplayChars(display
, drawable
, gc
, fontStructPtr
, string
, numChars
,
597 Display
*display
; /* Display on which to draw. */
598 Drawable drawable
; /* Window or pixmap in which to draw. */
599 GC gc
; /* Graphics context for actually drawing
601 XFontStruct
*fontStructPtr
; /* Font used in GC; must have been allocated
602 * by Tk_GetFontStruct. Used to compute sizes
604 char *string
; /* Characters to be displayed. */
605 int numChars
; /* Number of characters to display from
607 int x
, y
; /* Coordinates at which to draw string. */
608 int flags
; /* Flags to control display. Only
609 * TK_NEWLINES_NOT_SPECIAL is supported right
610 * now. See TkMeasureChars for information
613 register TkFont
*fontPtr
;
614 register char *p
; /* Current character being scanned. */
617 char *start
; /* First character waiting to be displayed. */
618 int startX
; /* X-coordinate corresponding to start. */
619 int curX
; /* X-coordinate corresponding to p. */
623 * Find the TkFont structure for this font, and make sure its
624 * font metrics exist.
627 if (lastFontStructPtr
== fontStructPtr
) {
628 fontPtr
= lastFontPtr
;
630 Tcl_HashEntry
*fontHashPtr
;
634 panic("TkDisplayChars received unknown font argument");
637 fontHashPtr
= Tcl_FindHashEntry(&fontTable
, (char *) fontStructPtr
);
638 if (fontHashPtr
== NULL
) {
641 fontPtr
= (TkFont
*) Tcl_GetHashValue(fontHashPtr
);
642 lastFontStructPtr
= fontPtr
->fontStructPtr
;
643 lastFontPtr
= fontPtr
;
645 if (fontPtr
->types
== NULL
) {
646 SetFontMetrics(fontPtr
);
650 * Scan the string one character at a time. Display control
651 * characters immediately, but delay displaying normal characters
652 * in order to pass many characters to the server all together.
657 for (p
= string
; numChars
> 0; numChars
--, p
++) {
659 type
= fontPtr
->types
[c
];
660 if (type
== NORMAL
) {
661 curX
+= fontPtr
->widths
[c
];
665 XDrawString(display
, drawable
, gc
, startX
, y
, start
, p
- start
);
669 curX
+= fontPtr
->tabWidth
;
670 curX
-= curX
% fontPtr
->tabWidth
;
671 } else if (type
== REPLACE
) {
675 replace
[2] = hexChars
[(c
>> 4) & 0xf];
676 replace
[3] = hexChars
[c
& 0xf];
677 XDrawString(display
, drawable
, gc
, startX
, y
, replace
, 4);
678 curX
+= fontPtr
->widths
[replace
[0]]
679 + fontPtr
->widths
[replace
[1]]
680 + fontPtr
->widths
[replace
[2]]
681 + fontPtr
->widths
[replace
[3]];
682 } else if (type
== NEWLINE
) {
683 if (flags
& TK_NEWLINES_NOT_SPECIAL
) {
686 y
+= fontStructPtr
->ascent
+ fontStructPtr
->descent
;
688 } else if (type
!= SKIP
) {
689 panic("Unknown type %d in TkDisplayChars", type
);
696 * At the very end, there may be one last batch of normal characters
701 XDrawString(display
, drawable
, gc
, startX
, y
, start
, p
- start
);
706 *----------------------------------------------------------------------
708 * TkUnderlineChars --
710 * This procedure draws an underline for a given range of characters
711 * in a given string, using appropriate information for the string's
712 * font. It doesn't draw the characters (which are assumed to have
713 * been displayed previously); it just draws the underline.
719 * Information gets displayed in "drawable".
721 *----------------------------------------------------------------------
725 TkUnderlineChars(display
, drawable
, gc
, fontStructPtr
, string
, x
, y
,
726 flags
, firstChar
, lastChar
)
727 Display
*display
; /* Display on which to draw. */
728 Drawable drawable
; /* Window or pixmap in which to draw. */
729 GC gc
; /* Graphics context for actually drawing
731 XFontStruct
*fontStructPtr
; /* Font used in GC; must have been allocated
732 * by Tk_GetFontStruct. Used to character
733 * dimensions, etc. */
734 char *string
; /* String containing characters to be
736 int x
, y
; /* Coordinates at which first character of
737 * string is drawn. */
738 int flags
; /* Flags that were passed to TkDisplayChars. */
739 int firstChar
; /* Index of first character to underline. */
740 int lastChar
; /* Index of last character to underline. */
742 int xUnder
, yUnder
, width
, height
;
746 * First compute the vertical span of the underline, using font
747 * properties if they exist.
750 if (XGetFontProperty(fontStructPtr
, XA_UNDERLINE_POSITION
, &value
)) {
753 yUnder
= y
+ fontStructPtr
->max_bounds
.descent
/2;
755 if (XGetFontProperty(fontStructPtr
, XA_UNDERLINE_THICKNESS
, &value
)) {
762 * Now compute the horizontal span of the underline.
765 TkMeasureChars(fontStructPtr
, string
, firstChar
, x
, (int) 1000000, flags
,
767 TkMeasureChars(fontStructPtr
, string
+firstChar
, lastChar
+1-firstChar
,
768 xUnder
, (int) 1000000, flags
, &width
);
771 XFillRectangle(display
, drawable
, gc
, xUnder
, yUnder
,
772 (unsigned int) width
, (unsigned int) height
);