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