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