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