]>
cvs.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 *----------------------------------------------------------------------
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). */
150 Tcl_HashEntry
*nameHashPtr
, *fontHashPtr
;
152 register TkFont
*fontPtr
;
153 XFontStruct
*fontStructPtr
;
160 * First, check to see if there's already a mapping for this font
165 nameKey
.display
= Tk_Display(tkwin
);
166 nameHashPtr
= Tcl_CreateHashEntry(&nameTable
, (char *) &nameKey
, &new);
168 fontPtr
= (TkFont
*) Tcl_GetHashValue(nameHashPtr
);
170 return fontPtr
->fontStructPtr
;
174 * The name isn't currently known. Map from the name to a font, and
175 * add a new structure to the database.
178 fontStructPtr
= XLoadQueryFont(nameKey
.display
, name
);
179 if (fontStructPtr
== NULL
) {
180 Tcl_DeleteHashEntry(nameHashPtr
);
181 Tcl_AppendResult(interp
, "font \"", name
, "\" doesn't exist",
185 fontPtr
= (TkFont
*) ckalloc(sizeof(TkFont
));
186 fontPtr
->display
= nameKey
.display
;
187 fontPtr
->fontStructPtr
= fontStructPtr
;
188 fontPtr
->refCount
= 1;
189 fontPtr
->types
= NULL
;
190 fontPtr
->widths
= NULL
;
191 fontPtr
->nameHashPtr
= nameHashPtr
;
192 fontHashPtr
= Tcl_CreateHashEntry(&fontTable
, (char *) fontStructPtr
, &new);
194 panic("XFontStruct already registered in Tk_GetFontStruct");
196 Tcl_SetHashValue(nameHashPtr
, fontPtr
);
197 Tcl_SetHashValue(fontHashPtr
, fontPtr
);
198 return fontPtr
->fontStructPtr
;
202 *--------------------------------------------------------------
204 * Tk_NameOfFontStruct --
206 * Given a font, return a textual string identifying it.
209 * If font was created by Tk_GetFontStruct, then the return
210 * value is the "string" that was used to create it.
211 * Otherwise the return value is a string giving the X
212 * identifier for the font. The storage for the returned
213 * string is only guaranteed to persist up until the next
214 * call to this procedure.
219 *--------------------------------------------------------------
223 Tk_NameOfFontStruct (
224 XFontStruct
*fontStructPtr
/* Font whose name is desired. */
227 Tcl_HashEntry
*fontHashPtr
;
229 static char string
[20];
233 sprintf(string
, "font id 0x%x", fontStructPtr
->fid
);
236 fontHashPtr
= Tcl_FindHashEntry(&fontTable
, (char *) fontStructPtr
);
237 if (fontHashPtr
== NULL
) {
240 fontPtr
= (TkFont
*) Tcl_GetHashValue(fontHashPtr
);
241 return ((NameKey
*) fontPtr
->nameHashPtr
->key
.words
)->name
;
245 *----------------------------------------------------------------------
247 * Tk_FreeFontStruct --
249 * This procedure is called to release a font allocated by
256 * The reference count associated with font is decremented, and
257 * the font is officially deallocated if no-one is using it
260 *----------------------------------------------------------------------
265 XFontStruct
*fontStructPtr
/* Font to be released. */
268 Tcl_HashEntry
*fontHashPtr
;
269 register TkFont
*fontPtr
;
272 panic("Tk_FreeFontStruct called before Tk_GetFontStruct");
275 fontHashPtr
= Tcl_FindHashEntry(&fontTable
, (char *) fontStructPtr
);
276 if (fontHashPtr
== NULL
) {
277 panic("Tk_FreeFontStruct received unknown font argument");
279 fontPtr
= (TkFont
*) Tcl_GetHashValue(fontHashPtr
);
281 if (fontPtr
->refCount
== 0) {
282 XFreeFont(fontPtr
->display
, fontPtr
->fontStructPtr
);
283 Tcl_DeleteHashEntry(fontPtr
->nameHashPtr
);
284 Tcl_DeleteHashEntry(fontHashPtr
);
285 if (fontPtr
->types
!= NULL
) {
286 ckfree(fontPtr
->types
);
288 if (fontPtr
->widths
!= NULL
) {
289 ckfree((char *) fontPtr
->widths
);
291 ckfree((char *) fontPtr
);
292 lastFontStructPtr
= NULL
;
297 *----------------------------------------------------------------------
301 * Initialize the structure used for font management.
309 *----------------------------------------------------------------------
316 Tcl_InitHashTable(&nameTable
, sizeof(NameKey
)/sizeof(int));
317 Tcl_InitHashTable(&fontTable
, TCL_ONE_WORD_KEYS
);
321 *--------------------------------------------------------------
325 * This procedure is called to fill in the "widths" and "types"
332 * FontPtr gets modified to hold font metric information.
334 *--------------------------------------------------------------
339 register TkFont
*fontPtr
/* Font structure in which to
343 int i
, replaceOK
, baseWidth
;
344 register XFontStruct
*fontStructPtr
= fontPtr
->fontStructPtr
;
348 * Pass 1: initialize the arrays.
351 fontPtr
->types
= (char *) ckalloc(256);
352 fontPtr
->widths
= (unsigned char *) ckalloc(256);
353 for (i
= 0; i
< 256; i
++) {
354 fontPtr
->types
[i
] = REPLACE
;
358 * Pass 2: for all characters that exist in the font and are
359 * not control characters, fill in the type and width
363 for (i
= ' '; i
< 256; i
++) {
364 if ((i
== 0177) || (i
< fontStructPtr
->min_char_or_byte2
)
365 || (i
> fontStructPtr
->max_char_or_byte2
)) {
368 fontPtr
->types
[i
] = NORMAL
;
369 if (fontStructPtr
->per_char
== NULL
) {
370 fontPtr
->widths
[i
] = fontStructPtr
->min_bounds
.width
;
372 fontPtr
->widths
[i
] = fontStructPtr
->per_char
[i
373 - fontStructPtr
->min_char_or_byte2
].width
;
378 * Pass 3: fill in information for characters that have to
379 * be replaced with "\xhh" strings. If the font doesn't
380 * have the characters needed for this, then just use the
381 * font's default character.
385 baseWidth
= fontPtr
->widths
['\\'] + fontPtr
->widths
['x'];
386 for (p
= hexChars
; *p
!= 0; p
++) {
387 if (fontPtr
->types
[*p
] != NORMAL
) {
392 for (i
= 0; i
< 256; i
++) {
393 if (fontPtr
->types
[i
] != REPLACE
) {
397 fontPtr
->widths
[i
] = baseWidth
398 + fontPtr
->widths
[hexChars
[i
& 0xf]]
399 + fontPtr
->widths
[hexChars
[(i
>>4) & 0xf]];
401 fontPtr
->types
[i
] = SKIP
;
402 fontPtr
->widths
[i
] = 0;
407 * Lastly, fill in special information for newline and tab.
410 fontPtr
->types
['\n'] = NEWLINE
;
411 fontPtr
->widths
['\n'] = 0;
412 fontPtr
->types
['\t'] = TAB
;
413 fontPtr
->widths
['\t'] = 0;
414 if (fontPtr
->types
['0'] == NORMAL
) {
415 fontPtr
->tabWidth
= 8*fontPtr
->widths
['0'];
417 fontPtr
->tabWidth
= 8*fontStructPtr
->max_bounds
.width
;
421 * Make sure the tab width isn't zero (some fonts may not have enough
422 * information to set a reasonable tab width).
425 if (fontPtr
->tabWidth
== 0) {
426 fontPtr
->tabWidth
= 1;
431 *--------------------------------------------------------------
435 * Measure the number of characters from a string that
436 * will fit in a given horizontal span. The measurement
437 * is done under the assumption that TkDisplayChars will
438 * be used to actually display the characters.
441 * The return value is the number of characters from source
442 * that fit in the span given by startX and maxX. *nextXPtr
443 * is filled in with the x-coordinate at which the first
444 * character that didn't fit would be drawn, if it were to
450 *--------------------------------------------------------------
455 XFontStruct
*fontStructPtr
, /* Font in which to draw characters. */
456 char *source
, /* Characters to be displayed. Need not
457 * be NULL-terminated. */
458 int maxChars
, /* Maximum # of characters to consider from
460 int startX
, /* X-position at which first character will
462 int maxX
, /* Don't consider any character that would
463 * cross this x-position. */
464 int flags
, /* Various flag bits OR-ed together.
465 * TK_WHOLE_WORDS means stop on a word boundary
466 * (just before a space character) if
467 * possible. TK_AT_LEAST_ONE means always
468 * return a value of at least one, even
469 * if the character doesn't fit.
470 * TK_PARTIAL_OK means it's OK to display only
471 * a part of the last character in the line.
472 * TK_NEWLINES_NOT_SPECIAL means that newlines
473 * are treated just like other control chars:
474 * they don't terminate the line,*/
475 int *nextXPtr
/* Return x-position of terminating
479 register TkFont
*fontPtr
;
480 register char *p
; /* Current character. */
482 char *term
; /* Pointer to most recent character that
483 * may legally be a terminating character. */
484 int termX
; /* X-position just after term. */
485 int curX
; /* X-position corresponding to p. */
486 int newX
; /* X-position corresponding to p+1. */
490 * Find the TkFont structure for this font, and make sure its
491 * font metrics exist.
494 if (lastFontStructPtr
== fontStructPtr
) {
495 fontPtr
= lastFontPtr
;
497 Tcl_HashEntry
*fontHashPtr
;
501 panic("TkMeasureChars received unknown font argument");
504 fontHashPtr
= Tcl_FindHashEntry(&fontTable
, (char *) fontStructPtr
);
505 if (fontHashPtr
== NULL
) {
508 fontPtr
= (TkFont
*) Tcl_GetHashValue(fontHashPtr
);
509 lastFontStructPtr
= fontPtr
->fontStructPtr
;
510 lastFontPtr
= fontPtr
;
512 if (fontPtr
->types
== NULL
) {
513 SetFontMetrics(fontPtr
);
517 * Scan the input string one character at a time, until a character
518 * is found that crosses maxX.
521 newX
= curX
= startX
;
522 termX
= 0; /* Not needed, but eliminates compiler warning. */
524 for (p
= source
, c
= *p
& 0xff; maxChars
> 0; p
++, maxChars
--) {
525 type
= fontPtr
->types
[c
];
526 if (type
== NORMAL
) {
527 newX
+= fontPtr
->widths
[c
];
528 } else if (type
== TAB
) {
529 newX
+= fontPtr
->tabWidth
;
530 newX
-= newX
% fontPtr
->tabWidth
;
531 } else if (type
== REPLACE
) {
533 newX
+= fontPtr
->widths
['\\'] + fontPtr
->widths
['x']
534 + fontPtr
->widths
[hexChars
[(c
>> 4) & 0xf]]
535 + fontPtr
->widths
[hexChars
[c
& 0xf]];
536 } else if (type
== NEWLINE
) {
537 if (flags
& TK_NEWLINES_NOT_SPECIAL
) {
541 } else if (type
!= SKIP
) {
542 panic("Unknown type %d in TkMeasureChars", type
);
548 if (isspace(c
) || (c
== 0)) {
556 * P points to the first character that doesn't fit in the desired
557 * span. Use the flags to figure out what to return.
560 if ((flags
& TK_PARTIAL_OK
) && (curX
< maxX
)) {
564 if ((flags
& TK_AT_LEAST_ONE
) && (term
== source
) && (maxChars
> 0)
568 if (term
== source
) {
572 } else if ((maxChars
== 0) || !(flags
& TK_WHOLE_WORDS
)) {
581 *--------------------------------------------------------------
585 * Draw a string of characters on the screen, converting
586 * tabs to the right number of spaces and control characters
587 * to sequences of the form "\xhh" where hh are two hex
594 * Information gets drawn on the screen.
596 *--------------------------------------------------------------
601 Display
*display
, /* Display on which to draw. */
602 Drawable drawable
, /* Window or pixmap in which to draw. */
603 GC gc
, /* Graphics context for actually drawing
605 XFontStruct
*fontStructPtr
, /* Font used in GC; must have been allocated
606 * by Tk_GetFontStruct. Used to compute sizes
608 char *string
, /* Characters to be displayed. */
609 int numChars
, /* Number of characters to display from
612 int y
, /* Coordinates at which to draw string. */
613 int flags
/* Flags to control display. Only
614 * TK_NEWLINES_NOT_SPECIAL is supported right
615 * now. See TkMeasureChars for information
619 register TkFont
*fontPtr
;
620 register char *p
; /* Current character being scanned. */
623 char *start
; /* First character waiting to be displayed. */
624 int startX
; /* X-coordinate corresponding to start. */
625 int curX
; /* X-coordinate corresponding to p. */
629 * Find the TkFont structure for this font, and make sure its
630 * font metrics exist.
633 if (lastFontStructPtr
== fontStructPtr
) {
634 fontPtr
= lastFontPtr
;
636 Tcl_HashEntry
*fontHashPtr
;
640 panic("TkDisplayChars received unknown font argument");
643 fontHashPtr
= Tcl_FindHashEntry(&fontTable
, (char *) fontStructPtr
);
644 if (fontHashPtr
== NULL
) {
647 fontPtr
= (TkFont
*) Tcl_GetHashValue(fontHashPtr
);
648 lastFontStructPtr
= fontPtr
->fontStructPtr
;
649 lastFontPtr
= fontPtr
;
651 if (fontPtr
->types
== NULL
) {
652 SetFontMetrics(fontPtr
);
656 * Scan the string one character at a time. Display control
657 * characters immediately, but delay displaying normal characters
658 * in order to pass many characters to the server all together.
663 for (p
= string
; numChars
> 0; numChars
--, p
++) {
665 type
= fontPtr
->types
[c
];
666 if (type
== NORMAL
) {
667 curX
+= fontPtr
->widths
[c
];
671 XDrawString(display
, drawable
, gc
, startX
, y
, start
, p
- start
);
675 curX
+= fontPtr
->tabWidth
;
676 curX
-= curX
% fontPtr
->tabWidth
;
677 } else if (type
== REPLACE
) {
681 replace
[2] = hexChars
[(c
>> 4) & 0xf];
682 replace
[3] = hexChars
[c
& 0xf];
683 XDrawString(display
, drawable
, gc
, startX
, y
, replace
, 4);
684 curX
+= fontPtr
->widths
[replace
[0]]
685 + fontPtr
->widths
[replace
[1]]
686 + fontPtr
->widths
[replace
[2]]
687 + fontPtr
->widths
[replace
[3]];
688 } else if (type
== NEWLINE
) {
689 if (flags
& TK_NEWLINES_NOT_SPECIAL
) {
692 y
+= fontStructPtr
->ascent
+ fontStructPtr
->descent
;
694 } else if (type
!= SKIP
) {
695 panic("Unknown type %d in TkDisplayChars", type
);
702 * At the very end, there may be one last batch of normal characters
707 XDrawString(display
, drawable
, gc
, startX
, y
, start
, p
- start
);
712 *----------------------------------------------------------------------
714 * TkUnderlineChars --
716 * This procedure draws an underline for a given range of characters
717 * in a given string, using appropriate information for the string's
718 * font. It doesn't draw the characters (which are assumed to have
719 * been displayed previously); it just draws the underline.
725 * Information gets displayed in "drawable".
727 *----------------------------------------------------------------------
732 Display
*display
, /* Display on which to draw. */
733 Drawable drawable
, /* Window or pixmap in which to draw. */
734 GC gc
, /* Graphics context for actually drawing
736 XFontStruct
*fontStructPtr
, /* Font used in GC; must have been allocated
737 * by Tk_GetFontStruct. Used to character
738 * dimensions, etc. */
739 char *string
, /* String containing characters to be
742 int y
, /* Coordinates at which first character of
743 * string is drawn. */
744 int flags
, /* Flags that were passed to TkDisplayChars. */
745 int firstChar
, /* Index of first character to underline. */
746 int lastChar
/* Index of last character to underline. */
749 int xUnder
, yUnder
, width
, height
;
753 * First compute the vertical span of the underline, using font
754 * properties if they exist.
757 if (XGetFontProperty(fontStructPtr
, XA_UNDERLINE_POSITION
, &value
)) {
760 yUnder
= y
+ fontStructPtr
->max_bounds
.descent
/2;
762 if (XGetFontProperty(fontStructPtr
, XA_UNDERLINE_THICKNESS
, &value
)) {
769 * Now compute the horizontal span of the underline.
772 TkMeasureChars(fontStructPtr
, string
, firstChar
, x
, (int) 1000000, flags
,
774 TkMeasureChars(fontStructPtr
, string
+firstChar
, lastChar
+1-firstChar
,
775 xUnder
, (int) 1000000, flags
, &width
);
778 XFillRectangle(display
, drawable
, gc
, xUnder
, yUnder
,
779 (unsigned int) width
, (unsigned int) height
);