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