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