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