1/* 2 * tkMacOSXFont.c -- 3 * 4 * Contains the Macintosh implementation of the platform-independant 5 * font package interface. 6 * 7 * Copyright 2002-2004 Benjamin Riefenstahl, Benjamin.Riefenstahl@epost.de 8 * Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net> 9 * Copyright 2008-2009, Apple Inc. 10 * 11 * See the file "license.terms" for information on usage and redistribution 12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 13 * 14 * RCS: @(#) $Id$ 15 */ 16 17#include "tkMacOSXPrivate.h" 18#include "tkMacOSXFont.h" 19 20/* 21#ifdef TK_MAC_DEBUG 22#define TK_MAC_DEBUG_FONTS 23#endif 24*/ 25 26/* 27 * The following structure represents our Macintosh-specific implementation 28 * of a font object. 29 */ 30 31typedef struct { 32 TkFont font; /* Stuff used by generic font package. Must 33 * be first in structure. */ 34 35 NSFont *nsFont; 36 NSDictionary *nsAttributes; 37} MacFont; 38 39/* 40 * The names for our "native" fonts. 41 */ 42 43#define SYSTEMFONT_NAME "system" 44#define APPLFONT_NAME "application" 45#define MENUITEMFONT_NAME "menu" 46 47struct SystemFontMapEntry { 48 const ThemeFontID id; 49 const char *systemName; 50 const char *tkName; 51 const char *tkName1; 52}; 53 54#define ThemeFont(n, ...) { kTheme##n##Font, "system" #n "Font", ##__VA_ARGS__ } 55static const struct SystemFontMapEntry systemFontMap[] = { 56 ThemeFont(System, "TkDefaultFont", "TkIconFont"), 57 ThemeFont(EmphasizedSystem, "TkCaptionFont"), 58 ThemeFont(SmallSystem, "TkHeadingFont", "TkTooltipFont"), 59 ThemeFont(SmallEmphasizedSystem), 60 ThemeFont(Application, "TkTextFont"), 61 ThemeFont(Label, "TkSmallCaptionFont"), 62 ThemeFont(Views), 63 ThemeFont(MenuTitle), 64 ThemeFont(MenuItem, "TkMenuFont"), 65 ThemeFont(MenuItemMark), 66 ThemeFont(MenuItemCmdKey), 67 ThemeFont(WindowTitle), 68 ThemeFont(PushButton), 69 ThemeFont(UtilityWindowTitle), 70 ThemeFont(AlertHeader), 71 ThemeFont(Toolbar), 72 ThemeFont(MiniSystem), 73 { kThemeSystemFontDetail, "systemDetailSystemFont" }, 74 { kThemeSystemFontDetailEmphasized, "systemDetailEmphasizedSystemFont" }, 75 { -1, NULL } 76}; 77#undef ThemeFont 78 79static int antialiasedTextEnabled = -1; 80static NSCharacterSet *whitespaceCharacterSet = nil; 81static NSCharacterSet *lineendingCharacterSet = nil; 82 83static void GetTkFontAttributesForNSFont(NSFont *nsFont, 84 TkFontAttributes *faPtr); 85static NSFont *FindNSFont(const char *familyName, NSFontTraitMask traits, 86 NSInteger weight, CGFloat size, int fallbackToDefault); 87static void InitFont(NSFont *nsFont, const TkFontAttributes *reqFaPtr, 88 MacFont * fontPtr); 89static int CreateNamedSystemFont(Tcl_Interp *interp, Tk_Window tkwin, 90 const char* name, TkFontAttributes *faPtr); 91static void DrawCharsInContext(Display *display, Drawable drawable, GC gc, 92 Tk_Font tkfont, const char *source, int numBytes, int rangeStart, 93 int rangeLength, int x, int y, double angle); 94 95@interface NSFont(TKFont) 96- (NSFont *)bestMatchingFontForCharacters:(const UTF16Char *)characters 97 length:(NSUInteger)length attributes:(NSDictionary *)attributes 98 actualCoveredLength:(NSUInteger *)coveredLength; 99@end 100 101#pragma mark - 102#pragma mark Font Helpers: 103 104#define GetNSFontTraitsFromTkFontAttributes(faPtr) \ 105 ((faPtr)->weight == TK_FW_BOLD ? NSBoldFontMask : NSUnboldFontMask) | \ 106 ((faPtr)->slant == TK_FS_ITALIC ? NSItalicFontMask : NSUnitalicFontMask) 107 108/* 109 *--------------------------------------------------------------------------- 110 * 111 * GetTkFontAttributesForNSFont -- 112 * 113 * Fill in TkFontAttributes for given NSFont. 114 * 115 * Results: 116 * None. 117 * 118 * Side effects: 119 * None. 120 * 121 *--------------------------------------------------------------------------- 122 */ 123 124static void 125GetTkFontAttributesForNSFont( 126 NSFont *nsFont, 127 TkFontAttributes *faPtr) 128{ 129 NSFontTraitMask traits = [[NSFontManager sharedFontManager] 130 traitsOfFont:nsFont]; 131 132 faPtr->family = Tk_GetUid([[nsFont familyName] UTF8String]); 133 faPtr->size = [nsFont pointSize]; 134 faPtr->weight = (traits & NSBoldFontMask ? TK_FW_BOLD : TK_FW_NORMAL); 135 faPtr->slant = (traits & NSItalicFontMask ? TK_FS_ITALIC : TK_FS_ROMAN); 136} 137 138/* 139 *--------------------------------------------------------------------------- 140 * 141 * FindNSFont -- 142 * 143 * Find NSFont for given attributes. Use default values for missing 144 * attributes, and do a case-insensitive search for font family names 145 * if necessary. If fallbackToDefault flag is set, use the system font 146 * as a last resort. 147 * 148 * Results: 149 * None. 150 * 151 * Side effects: 152 * None. 153 * 154 *--------------------------------------------------------------------------- 155 */ 156 157static NSFont * 158FindNSFont( 159 const char *familyName, 160 NSFontTraitMask traits, 161 NSInteger weight, 162 CGFloat size, 163 int fallbackToDefault) 164{ 165 NSFontManager *fm = [NSFontManager sharedFontManager]; 166 NSFont *nsFont, *dflt = nil; 167 #define defaultFont (dflt ? dflt : (dflt = [NSFont systemFontOfSize:0])) 168 NSString *family; 169 170 if (familyName) { 171 family = [[[NSString alloc] initWithUTF8String:familyName] autorelease]; 172 } else { 173 family = [defaultFont familyName]; 174 } 175 if (size == 0.0) { 176 size = [defaultFont pointSize]; 177 } 178 nsFont = [fm fontWithFamily:family traits:traits weight:weight size:size]; 179 if (!nsFont) { 180 NSArray *availableFamilies = [fm availableFontFamilies]; 181 NSString *caseFamily = nil; 182 183 for (NSString *f in availableFamilies) { 184 if ([family caseInsensitiveCompare:f] == NSOrderedSame) { 185 caseFamily = f; 186 break; 187 } 188 } 189 if (caseFamily) { 190 nsFont = [fm fontWithFamily:caseFamily traits:traits weight:weight 191 size:size]; 192 } 193 } 194 if (!nsFont) { 195 nsFont = [NSFont fontWithName:family size:size]; 196 } 197 if (!nsFont && fallbackToDefault) { 198 nsFont = [fm convertFont:defaultFont toFamily:family]; 199 nsFont = [fm convertFont:nsFont toSize:size]; 200 nsFont = [fm convertFont:nsFont toHaveTrait:traits]; 201 } 202 #undef defaultFont 203 return nsFont; 204} 205 206/* 207 *--------------------------------------------------------------------------- 208 * 209 * InitFont -- 210 * 211 * Helper for TkpGetNativeFont() and TkpGetFontFromAttributes(). 212 * 213 * Results: 214 * Fills the MacFont structure. 215 * 216 * Side effects: 217 * Memory allocated. 218 * 219 *--------------------------------------------------------------------------- 220 */ 221 222static void 223InitFont( 224 NSFont *nsFont, 225 const TkFontAttributes *reqFaPtr, /* Can be NULL */ 226 MacFont *fontPtr) 227{ 228 TkFontAttributes *faPtr; 229 TkFontMetrics *fmPtr; 230 NSDictionary *nsAttributes; 231 NSRect bounds; 232 CGFloat kern = 0.0; 233 NSFontRenderingMode renderingMode = NSFontDefaultRenderingMode; 234 int ascent, descent, dontAA; 235 static const UniChar ch[] = {'.', 'W', ' ', 0xc4, 0xc1, 0xc2, 0xc3, 0xc7}; 236 /* ., W, Space, Auml, Aacute, Acirc, Atilde, Ccedilla */ 237 #define nCh (sizeof(ch) / sizeof(UniChar)) 238 CGGlyph glyphs[nCh]; 239 CGRect boundingRects[nCh]; 240 241 fontPtr->font.fid = (Font) fontPtr; 242 faPtr = &fontPtr->font.fa; 243 if (reqFaPtr) { 244 *faPtr = *reqFaPtr; 245 } else { 246 TkInitFontAttributes(faPtr); 247 } 248 fontPtr->nsFont = nsFont; 249 dontAA = [nsFont isFixedPitch] && fontPtr->font.fa.size <= 10; 250 if (antialiasedTextEnabled >= 0 || dontAA) { 251 renderingMode = (antialiasedTextEnabled == 0 || dontAA) ? 252 NSFontIntegerAdvancementsRenderingMode : 253 NSFontAntialiasedRenderingMode; 254 } 255 nsFont = [nsFont screenFontWithRenderingMode:renderingMode]; 256 GetTkFontAttributesForNSFont(nsFont, faPtr); 257 fmPtr = &fontPtr->font.fm; 258 fmPtr->ascent = floor([nsFont ascender] + [nsFont leading] + 0.5); 259 fmPtr->descent = floor(-[nsFont descender] + 0.5); 260 fmPtr->maxWidth = [nsFont maximumAdvancement].width; 261 fmPtr->fixed = [nsFont isFixedPitch]; /* Does not work for all fonts */ 262 263 /* 264 * The ascent, descent and fixed fields are not correct for all fonts, as 265 * a workaround deduce that info from the metrics of some typical glyphs, 266 * along with screenfont kerning (space advance difference to printer font) 267 */ 268 269 bounds = [nsFont boundingRectForFont]; 270 if (CTFontGetGlyphsForCharacters((CTFontRef) nsFont, ch, glyphs, nCh)) { 271 fmPtr->fixed = [nsFont advancementForGlyph:glyphs[0]].width == 272 [nsFont advancementForGlyph:glyphs[1]].width; 273 bounds = NSRectFromCGRect(CTFontGetBoundingRectsForGlyphs((CTFontRef) 274 nsFont, kCTFontDefaultOrientation, ch, boundingRects, nCh)); 275 kern = [nsFont advancementForGlyph:glyphs[2]].width - 276 [fontPtr->nsFont advancementForGlyph:glyphs[2]].width; 277 } 278 descent = floor(-bounds.origin.y + 0.5); 279 ascent = floor(bounds.size.height + bounds.origin.y + 0.5); 280 if (ascent > fmPtr->ascent) { 281 fmPtr->ascent = ascent; 282 } 283 if (descent > fmPtr->descent) { 284 fmPtr->descent = descent; 285 } 286 nsAttributes = [NSDictionary dictionaryWithObjectsAndKeys: 287 nsFont, NSFontAttributeName, 288 [NSNumber numberWithInt:faPtr->underline ? 289 NSUnderlineStyleSingle|NSUnderlinePatternSolid : 290 NSUnderlineStyleNone], NSUnderlineStyleAttributeName, 291 [NSNumber numberWithInt:faPtr->overstrike ? 292 NSUnderlineStyleSingle|NSUnderlinePatternSolid : 293 NSUnderlineStyleNone], NSStrikethroughStyleAttributeName, 294 [NSNumber numberWithInt:fmPtr->fixed ? 0 : 1], 295 NSLigatureAttributeName, 296 [NSNumber numberWithDouble:kern], NSKernAttributeName, nil]; 297 fontPtr->nsAttributes = TkMacOSXMakeUncollectableAndRetain(nsAttributes); 298 #undef nCh 299} 300 301/* 302 *------------------------------------------------------------------------- 303 * 304 * CreateNamedSystemFont -- 305 * 306 * Register a system font with the Tk named font mechanism. 307 * 308 * Results: 309 * 310 * Result from TkCreateNamedFont(). 311 * 312 * Side effects: 313 * 314 * A new named font is added to the Tk font registry. 315 * 316 *------------------------------------------------------------------------- 317 */ 318 319static int 320CreateNamedSystemFont( 321 Tcl_Interp *interp, 322 Tk_Window tkwin, 323 const char* name, 324 TkFontAttributes *faPtr) 325{ 326 TkDeleteNamedFont(NULL, tkwin, name); 327 return TkCreateNamedFont(interp, tkwin, name, faPtr); 328} 329 330#pragma mark - 331#pragma mark Font handling: 332 333/* 334 *------------------------------------------------------------------------- 335 * 336 * TkpFontPkgInit -- 337 * 338 * This procedure is called when an application is created. It 339 * initializes all the structures that are used by the 340 * platform-dependant code on a per application basis. 341 * Note that this is called before TkpInit() ! 342 * 343 * Results: 344 * None. 345 * 346 * Side effects: 347 * Initialize named system fonts. 348 * 349 *------------------------------------------------------------------------- 350 */ 351 352void 353TkpFontPkgInit( 354 TkMainInfo *mainPtr) /* The application being created. */ 355{ 356 Tcl_Interp *interp = mainPtr->interp; 357 Tk_Window tkwin = (Tk_Window) mainPtr->winPtr; 358 const struct SystemFontMapEntry *systemFont = systemFontMap; 359 NSFont *nsFont; 360 TkFontAttributes fa; 361 NSMutableCharacterSet *cs; 362 NSAutoreleasePool *pool = [NSAutoreleasePool new]; 363 364 /* force this for now */ 365 if (!mainPtr->winPtr->mainPtr) { 366 mainPtr->winPtr->mainPtr = mainPtr; 367 } 368 while (systemFont->systemName) { 369 nsFont = (NSFont*) CTFontCreateUIFontForLanguage( 370 HIThemeGetUIFontType(systemFont->id), 0, NULL); 371 if (nsFont) { 372 TkInitFontAttributes(&fa); 373 GetTkFontAttributesForNSFont(nsFont, &fa); 374 CreateNamedSystemFont(interp, tkwin, systemFont->systemName, &fa); 375 if (systemFont->tkName) { 376 CreateNamedSystemFont(interp, tkwin, systemFont->tkName, &fa); 377 } 378 if (systemFont->tkName1) { 379 CreateNamedSystemFont(interp, tkwin, systemFont->tkName1, &fa); 380 } 381 CFRelease(nsFont); 382 } 383 systemFont++; 384 } 385 TkInitFontAttributes(&fa); 386 nsFont = (NSFont*) CTFontCreateUIFontForLanguage( 387 kCTFontUserFixedPitchFontType, 11, NULL); 388 if (nsFont) { 389 GetTkFontAttributesForNSFont(nsFont, &fa); 390 CFRelease(nsFont); 391 } else { 392 fa.family = Tk_GetUid("Monaco"); 393 fa.size = 11; 394 fa.weight = TK_FW_NORMAL; 395 fa.slant = TK_FS_ROMAN; 396 } 397 CreateNamedSystemFont(interp, tkwin, "TkFixedFont", &fa); 398 if (!whitespaceCharacterSet) { 399 whitespaceCharacterSet = [[NSCharacterSet 400 whitespaceAndNewlineCharacterSet] retain]; 401 cs = [whitespaceCharacterSet mutableCopy]; 402 [cs removeCharactersInString:@" "]; 403 lineendingCharacterSet = [cs copy]; 404 [cs release]; 405 } 406 [pool drain]; 407} 408 409/* 410 *--------------------------------------------------------------------------- 411 * 412 * TkpGetNativeFont -- 413 * 414 * Map a platform-specific native font name to a TkFont. 415 * 416 * Results: 417 * The return value is a pointer to a TkFont that represents the 418 * native font. If a native font by the given name could not be 419 * found, the return value is NULL. 420 * 421 * Every call to this procedure returns a new TkFont structure, even 422 * if the name has already been seen before. The caller should call 423 * TkpDeleteFont() when the font is no longer needed. 424 * 425 * The caller is responsible for initializing the memory associated 426 * with the generic TkFont when this function returns and releasing 427 * the contents of the generics TkFont before calling TkpDeleteFont(). 428 * 429 * Side effects: 430 * None. 431 * 432 *--------------------------------------------------------------------------- 433 */ 434 435TkFont * 436TkpGetNativeFont( 437 Tk_Window tkwin, /* For display where font will be used. */ 438 const char *name) /* Platform-specific font name. */ 439{ 440 MacFont *fontPtr = NULL; 441 ThemeFontID themeFontId; 442 CTFontRef ctFont; 443 444 if (strcmp(name, SYSTEMFONT_NAME) == 0) { 445 themeFontId = kThemeSystemFont; 446 } else if (strcmp(name, APPLFONT_NAME) == 0) { 447 themeFontId = kThemeApplicationFont; 448 } else if (strcmp(name, MENUITEMFONT_NAME) == 0) { 449 themeFontId = kThemeMenuItemFont; 450 } else { 451 return NULL; 452 } 453 ctFont = CTFontCreateUIFontForLanguage(HIThemeGetUIFontType( 454 themeFontId), 0, NULL); 455 if (ctFont) { 456 fontPtr = (MacFont *) ckalloc(sizeof(MacFont)); 457 InitFont((NSFont*) ctFont, NULL, fontPtr); 458 } 459 460 return (TkFont *) fontPtr; 461} 462 463/* 464 *--------------------------------------------------------------------------- 465 * 466 * TkpGetFontFromAttributes -- 467 * 468 * Given a desired set of attributes for a font, find a font with the 469 * closest matching attributes. 470 * 471 * Results: 472 * The return value is a pointer to a TkFont that represents the font 473 * with the desired attributes. If a font with the desired attributes 474 * could not be constructed, some other font will be substituted 475 * automatically. 476 * 477 * Every call to this procedure returns a new TkFont structure, even 478 * if the specified attributes have already been seen before. The 479 * caller should call TkpDeleteFont() to free the platform- specific 480 * data when the font is no longer needed. 481 * 482 * The caller is responsible for initializing the memory associated 483 * with the generic TkFont when this function returns and releasing 484 * the contents of the generic TkFont before calling TkpDeleteFont(). 485 * 486 * Side effects: 487 * None. 488 * 489 *--------------------------------------------------------------------------- 490 */ 491 492TkFont * 493TkpGetFontFromAttributes( 494 TkFont *tkFontPtr, /* If non-NULL, store the information in this 495 * existing TkFont structure, rather than 496 * allocating a new structure to hold the 497 * font; the existing contents of the font 498 * will be released. If NULL, a new TkFont 499 * structure is allocated. */ 500 Tk_Window tkwin, /* For display where font will be used. */ 501 const TkFontAttributes *faPtr) 502 /* Set of attributes to match. */ 503{ 504 MacFont *fontPtr; 505 int points = TkFontGetPoints(tkwin, faPtr->size); 506 NSFontTraitMask traits = GetNSFontTraitsFromTkFontAttributes(faPtr); 507 NSInteger weight = (faPtr->weight == TK_FW_BOLD ? 9 : 5); 508 NSFont *nsFont; 509 510 nsFont = FindNSFont(faPtr->family, traits, weight, points, 0); 511 if (!nsFont) { 512 char *const *aliases = TkFontGetAliasList(faPtr->family); 513 514 while (aliases && !nsFont) { 515 nsFont = FindNSFont(*aliases++, traits, weight, points, 0); 516 } 517 } 518 if (!nsFont) { 519 nsFont = FindNSFont(faPtr->family, traits, weight, points, 1); 520 } 521 if (!nsFont) { 522 Tcl_Panic("Could not deternmine NSFont from TkFontAttributes"); 523 } 524 if (tkFontPtr == NULL) { 525 fontPtr = (MacFont *) ckalloc(sizeof(MacFont)); 526 } else { 527 fontPtr = (MacFont *) tkFontPtr; 528 TkpDeleteFont(tkFontPtr); 529 } 530 CFRetain(nsFont); /* Always needed to allow unconditional CFRelease below */ 531 InitFont(nsFont, faPtr, fontPtr); 532 533 return (TkFont *) fontPtr; 534} 535 536/* 537 *--------------------------------------------------------------------------- 538 * 539 * TkpDeleteFont -- 540 * 541 * Called to release a font allocated by TkpGetNativeFont() or 542 * TkpGetFontFromAttributes(). The caller should have already 543 * released the fields of the TkFont that are used exclusively by the 544 * generic TkFont code. 545 * 546 * Results: 547 * TkFont is deallocated. 548 * 549 * Side effects: 550 * None. 551 * 552 *--------------------------------------------------------------------------- 553 */ 554 555void 556TkpDeleteFont( 557 TkFont *tkFontPtr) /* Token of font to be deleted. */ 558{ 559 MacFont *fontPtr = (MacFont *) tkFontPtr; 560 561 TkMacOSXMakeCollectableAndRelease(fontPtr->nsAttributes); 562 CFRelease(fontPtr->nsFont); /* Either a CTFontRef or a CFRetained NSFont */ 563} 564 565/* 566 *--------------------------------------------------------------------------- 567 * 568 * TkpGetFontFamilies -- 569 * 570 * Return information about the font families that are available on 571 * the display of the given window. 572 * 573 * Results: 574 * Modifies interp's result object to hold a list of all the available 575 * font families. 576 * 577 * Side effects: 578 * None. 579 * 580 *--------------------------------------------------------------------------- 581 */ 582 583void 584TkpGetFontFamilies( 585 Tcl_Interp *interp, /* Interp to hold result. */ 586 Tk_Window tkwin) /* For display to query. */ 587{ 588 Tcl_Obj *resultPtr = Tcl_NewListObj(0, NULL); 589 NSArray *list = [[NSFontManager sharedFontManager] availableFontFamilies]; 590 591 for (NSString *family in list) { 592 Tcl_ListObjAppendElement(NULL, resultPtr, 593 Tcl_NewStringObj([family UTF8String], -1)); 594 } 595 Tcl_SetObjResult(interp, resultPtr); 596} 597 598/* 599 *------------------------------------------------------------------------- 600 * 601 * TkpGetSubFonts -- 602 * 603 * A function used by the testing package for querying the actual 604 * screen fonts that make up a font object. 605 * 606 * Results: 607 * Modifies interp's result object to hold a list containing the names 608 * of the screen fonts that make up the given font object. 609 * 610 * Side effects: 611 * None. 612 * 613 *------------------------------------------------------------------------- 614 */ 615 616void 617TkpGetSubFonts( 618 Tcl_Interp *interp, /* Interp to hold result. */ 619 Tk_Font tkfont) /* Font object to query. */ 620{ 621 MacFont *fontPtr = (MacFont *) tkfont; 622 Tcl_Obj *resultPtr = Tcl_NewListObj(0, NULL); 623 624 if (fontPtr->nsFont) { 625 NSArray *list = [[fontPtr->nsFont fontDescriptor] 626 objectForKey:NSFontCascadeListAttribute]; 627 628 for (NSFontDescriptor *subFontDesc in list) { 629 NSString *family = [subFontDesc objectForKey:NSFontFamilyAttribute]; 630 631 if (family) { 632 Tcl_ListObjAppendElement(NULL, resultPtr, 633 Tcl_NewStringObj([family UTF8String], -1)); 634 } 635 } 636 } 637 Tcl_SetObjResult(interp, resultPtr); 638} 639 640/* 641 *---------------------------------------------------------------------- 642 * 643 * TkpGetFontAttrsForChar -- 644 * 645 * Retrieve the font attributes of the actual font used to render a 646 * given character. 647 * 648 * Results: 649 * None. 650 * 651 * Side effects: 652 * The font attributes are stored in *faPtr. 653 * 654 *---------------------------------------------------------------------- 655 */ 656 657void 658TkpGetFontAttrsForChar( 659 Tk_Window tkwin, /* Window on the font's display */ 660 Tk_Font tkfont, /* Font to query */ 661 Tcl_UniChar c, /* Character of interest */ 662 TkFontAttributes* faPtr) /* Output: Font attributes */ 663{ 664 MacFont *fontPtr = (MacFont *) tkfont; 665 NSFont *nsFont = fontPtr->nsFont; 666 667 *faPtr = fontPtr->font.fa; 668 if (nsFont && ![[nsFont coveredCharacterSet] characterIsMember:c]) { 669 UTF16Char ch = c; 670 671 nsFont = [nsFont bestMatchingFontForCharacters:&ch 672 length:1 attributes:nil actualCoveredLength:NULL]; 673 if (nsFont) { 674 GetTkFontAttributesForNSFont(nsFont, faPtr); 675 } 676 } 677} 678 679#pragma mark - 680#pragma mark Measuring and drawing: 681 682/* 683 *--------------------------------------------------------------------------- 684 * 685 * Tk_MeasureChars -- 686 * 687 * Determine the number of characters from the string that will fit in 688 * the given horizontal span. The measurement is done under the 689 * assumption that Tk_DrawChars() will be used to actually display the 690 * characters. 691 * 692 * With ATSUI we need the line context to do this right, so we have the 693 * actual implementation in TkpMeasureCharsInContext(). 694 * 695 * Results: 696 * The return value is the number of bytes from source that fit into the 697 * span that extends from 0 to maxLength. *lengthPtr is filled with the 698 * x-coordinate of the right edge of the last character that did fit. 699 * 700 * Side effects: 701 * None. 702 * 703 * Todo: 704 * Effects of the "flags" parameter are untested. 705 * 706 *--------------------------------------------------------------------------- 707 */ 708 709int 710Tk_MeasureChars( 711 Tk_Font tkfont, /* Font in which characters will be drawn. */ 712 const char *source, /* UTF-8 string to be displayed. Need not be 713 * '\0' terminated. */ 714 int numBytes, /* Maximum number of bytes to consider from 715 * source string. */ 716 int maxLength, /* If >= 0, maxLength specifies the longest 717 * permissible line length; don't consider any 718 * character that would cross this x-position. 719 * If < 0, then line length is unbounded and 720 * the flags argument is ignored. */ 721 int flags, /* Various flag bits OR-ed together: 722 * TK_PARTIAL_OK means include the last char 723 * which only partially fit on this line. 724 * TK_WHOLE_WORDS means stop on a word 725 * boundary, if possible. TK_AT_LEAST_ONE 726 * means return at least one character even if 727 * no characters fit. */ 728 int *lengthPtr) /* Filled with x-location just after the 729 * terminating character. */ 730{ 731 return TkpMeasureCharsInContext(tkfont, source, numBytes, 0, numBytes, 732 maxLength, flags, lengthPtr); 733} 734 735/* 736 *--------------------------------------------------------------------------- 737 * 738 * TkpMeasureCharsInContext -- 739 * 740 * Determine the number of bytes from the string that will fit in the 741 * given horizontal span. The measurement is done under the assumption 742 * that TkpDrawCharsInContext() will be used to actually display the 743 * characters. 744 * 745 * This one is almost the same as Tk_MeasureChars(), but with access to 746 * all the characters on the line for context. 747 * 748 * Results: 749 * The return value is the number of bytes from source that 750 * fit into the span that extends from 0 to maxLength. *lengthPtr is 751 * filled with the x-coordinate of the right edge of the last 752 * character that did fit. 753 * 754 * Side effects: 755 * None. 756 * 757 *--------------------------------------------------------------------------- 758 */ 759 760int 761TkpMeasureCharsInContext( 762 Tk_Font tkfont, /* Font in which characters will be drawn. */ 763 const char * source, /* UTF-8 string to be displayed. Need not be 764 * '\0' terminated. */ 765 int numBytes, /* Maximum number of bytes to consider from 766 * source string in all. */ 767 int rangeStart, /* Index of first byte to measure. */ 768 int rangeLength, /* Length of range to measure in bytes. */ 769 int maxLength, /* If >= 0, maxLength specifies the longest 770 * permissible line length; don't consider any 771 * character that would cross this x-position. 772 * If < 0, then line length is unbounded and 773 * the flags argument is ignored. */ 774 int flags, /* Various flag bits OR-ed together: 775 * TK_PARTIAL_OK means include the last char 776 * which only partially fits on this line. 777 * TK_WHOLE_WORDS means stop on a word 778 * boundary, if possible. TK_AT_LEAST_ONE 779 * means return at least one character even 780 * if no characters fit. If TK_WHOLE_WORDS 781 * and TK_AT_LEAST_ONE are set and the first 782 * word doesn't fit, we return at least one 783 * character or whatever characters fit into 784 * maxLength. TK_ISOLATE_END means that the 785 * last character should not be considered in 786 * context with the rest of the string (used 787 * for breaking lines). */ 788 int *lengthPtr) /* Filled with x-location just after the 789 * terminating character. */ 790{ 791 const MacFont *fontPtr = (const MacFont *) tkfont; 792 NSString *string; 793 NSAttributedString *attributedString; 794 CTTypesetterRef typesetter; 795 CFIndex start, len; 796 CFRange range = {0, 0}; 797 CTLineRef line; 798 CGFloat offset = 0; 799 CFIndex index; 800 double width; 801 int length, fit; 802 803 if (rangeStart < 0 || rangeLength <= 0 || 804 rangeStart + rangeLength > numBytes || 805 (maxLength == 0 && !(flags & TK_AT_LEAST_ONE))) { 806 *lengthPtr = 0; 807 return 0; 808 } 809#if 0 810 /* Back-compatibility with ATSUI renderer, appears not to be needed */ 811 if (rangeStart == 0 && maxLength == 1 && (flags & TK_ISOLATE_END) && 812 !(flags & TK_AT_LEAST_ONE)) { 813 length = 0; 814 fit = 0; 815 goto done; 816 } 817#endif 818 if (maxLength > 32767) { 819 maxLength = 32767; 820 } 821 string = [[NSString alloc] initWithBytesNoCopy:(void*)source 822 length:numBytes encoding:NSUTF8StringEncoding freeWhenDone:NO]; 823 if (!string) { 824 length = 0; 825 fit = rangeLength; 826 goto done; 827 } 828 attributedString = [[NSAttributedString alloc] initWithString:string 829 attributes:fontPtr->nsAttributes]; 830 typesetter = CTTypesetterCreateWithAttributedString( 831 (CFAttributedStringRef)attributedString); 832 start = Tcl_NumUtfChars(source, rangeStart); 833 len = Tcl_NumUtfChars(source, rangeStart + rangeLength); 834 if (start > 0) { 835 range.length = start; 836 line = CTTypesetterCreateLine(typesetter, range); 837 offset = CTLineGetTypographicBounds(line, NULL, NULL, NULL); 838 CFRelease(line); 839 } 840 if (maxLength < 0) { 841 index = len; 842 range.length = len; 843 line = CTTypesetterCreateLine(typesetter, range); 844 width = CTLineGetTypographicBounds(line, NULL, NULL, NULL); 845 CFRelease(line); 846 } else { 847 double maxWidth = maxLength + offset; 848 NSCharacterSet *cs; 849 850 index = start; 851 if (flags & TK_WHOLE_WORDS) { 852 index = CTTypesetterSuggestLineBreak(typesetter, 0, maxWidth); 853 if (index <= start && (flags & TK_AT_LEAST_ONE)) { 854 flags &= ~TK_WHOLE_WORDS; 855 } 856 } 857 if (index <= start && !(flags & TK_WHOLE_WORDS)) { 858 index = CTTypesetterSuggestClusterBreak(typesetter, 0, maxWidth); 859 } 860 cs = (index < len || (flags & TK_WHOLE_WORDS)) ? 861 whitespaceCharacterSet : lineendingCharacterSet; 862 while (index > start && 863 [cs characterIsMember:[string characterAtIndex:(index - 1)]]) { 864 index--; 865 } 866 if (index <= start && (flags & TK_AT_LEAST_ONE)) { 867 index = start + 1; 868 } 869 if (index > 0) { 870 range.length = index; 871 line = CTTypesetterCreateLine(typesetter, range); 872 width = CTLineGetTypographicBounds(line, NULL, NULL, NULL); 873 CFRelease(line); 874 } else { 875 width = 0; 876 } 877 if (width < maxWidth && (flags & TK_PARTIAL_OK) && index < len) { 878 range.length = ++index; 879 line = CTTypesetterCreateLine(typesetter, range); 880 width = CTLineGetTypographicBounds(line, NULL, NULL, NULL); 881 CFRelease(line); 882 } 883 } 884 CFRelease(typesetter); 885 [attributedString release]; 886 [string release]; 887 length = lround(width - offset); 888 fit = (Tcl_UtfAtIndex(source, index) - source) - rangeStart; 889done: 890#ifdef TK_MAC_DEBUG_FONTS 891 TkMacOSXDbgMsg("measure: source=\"%s\" range=\"%.*s\" maxLength=%d " 892 "flags='%s%s%s%s' -> width=%d bytesFit=%d\n", source, rangeLength, 893 source+rangeStart, maxLength, 894 flags & TK_PARTIAL_OK ? "partialOk " : "", 895 flags & TK_WHOLE_WORDS ? "wholeWords " : "", 896 flags & TK_AT_LEAST_ONE ? "atLeastOne " : "", 897 flags & TK_ISOLATE_END ? "isolateEnd " : "", 898 length, fit); 899#endif 900 *lengthPtr = length; 901 return fit; 902} 903 904/* 905 *--------------------------------------------------------------------------- 906 * 907 * Tk_DrawChars -- 908 * 909 * Draw a string of characters on the screen. 910 * 911 * With ATSUI we need the line context to do this right, so we have the 912 * actual implementation in TkpDrawCharsInContext(). 913 * 914 * Results: 915 * None. 916 * 917 * Side effects: 918 * Information gets drawn on the screen. 919 * 920 *--------------------------------------------------------------------------- 921 */ 922 923void 924Tk_DrawChars( 925 Display *display, /* Display on which to draw. */ 926 Drawable drawable, /* Window or pixmap in which to draw. */ 927 GC gc, /* Graphics context for drawing characters. */ 928 Tk_Font tkfont, /* Font in which characters will be drawn; must 929 * be the same as font used in GC. */ 930 const char *source, /* UTF-8 string to be displayed. Need not be 931 * '\0' terminated. All Tk meta-characters 932 * (tabs, control characters, and newlines) 933 * should be stripped out of the string that 934 * is passed to this function. If they are not 935 * stripped out, they will be displayed as 936 * regular printing characters. */ 937 int numBytes, /* Number of bytes in string. */ 938 int x, int y) /* Coordinates at which to place origin of the 939 * string when drawing. */ 940{ 941 DrawCharsInContext(display, drawable, gc, tkfont, source, numBytes, 942 0, numBytes, x, y, 0.0); 943} 944 945/* 946 *--------------------------------------------------------------------------- 947 * 948 * TkpDrawCharsInContext -- 949 * 950 * Draw a string of characters on the screen like Tk_DrawChars(), with 951 * access to all the characters on the line for context. 952 * 953 * Results: 954 * None. 955 * 956 * Side effects: 957 * Information gets drawn on the screen. 958 * 959 * Todo: 960 * Stippled text drawing. 961 * 962 *--------------------------------------------------------------------------- 963 */ 964 965void 966TkpDrawCharsInContext( 967 Display *display, /* Display on which to draw. */ 968 Drawable drawable, /* Window or pixmap in which to draw. */ 969 GC gc, /* Graphics context for drawing characters. */ 970 Tk_Font tkfont, /* Font in which characters will be drawn; must 971 * be the same as font used in GC. */ 972 const char * source, /* UTF-8 string to be displayed. Need not be 973 * '\0' terminated. All Tk meta-characters 974 * (tabs, control characters, and newlines) 975 * should be stripped out of the string that 976 * is passed to this function. If they are not 977 * stripped out, they will be displayed as 978 * regular printing characters. */ 979 int numBytes, /* Number of bytes in string. */ 980 int rangeStart, /* Index of first byte to draw. */ 981 int rangeLength, /* Length of range to draw in bytes. */ 982 int x, int y) /* Coordinates at which to place origin of the 983 * whole (not just the range) string when 984 * drawing. */ 985{ 986 DrawCharsInContext(display, drawable, gc, tkfont, source, numBytes, 987 rangeStart, rangeLength, x, y, 0.0); 988} 989 990static void 991DrawCharsInContext( 992 Display *display, /* Display on which to draw. */ 993 Drawable drawable, /* Window or pixmap in which to draw. */ 994 GC gc, /* Graphics context for drawing characters. */ 995 Tk_Font tkfont, /* Font in which characters will be drawn; must 996 * be the same as font used in GC. */ 997 const char * source, /* UTF-8 string to be displayed. Need not be 998 * '\0' terminated. All Tk meta-characters 999 * (tabs, control characters, and newlines) 1000 * should be stripped out of the string that 1001 * is passed to this function. If they are not 1002 * stripped out, they will be displayed as 1003 * regular printing characters. */ 1004 int numBytes, /* Number of bytes in string. */ 1005 int rangeStart, /* Index of first byte to draw. */ 1006 int rangeLength, /* Length of range to draw in bytes. */ 1007 int x, int y, /* Coordinates at which to place origin of the 1008 * whole (not just the range) string when 1009 * drawing. */ 1010 double angle) 1011{ 1012 const MacFont *fontPtr = (const MacFont *) tkfont; 1013 NSString *string; 1014 NSMutableDictionary *attributes; 1015 NSAttributedString *attributedString; 1016 CTTypesetterRef typesetter; 1017 CFIndex start, len; 1018 CTLineRef line; 1019 MacDrawable *macWin = (MacDrawable *) drawable; 1020 TkMacOSXDrawingContext drawingContext; 1021 CGContextRef context; 1022 CGColorRef fg; 1023 NSFont *nsFont; 1024 CGAffineTransform t; 1025 int h; 1026 1027 if (rangeStart < 0 || rangeLength <= 0 || 1028 rangeStart + rangeLength > numBytes || 1029 !TkMacOSXSetupDrawingContext(drawable, gc, 1, &drawingContext)) { 1030 return; 1031 } 1032 string = [[NSString alloc] initWithBytesNoCopy:(void*)source 1033 length:numBytes encoding:NSUTF8StringEncoding freeWhenDone:NO]; 1034 if (!string) { 1035 return; 1036 } 1037 context = drawingContext.context; 1038 fg = TkMacOSXCreateCGColor(gc, gc->foreground); 1039 attributes = [fontPtr->nsAttributes mutableCopy]; 1040 [attributes setObject:(id)fg forKey:(id)kCTForegroundColorAttributeName]; 1041 CFRelease(fg); 1042 nsFont = [attributes objectForKey:NSFontAttributeName]; 1043 [nsFont setInContext:[NSGraphicsContext graphicsContextWithGraphicsPort: 1044 context flipped:NO]]; 1045 CGContextSetTextMatrix(context, CGAffineTransformIdentity); 1046 attributedString = [[NSAttributedString alloc] initWithString:string 1047 attributes:attributes]; 1048 typesetter = CTTypesetterCreateWithAttributedString( 1049 (CFAttributedStringRef)attributedString); 1050 x += macWin->xOff; 1051 y += macWin->yOff; 1052 h = drawingContext.portBounds.size.height; 1053 y = h - y; 1054 t = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, h); 1055 if (angle != 0.0) { 1056 t = CGAffineTransformTranslate(CGAffineTransformRotate( 1057 CGAffineTransformTranslate(t, x, y), angle*M_PI/180.0), -x, -y); 1058 } 1059 CGContextConcatCTM(context, t); 1060 CGContextSetTextPosition(context, x, y); 1061 start = Tcl_NumUtfChars(source, rangeStart); 1062 len = Tcl_NumUtfChars(source, rangeStart + rangeLength); 1063 if (start > 0) { 1064 CGRect clipRect = CGRectInfinite, startBounds; 1065 line = CTTypesetterCreateLine(typesetter, CFRangeMake(0, start)); 1066 startBounds = CTLineGetImageBounds(line, context); 1067 CFRelease(line); 1068 clipRect.origin.x = startBounds.origin.x + startBounds.size.width; 1069 CGContextClipToRect(context, clipRect); 1070 } 1071 line = CTTypesetterCreateLine(typesetter, CFRangeMake(0, len)); 1072 CTLineDraw(line, context); 1073 CFRelease(line); 1074 CFRelease(typesetter); 1075 [attributedString release]; 1076 [string release]; 1077 [attributes release]; 1078 TkMacOSXRestoreDrawingContext(&drawingContext); 1079} 1080 1081#pragma mark - 1082#pragma mark Accessors: 1083 1084/* 1085 *--------------------------------------------------------------------------- 1086 * 1087 * TkMacOSXNSFontForFont -- 1088 * 1089 * Return an NSFont for the given Tk_Font. 1090 * 1091 * Results: 1092 * NSFont*. 1093 * 1094 * Side effects: 1095 * None. 1096 * 1097 *--------------------------------------------------------------------------- 1098 */ 1099 1100MODULE_SCOPE NSFont* 1101TkMacOSXNSFontForFont( 1102 Tk_Font tkfont) 1103{ 1104 return tkfont ? ((MacFont *)tkfont)->nsFont : nil; 1105} 1106 1107/* 1108 *--------------------------------------------------------------------------- 1109 * 1110 * TkMacOSXNSFontAttributesForFont -- 1111 * 1112 * Return an NSDictionary of font attributes for the given Tk_Font. 1113 * 1114 * Results: 1115 * NSFont*. 1116 * 1117 * Side effects: 1118 * None. 1119 * 1120 *--------------------------------------------------------------------------- 1121 */ 1122 1123MODULE_SCOPE NSDictionary* 1124TkMacOSXNSFontAttributesForFont( 1125 Tk_Font tkfont) 1126{ 1127 return tkfont ? ((MacFont *)tkfont)->nsAttributes : nil; 1128} 1129 1130/* 1131 *--------------------------------------------------------------------------- 1132 * 1133 * TkMacOSXIsCharacterMissing -- 1134 * 1135 * Given a tkFont and a character determine whether the character has 1136 * a glyph defined in the font or not. 1137 * 1138 * Results: 1139 * Returns a 1 if the character is missing, a 0 if it is not. 1140 * 1141 * Side effects: 1142 * None. 1143 * 1144 *--------------------------------------------------------------------------- 1145 */ 1146 1147int 1148TkMacOSXIsCharacterMissing( 1149 Tk_Font tkfont, /* The font we are looking in. */ 1150 unsigned int searchChar) /* The character we are looking for. */ 1151{ 1152 return 0; 1153} 1154 1155/* 1156 *---------------------------------------------------------------------- 1157 * 1158 * TkMacOSXUseAntialiasedText -- 1159 * 1160 * Enables or disables application-wide use of antialiased text (where 1161 * available). Sets up a linked Tcl global variable to allow 1162 * disabling of antialiased text from tcl. 1163 * The possible values for this variable are: 1164 * 1165 * -1 - Use system default as configurable in "System Prefs" -> "General". 1166 * 0 - Unconditionally disable antialiasing. 1167 * 1 - Unconditionally enable antialiasing. 1168 * 1169 * Results: 1170 * 1171 * TCL_OK. 1172 * 1173 * Side effects: 1174 * 1175 * None. 1176 * 1177 *---------------------------------------------------------------------- 1178 */ 1179 1180MODULE_SCOPE int 1181TkMacOSXUseAntialiasedText( 1182 Tcl_Interp * interp, /* The Tcl interpreter to receive the 1183 * variable.*/ 1184 int enable) /* Initial value. */ 1185{ 1186 static Boolean initialized = FALSE; 1187 1188 if (!initialized) { 1189 initialized = TRUE; 1190 1191 if (Tcl_CreateNamespace(interp, "::tk::mac", NULL, NULL) == NULL) { 1192 Tcl_ResetResult(interp); 1193 } 1194 if (Tcl_LinkVar(interp, "::tk::mac::antialiasedtext", 1195 (char *) &antialiasedTextEnabled, 1196 TCL_LINK_INT) != TCL_OK) { 1197 Tcl_ResetResult(interp); 1198 } 1199 } 1200 antialiasedTextEnabled = enable; 1201 return TCL_OK; 1202} 1203 1204/* 1205 * Local Variables: 1206 * mode: c 1207 * c-basic-offset: 4 1208 * fill-column: 79 1209 * coding: utf-8 1210 * End: 1211 */ 1212