Files
cpython-source-deps/macosx/tkMacOSXFont.c
Cheryl Sabella 8e57feeeb9 Import Tk 8.6.8
2018-02-22 14:31:15 -05:00

1313 lines
40 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* tkMacOSXFont.c --
*
* Contains the Macintosh implementation of the platform-independant
* font package interface.
*
* Copyright 2002-2004 Benjamin Riefenstahl, Benjamin.Riefenstahl@epost.de
* Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net>
* Copyright 2008-2009, Apple Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
#include "tkMacOSXPrivate.h"
#include "tkMacOSXFont.h"
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080
#define defaultOrientation kCTFontDefaultOrientation
#define verticalOrientation kCTFontVerticalOrientation
#else
#define defaultOrientation kCTFontOrientationDefault
#define verticalOrientation kCTFontOrientationVertical
#endif
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101100
#define fixedPitch kCTFontUserFixedPitchFontType
#else
#define fixedPitch kCTFontUIFontUserFixedPitch
#endif
/*
#ifdef TK_MAC_DEBUG
#define TK_MAC_DEBUG_FONTS
#endif
*/
/*
* The following structure represents our Macintosh-specific implementation
* of a font object.
*/
typedef struct {
TkFont font; /* Stuff used by generic font package. Must
* be first in structure. */
NSFont *nsFont;
NSDictionary *nsAttributes;
} MacFont;
/*
* The names for our "native" fonts.
*/
#define SYSTEMFONT_NAME "system"
#define APPLFONT_NAME "application"
#define MENUITEMFONT_NAME "menu"
struct SystemFontMapEntry {
const ThemeFontID id;
const char *systemName;
const char *tkName;
const char *tkName1;
};
#define ThemeFont(n, ...) { kTheme##n##Font, "system" #n "Font", ##__VA_ARGS__ }
static const struct SystemFontMapEntry systemFontMap[] = {
ThemeFont(System, "TkDefaultFont", "TkIconFont"),
ThemeFont(EmphasizedSystem, "TkCaptionFont"),
ThemeFont(SmallSystem, "TkHeadingFont", "TkTooltipFont"),
ThemeFont(SmallEmphasizedSystem),
ThemeFont(Application, "TkTextFont"),
ThemeFont(Label, "TkSmallCaptionFont"),
ThemeFont(Views),
ThemeFont(MenuTitle),
ThemeFont(MenuItem, "TkMenuFont"),
ThemeFont(MenuItemMark),
ThemeFont(MenuItemCmdKey),
ThemeFont(WindowTitle),
ThemeFont(PushButton),
ThemeFont(UtilityWindowTitle),
ThemeFont(AlertHeader),
ThemeFont(Toolbar),
ThemeFont(MiniSystem),
{ kThemeSystemFontDetail, "systemDetailSystemFont" },
{ kThemeSystemFontDetailEmphasized, "systemDetailEmphasizedSystemFont" },
{ -1, NULL }
};
#undef ThemeFont
static int antialiasedTextEnabled = -1;
static NSCharacterSet *whitespaceCharacterSet = nil;
static NSCharacterSet *lineendingCharacterSet = nil;
static void GetTkFontAttributesForNSFont(NSFont *nsFont,
TkFontAttributes *faPtr);
static NSFont *FindNSFont(const char *familyName, NSFontTraitMask traits,
NSInteger weight, CGFloat size, int fallbackToDefault);
static void InitFont(NSFont *nsFont, const TkFontAttributes *reqFaPtr,
MacFont * fontPtr);
static int CreateNamedSystemFont(Tcl_Interp *interp, Tk_Window tkwin,
const char* name, TkFontAttributes *faPtr);
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);
@interface NSFont(TKFont)
- (NSFont *) bestMatchingFontForCharacters: (const UTF16Char *) characters
length: (NSUInteger) length attributes: (NSDictionary *) attributes
actualCoveredLength: (NSUInteger *) coveredLength;
@end
#pragma mark -
#pragma mark Font Helpers:
#define GetNSFontTraitsFromTkFontAttributes(faPtr) \
((faPtr)->weight == TK_FW_BOLD ? NSBoldFontMask : NSUnboldFontMask) | \
((faPtr)->slant == TK_FS_ITALIC ? NSItalicFontMask : NSUnitalicFontMask)
/*
*---------------------------------------------------------------------------
*
* GetTkFontAttributesForNSFont --
*
* Fill in TkFontAttributes for given NSFont.
*
* Results:
* None.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
static void
GetTkFontAttributesForNSFont(
NSFont *nsFont,
TkFontAttributes *faPtr)
{
NSFontTraitMask traits = [[NSFontManager sharedFontManager]
traitsOfFont:nsFont];
faPtr->family = Tk_GetUid([[nsFont familyName] UTF8String]);
faPtr->size = [nsFont pointSize];
faPtr->weight = (traits & NSBoldFontMask ? TK_FW_BOLD : TK_FW_NORMAL);
faPtr->slant = (traits & NSItalicFontMask ? TK_FS_ITALIC : TK_FS_ROMAN);
}
/*
*---------------------------------------------------------------------------
*
* FindNSFont --
*
* Find NSFont for given attributes. Use default values for missing
* attributes, and do a case-insensitive search for font family names
* if necessary. If fallbackToDefault flag is set, use the system font
* as a last resort.
*
* Results:
* None.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
static NSFont *
FindNSFont(
const char *familyName,
NSFontTraitMask traits,
NSInteger weight,
CGFloat size,
int fallbackToDefault)
{
NSFontManager *fm = [NSFontManager sharedFontManager];
NSFont *nsFont, *dflt = nil;
#define defaultFont (dflt ? dflt : (dflt = [NSFont systemFontOfSize:0]))
NSString *family;
if (familyName) {
family = [[[NSString alloc] initWithUTF8String:familyName] autorelease];
} else {
family = [defaultFont familyName];
}
if (size == 0.0) {
size = [defaultFont pointSize];
}
nsFont = [fm fontWithFamily:family traits:traits weight:weight size:size];
if (!nsFont) {
NSArray *availableFamilies = [fm availableFontFamilies];
NSString *caseFamily = nil;
for (NSString *f in availableFamilies) {
if ([family caseInsensitiveCompare:f] == NSOrderedSame) {
caseFamily = f;
break;
}
}
if (caseFamily) {
nsFont = [fm fontWithFamily:caseFamily traits:traits weight:weight
size:size];
}
}
if (!nsFont) {
nsFont = [NSFont fontWithName:family size:size];
}
if (!nsFont && fallbackToDefault) {
nsFont = [fm convertFont:defaultFont toFamily:family];
nsFont = [fm convertFont:nsFont toSize:size];
nsFont = [fm convertFont:nsFont toHaveTrait:traits];
}
[nsFont retain];
#undef defaultFont
return nsFont;
}
/*
*---------------------------------------------------------------------------
*
* InitFont --
*
* Helper for TkpGetNativeFont() and TkpGetFontFromAttributes().
*
* Results:
* Fills the MacFont structure.
*
* Side effects:
* Memory allocated.
*
*---------------------------------------------------------------------------
*/
static void
InitFont(
NSFont *nsFont,
const TkFontAttributes *reqFaPtr, /* Can be NULL */
MacFont *fontPtr)
{
TkFontAttributes *faPtr;
TkFontMetrics *fmPtr;
NSDictionary *nsAttributes;
NSRect bounds;
CGFloat kern = 0.0;
NSFontRenderingMode renderingMode = NSFontDefaultRenderingMode;
int ascent, descent/*, dontAA*/;
static const UniChar ch[] = {'.', 'W', ' ', 0xc4, 0xc1, 0xc2, 0xc3, 0xc7};
/* ., W, Space, Auml, Aacute, Acirc, Atilde, Ccedilla */
#define nCh (sizeof(ch) / sizeof(UniChar))
CGGlyph glyphs[nCh];
CGRect boundingRects[nCh];
fontPtr->font.fid = (Font) fontPtr;
faPtr = &fontPtr->font.fa;
if (reqFaPtr) {
*faPtr = *reqFaPtr;
} else {
TkInitFontAttributes(faPtr);
}
fontPtr->nsFont = nsFont;
// some don't like antialiasing on fixed-width even if bigger than limit
// dontAA = [nsFont isFixedPitch] && fontPtr->font.fa.size <= 10;
if (antialiasedTextEnabled >= 0/* || dontAA*/) {
renderingMode = (antialiasedTextEnabled == 0/* || dontAA*/) ?
NSFontIntegerAdvancementsRenderingMode :
NSFontAntialiasedRenderingMode;
}
nsFont = [nsFont screenFontWithRenderingMode:renderingMode];
GetTkFontAttributesForNSFont(nsFont, faPtr);
fmPtr = &fontPtr->font.fm;
fmPtr->ascent = floor([nsFont ascender] + [nsFont leading] + 0.5);
fmPtr->descent = floor(-[nsFont descender] + 0.5);
fmPtr->maxWidth = [nsFont maximumAdvancement].width;
fmPtr->fixed = [nsFont isFixedPitch]; /* Does not work for all fonts */
/*
* The ascent, descent and fixed fields are not correct for all fonts, as
* a workaround deduce that info from the metrics of some typical glyphs,
* along with screenfont kerning (space advance difference to printer font)
*/
bounds = [nsFont boundingRectForFont];
if (CTFontGetGlyphsForCharacters((CTFontRef) nsFont, ch, glyphs, nCh)) {
fmPtr->fixed = [nsFont advancementForGlyph:glyphs[0]].width ==
[nsFont advancementForGlyph:glyphs[1]].width;
bounds = NSRectFromCGRect(CTFontGetBoundingRectsForGlyphs((CTFontRef)
nsFont, defaultOrientation, ch, boundingRects, nCh));
kern = [nsFont advancementForGlyph:glyphs[2]].width -
[fontPtr->nsFont advancementForGlyph:glyphs[2]].width;
}
descent = floor(-bounds.origin.y + 0.5);
ascent = floor(bounds.size.height + bounds.origin.y + 0.5);
if (ascent > fmPtr->ascent) {
fmPtr->ascent = ascent;
}
if (descent > fmPtr->descent) {
fmPtr->descent = descent;
}
nsAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
nsFont, NSFontAttributeName,
[NSNumber numberWithInt:faPtr->underline ?
NSUnderlineStyleSingle|NSUnderlinePatternSolid :
NSUnderlineStyleNone], NSUnderlineStyleAttributeName,
[NSNumber numberWithInt:faPtr->overstrike ?
NSUnderlineStyleSingle|NSUnderlinePatternSolid :
NSUnderlineStyleNone], NSStrikethroughStyleAttributeName,
[NSNumber numberWithInt:fmPtr->fixed ? 0 : 1],
NSLigatureAttributeName,
[NSNumber numberWithDouble:kern], NSKernAttributeName, nil];
fontPtr->nsAttributes = [nsAttributes retain];
#undef nCh
}
/*
*-------------------------------------------------------------------------
*
* CreateNamedSystemFont --
*
* Register a system font with the Tk named font mechanism.
*
* Results:
*
* Result from TkCreateNamedFont().
*
* Side effects:
*
* A new named font is added to the Tk font registry.
*
*-------------------------------------------------------------------------
*/
static int
CreateNamedSystemFont(
Tcl_Interp *interp,
Tk_Window tkwin,
const char* name,
TkFontAttributes *faPtr)
{
TkDeleteNamedFont(NULL, tkwin, name);
return TkCreateNamedFont(interp, tkwin, name, faPtr);
}
#pragma mark -
#pragma mark Font handling:
/*
*-------------------------------------------------------------------------
*
* TkpFontPkgInit --
*
* This procedure is called when an application is created. It
* initializes all the structures that are used by the
* platform-dependant code on a per application basis.
* Note that this is called before TkpInit() !
*
* Results:
* None.
*
* Side effects:
* Initialize named system fonts.
*
*-------------------------------------------------------------------------
*/
void
TkpFontPkgInit(
TkMainInfo *mainPtr) /* The application being created. */
{
Tcl_Interp *interp = mainPtr->interp;
Tk_Window tkwin = (Tk_Window) mainPtr->winPtr;
const struct SystemFontMapEntry *systemFont = systemFontMap;
NSFont *nsFont;
TkFontAttributes fa;
NSMutableCharacterSet *cs;
/* Since we called before TkpInit, we need our own autorelease pool. */
NSAutoreleasePool *pool = [NSAutoreleasePool new];
/* force this for now */
if (!mainPtr->winPtr->mainPtr) {
mainPtr->winPtr->mainPtr = mainPtr;
}
while (systemFont->systemName) {
nsFont = (NSFont*) CTFontCreateUIFontForLanguage(
HIThemeGetUIFontType(systemFont->id), 0, NULL);
if (nsFont) {
TkInitFontAttributes(&fa);
GetTkFontAttributesForNSFont(nsFont, &fa);
CreateNamedSystemFont(interp, tkwin, systemFont->systemName, &fa);
if (systemFont->tkName) {
CreateNamedSystemFont(interp, tkwin, systemFont->tkName, &fa);
}
if (systemFont->tkName1) {
CreateNamedSystemFont(interp, tkwin, systemFont->tkName1, &fa);
}
CFRelease(nsFont);
}
systemFont++;
}
TkInitFontAttributes(&fa);
nsFont = (NSFont*) CTFontCreateUIFontForLanguage(fixedPitch, 11, NULL);
if (nsFont) {
GetTkFontAttributesForNSFont(nsFont, &fa);
CFRelease(nsFont);
} else {
fa.family = Tk_GetUid("Monaco");
fa.size = 11;
fa.weight = TK_FW_NORMAL;
fa.slant = TK_FS_ROMAN;
}
CreateNamedSystemFont(interp, tkwin, "TkFixedFont", &fa);
if (!whitespaceCharacterSet) {
whitespaceCharacterSet = [[NSCharacterSet
whitespaceAndNewlineCharacterSet] retain];
cs = [whitespaceCharacterSet mutableCopy];
[cs removeCharactersInString:@" "];
lineendingCharacterSet = [cs copy];
[cs release];
}
[pool drain];
}
/*
*---------------------------------------------------------------------------
*
* TkpGetNativeFont --
*
* Map a platform-specific native font name to a TkFont.
*
* Results:
* The return value is a pointer to a TkFont that represents the
* native font. If a native font by the given name could not be
* found, the return value is NULL.
*
* Every call to this procedure returns a new TkFont structure, even
* if the name has already been seen before. The caller should call
* TkpDeleteFont() when the font is no longer needed.
*
* The caller is responsible for initializing the memory associated
* with the generic TkFont when this function returns and releasing
* the contents of the generics TkFont before calling TkpDeleteFont().
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
TkFont *
TkpGetNativeFont(
Tk_Window tkwin, /* For display where font will be used. */
const char *name) /* Platform-specific font name. */
{
MacFont *fontPtr = NULL;
ThemeFontID themeFontId;
CTFontRef ctFont;
if (strcmp(name, SYSTEMFONT_NAME) == 0) {
themeFontId = kThemeSystemFont;
} else if (strcmp(name, APPLFONT_NAME) == 0) {
themeFontId = kThemeApplicationFont;
} else if (strcmp(name, MENUITEMFONT_NAME) == 0) {
themeFontId = kThemeMenuItemFont;
} else {
return NULL;
}
ctFont = CTFontCreateUIFontForLanguage(HIThemeGetUIFontType(
themeFontId), 0, NULL);
if (ctFont) {
fontPtr = ckalloc(sizeof(MacFont));
InitFont((NSFont*) ctFont, NULL, fontPtr);
}
return (TkFont *) fontPtr;
}
/*
*---------------------------------------------------------------------------
*
* TkpGetFontFromAttributes --
*
* Given a desired set of attributes for a font, find a font with the
* closest matching attributes.
*
* Results:
* The return value is a pointer to a TkFont that represents the font
* with the desired attributes. If a font with the desired attributes
* could not be constructed, some other font will be substituted
* automatically.
*
* Every call to this procedure returns a new TkFont structure, even
* if the specified attributes have already been seen before. The
* caller should call TkpDeleteFont() to free the platform- specific
* data when the font is no longer needed.
*
* The caller is responsible for initializing the memory associated
* with the generic TkFont when this function returns and releasing
* the contents of the generic TkFont before calling TkpDeleteFont().
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
TkFont *
TkpGetFontFromAttributes(
TkFont *tkFontPtr, /* If non-NULL, store the information in this
* existing TkFont structure, rather than
* allocating a new structure to hold the
* font; the existing contents of the font
* will be released. If NULL, a new TkFont
* structure is allocated. */
Tk_Window tkwin, /* For display where font will be used. */
const TkFontAttributes *faPtr)
/* Set of attributes to match. */
{
MacFont *fontPtr;
int points = (int)(TkFontGetPoints(tkwin, faPtr->size) + 0.5);
NSFontTraitMask traits = GetNSFontTraitsFromTkFontAttributes(faPtr);
NSInteger weight = (faPtr->weight == TK_FW_BOLD ? 9 : 5);
NSFont *nsFont;
nsFont = FindNSFont(faPtr->family, traits, weight, points, 0);
if (!nsFont) {
const char *const *aliases = TkFontGetAliasList(faPtr->family);
while (aliases && !nsFont) {
nsFont = FindNSFont(*aliases++, traits, weight, points, 0);
}
}
if (!nsFont) {
nsFont = FindNSFont(faPtr->family, traits, weight, points, 1);
}
if (!nsFont) {
Tcl_Panic("Could not determine NSFont from TkFontAttributes");
}
if (tkFontPtr == NULL) {
fontPtr = ckalloc(sizeof(MacFont));
} else {
fontPtr = (MacFont *) tkFontPtr;
TkpDeleteFont(tkFontPtr);
}
CFRetain(nsFont); /* Always needed to allow unconditional CFRelease below */
InitFont(nsFont, faPtr, fontPtr);
return (TkFont *) fontPtr;
}
/*
*---------------------------------------------------------------------------
*
* TkpDeleteFont --
*
* Called to release a font allocated by TkpGetNativeFont() or
* TkpGetFontFromAttributes(). The caller should have already
* released the fields of the TkFont that are used exclusively by the
* generic TkFont code.
*
* Results:
* TkFont is deallocated.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
void
TkpDeleteFont(
TkFont *tkFontPtr) /* Token of font to be deleted. */
{
MacFont *fontPtr = (MacFont *) tkFontPtr;
[fontPtr->nsAttributes release];
fontPtr->nsAttributes = NULL;
CFRelease(fontPtr->nsFont); /* Either a CTFontRef or a CFRetained NSFont */
}
/*
*---------------------------------------------------------------------------
*
* TkpGetFontFamilies --
*
* Return information about the font families that are available on
* the display of the given window.
*
* Results:
* Modifies interp's result object to hold a list of all the available
* font families.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
void
TkpGetFontFamilies(
Tcl_Interp *interp, /* Interp to hold result. */
Tk_Window tkwin) /* For display to query. */
{
Tcl_Obj *resultPtr = Tcl_NewListObj(0, NULL);
NSArray *list = [[NSFontManager sharedFontManager] availableFontFamilies];
for (NSString *family in list) {
Tcl_ListObjAppendElement(NULL, resultPtr,
Tcl_NewStringObj([family UTF8String], -1));
}
Tcl_SetObjResult(interp, resultPtr);
}
/*
*-------------------------------------------------------------------------
*
* TkpGetSubFonts --
*
* A function used by the testing package for querying the actual
* screen fonts that make up a font object.
*
* Results:
* Modifies interp's result object to hold a list containing the names
* of the screen fonts that make up the given font object.
*
* Side effects:
* None.
*
*-------------------------------------------------------------------------
*/
void
TkpGetSubFonts(
Tcl_Interp *interp, /* Interp to hold result. */
Tk_Font tkfont) /* Font object to query. */
{
MacFont *fontPtr = (MacFont *) tkfont;
Tcl_Obj *resultPtr = Tcl_NewListObj(0, NULL);
if (fontPtr->nsFont) {
NSArray *list = [[fontPtr->nsFont fontDescriptor]
objectForKey:NSFontCascadeListAttribute];
for (NSFontDescriptor *subFontDesc in list) {
NSString *family = [subFontDesc objectForKey:NSFontFamilyAttribute];
if (family) {
Tcl_ListObjAppendElement(NULL, resultPtr,
Tcl_NewStringObj([family UTF8String], -1));
}
}
}
Tcl_SetObjResult(interp, resultPtr);
}
/*
*----------------------------------------------------------------------
*
* TkpGetFontAttrsForChar --
*
* Retrieve the font attributes of the actual font used to render a
* given character.
*
* Results:
* None.
*
* Side effects:
* The font attributes are stored in *faPtr.
*
*----------------------------------------------------------------------
*/
void
TkpGetFontAttrsForChar(
Tk_Window tkwin, /* Window on the font's display */
Tk_Font tkfont, /* Font to query */
int c, /* Character of interest */
TkFontAttributes* faPtr) /* Output: Font attributes */
{
MacFont *fontPtr = (MacFont *) tkfont;
NSFont *nsFont = fontPtr->nsFont;
*faPtr = fontPtr->font.fa;
if (nsFont && ![[nsFont coveredCharacterSet] characterIsMember:c]) {
UTF16Char ch = (UTF16Char) c;
nsFont = [nsFont bestMatchingFontForCharacters:&ch
length:1 attributes:nil actualCoveredLength:NULL];
if (nsFont) {
GetTkFontAttributesForNSFont(nsFont, faPtr);
}
}
}
#pragma mark -
#pragma mark Measuring and drawing:
/*
*---------------------------------------------------------------------------
*
* Tk_MeasureChars --
*
* Determine the number of characters from the string that will fit in
* the given horizontal span. The measurement is done under the
* assumption that Tk_DrawChars() will be used to actually display the
* characters.
*
* With ATSUI we need the line context to do this right, so we have the
* actual implementation in TkpMeasureCharsInContext().
*
* Results:
* The return value is the number of bytes from source that fit into the
* span that extends from 0 to maxLength. *lengthPtr is filled with the
* x-coordinate of the right edge of the last character that did fit.
*
* Side effects:
* None.
*
* Todo:
* Effects of the "flags" parameter are untested.
*
*---------------------------------------------------------------------------
*/
int
Tk_MeasureChars(
Tk_Font tkfont, /* Font in which characters will be drawn. */
const char *source, /* UTF-8 string to be displayed. Need not be
* '\0' terminated. */
int numBytes, /* Maximum number of bytes to consider from
* source string. */
int maxLength, /* If >= 0, maxLength specifies the longest
* permissible line length; don't consider any
* character that would cross this x-position.
* If < 0, then line length is unbounded and
* the flags argument is ignored. */
int flags, /* Various flag bits OR-ed together:
* TK_PARTIAL_OK means include the last char
* which only partially fit on this line.
* TK_WHOLE_WORDS means stop on a word
* boundary, if possible. TK_AT_LEAST_ONE
* means return at least one character even if
* no characters fit. */
int *lengthPtr) /* Filled with x-location just after the
* terminating character. */
{
return TkpMeasureCharsInContext(tkfont, source, numBytes, 0, numBytes,
maxLength, flags, lengthPtr);
}
/*
*---------------------------------------------------------------------------
*
* TkpMeasureCharsInContext --
*
* Determine the number of bytes from the string that will fit in the
* given horizontal span. The measurement is done under the assumption
* that TkpDrawCharsInContext() will be used to actually display the
* characters.
*
* This one is almost the same as Tk_MeasureChars(), but with access to
* all the characters on the line for context.
*
* Results:
* The return value is the number of bytes from source that
* fit into the span that extends from 0 to maxLength. *lengthPtr is
* filled with the x-coordinate of the right edge of the last
* character that did fit.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
int
TkpMeasureCharsInContext(
Tk_Font tkfont, /* Font in which characters will be drawn. */
const char * source, /* UTF-8 string to be displayed. Need not be
* '\0' terminated. */
int numBytes, /* Maximum number of bytes to consider from
* source string in all. */
int rangeStart, /* Index of first byte to measure. */
int rangeLength, /* Length of range to measure in bytes. */
int maxLength, /* If >= 0, maxLength specifies the longest
* permissible line length; don't consider any
* character that would cross this x-position.
* If < 0, then line length is unbounded and
* the flags argument is ignored. */
int flags, /* Various flag bits OR-ed together:
* TK_PARTIAL_OK means include the last char
* which only partially fits on this line.
* TK_WHOLE_WORDS means stop on a word
* boundary, if possible. TK_AT_LEAST_ONE
* means return at least one character even
* if no characters fit. If TK_WHOLE_WORDS
* and TK_AT_LEAST_ONE are set and the first
* word doesn't fit, we return at least one
* character or whatever characters fit into
* maxLength. TK_ISOLATE_END means that the
* last character should not be considered in
* context with the rest of the string (used
* for breaking lines). */
int *lengthPtr) /* Filled with x-location just after the
* terminating character. */
{
const MacFont *fontPtr = (const MacFont *) tkfont;
NSString *string;
NSAttributedString *attributedString;
CTTypesetterRef typesetter;
CFIndex start, len;
CFRange range = {0, 0};
CTLineRef line;
CGFloat offset = 0;
CFIndex index;
double width;
int length, fit;
if (rangeStart < 0 || rangeLength <= 0 ||
rangeStart + rangeLength > numBytes ||
(maxLength == 0 && !(flags & TK_AT_LEAST_ONE))) {
*lengthPtr = 0;
return 0;
}
#if 0
/* Back-compatibility with ATSUI renderer, appears not to be needed */
if (rangeStart == 0 && maxLength == 1 && (flags & TK_ISOLATE_END) &&
!(flags & TK_AT_LEAST_ONE)) {
length = 0;
fit = 0;
goto done;
}
#endif
if (maxLength > 32767) {
maxLength = 32767;
}
string = [[NSString alloc] initWithBytesNoCopy:(void*)source
length:numBytes encoding:NSUTF8StringEncoding freeWhenDone:NO];
if (!string) {
length = 0;
fit = rangeLength;
goto done;
}
attributedString = [[NSAttributedString alloc] initWithString:string
attributes:fontPtr->nsAttributes];
typesetter = CTTypesetterCreateWithAttributedString(
(CFAttributedStringRef)attributedString);
start = Tcl_NumUtfChars(source, rangeStart);
len = Tcl_NumUtfChars(source + rangeStart, rangeLength);
if (start > 0) {
range.length = start;
line = CTTypesetterCreateLine(typesetter, range);
offset = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
CFRelease(line);
}
if (maxLength < 0) {
index = len;
range.length = len;
line = CTTypesetterCreateLine(typesetter, range);
width = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
CFRelease(line);
} else {
double maxWidth = maxLength + offset;
NSCharacterSet *cs;
index = start;
if (flags & TK_WHOLE_WORDS) {
index = CTTypesetterSuggestLineBreak(typesetter, start, maxWidth);
if (index <= start && (flags & TK_AT_LEAST_ONE)) {
flags &= ~TK_WHOLE_WORDS;
}
}
if (index <= start && !(flags & TK_WHOLE_WORDS)) {
index = CTTypesetterSuggestClusterBreak(typesetter, start, maxWidth);
}
cs = (index <= len && (flags & TK_WHOLE_WORDS)) ?
whitespaceCharacterSet : lineendingCharacterSet;
while (index > start &&
[cs characterIsMember:[string characterAtIndex:(index - 1)]]) {
index--;
}
if (index <= start && (flags & TK_AT_LEAST_ONE)) {
index = start + 1;
}
if (index > 0) {
range.length = index;
line = CTTypesetterCreateLine(typesetter, range);
width = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
CFRelease(line);
} else {
width = 0;
}
if (width < maxWidth && (flags & TK_PARTIAL_OK) && index < len) {
range.length = ++index;
line = CTTypesetterCreateLine(typesetter, range);
width = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
CFRelease(line);
}
/* The call to CTTypesetterSuggestClusterBreak above will always
return at least one character regardless of whether it exceeded
it or not. Clean that up now. */
while (width > maxWidth && !(flags & TK_PARTIAL_OK)
&& index > start+(flags & TK_AT_LEAST_ONE)) {
range.length = --index;
line = CTTypesetterCreateLine(typesetter, range);
width = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
CFRelease(line);
}
}
CFRelease(typesetter);
[attributedString release];
[string release];
length = ceil(width - offset);
fit = (Tcl_UtfAtIndex(source, index) - source) - rangeStart;
done:
#ifdef TK_MAC_DEBUG_FONTS
TkMacOSXDbgMsg("measure: source=\"%s\" range=\"%.*s\" maxLength=%d "
"flags='%s%s%s%s' -> width=%d bytesFit=%d\n", source, rangeLength,
source+rangeStart, maxLength,
flags & TK_PARTIAL_OK ? "partialOk " : "",
flags & TK_WHOLE_WORDS ? "wholeWords " : "",
flags & TK_AT_LEAST_ONE ? "atLeastOne " : "",
flags & TK_ISOLATE_END ? "isolateEnd " : "",
length, fit);
//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);
#endif
*lengthPtr = length;
return fit;
}
/*
*---------------------------------------------------------------------------
*
* Tk_DrawChars --
*
* Draw a string of characters on the screen.
*
* With ATSUI we need the line context to do this right, so we have the
* actual implementation in TkpDrawCharsInContext().
*
* Results:
* None.
*
* Side effects:
* Information gets drawn on the screen.
*
*---------------------------------------------------------------------------
*/
void
Tk_DrawChars(
Display *display, /* Display on which to draw. */
Drawable drawable, /* Window or pixmap in which to draw. */
GC gc, /* Graphics context for drawing characters. */
Tk_Font tkfont, /* Font in which characters will be drawn; must
* be the same as font used in GC. */
const char *source, /* UTF-8 string to be displayed. Need not be
* '\0' terminated. All Tk meta-characters
* (tabs, control characters, and newlines)
* should be stripped out of the string that
* is passed to this function. If they are not
* stripped out, they will be displayed as
* regular printing characters. */
int numBytes, /* Number of bytes in string. */
int x, int y) /* Coordinates at which to place origin of the
* string when drawing. */
{
DrawCharsInContext(display, drawable, gc, tkfont, source, numBytes,
0, numBytes, x, y, 0.0);
}
void
TkDrawAngledChars(
Display *display, /* Display on which to draw. */
Drawable drawable, /* Window or pixmap in which to draw. */
GC gc, /* Graphics context for drawing characters. */
Tk_Font tkfont, /* Font in which characters will be drawn;
* must be the same as font used in GC. */
const char *source, /* UTF-8 string to be displayed. Need not be
* '\0' terminated. All Tk meta-characters
* (tabs, control characters, and newlines)
* should be stripped out of the string that
* is passed to this function. If they are not
* stripped out, they will be displayed as
* regular printing characters. */
int numBytes, /* Number of bytes in string. */
double x, double y, /* Coordinates at which to place origin of
* string when drawing. */
double angle) /* What angle to put text at, in degrees. */
{
DrawCharsInContext(display, drawable, gc, tkfont, source, numBytes,
0, numBytes, x, y, angle);
}
/*
*---------------------------------------------------------------------------
*
* TkpDrawCharsInContext --
*
* Draw a string of characters on the screen like Tk_DrawChars(), with
* access to all the characters on the line for context.
*
* Results:
* None.
*
* Side effects:
* Information gets drawn on the screen.
*
* Todo:
* Stippled text drawing.
*
*---------------------------------------------------------------------------
*/
void
TkpDrawCharsInContext(
Display *display, /* Display on which to draw. */
Drawable drawable, /* Window or pixmap in which to draw. */
GC gc, /* Graphics context for drawing characters. */
Tk_Font tkfont, /* Font in which characters will be drawn; must
* be the same as font used in GC. */
const char * source, /* UTF-8 string to be displayed. Need not be
* '\0' terminated. All Tk meta-characters
* (tabs, control characters, and newlines)
* should be stripped out of the string that
* is passed to this function. If they are not
* stripped out, they will be displayed as
* regular printing characters. */
int numBytes, /* Number of bytes in string. */
int rangeStart, /* Index of first byte to draw. */
int rangeLength, /* Length of range to draw in bytes. */
int x, int y) /* Coordinates at which to place origin of the
* whole (not just the range) string when
* drawing. */
{
DrawCharsInContext(display, drawable, gc, tkfont, source, numBytes,
rangeStart, rangeLength, x, y, 0.0);
}
static void
DrawCharsInContext(
Display *display, /* Display on which to draw. */
Drawable drawable, /* Window or pixmap in which to draw. */
GC gc, /* Graphics context for drawing characters. */
Tk_Font tkfont, /* Font in which characters will be drawn; must
* be the same as font used in GC. */
const char * source, /* UTF-8 string to be displayed. Need not be
* '\0' terminated. All Tk meta-characters
* (tabs, control characters, and newlines)
* should be stripped out of the string that
* is passed to this function. If they are not
* stripped out, they will be displayed as
* regular printing characters. */
int numBytes, /* Number of bytes in string. */
int rangeStart, /* Index of first byte to draw. */
int rangeLength, /* Length of range to draw in bytes. */
int x, int y, /* Coordinates at which to place origin of the
* whole (not just the range) string when
* drawing. */
double angle)
{
const MacFont *fontPtr = (const MacFont *) tkfont;
NSString *string;
NSMutableDictionary *attributes;
NSAttributedString *attributedString;
CTTypesetterRef typesetter;
CFIndex start, len;
CTLineRef line;
MacDrawable *macWin = (MacDrawable *) drawable;
TkMacOSXDrawingContext drawingContext;
CGContextRef context;
CGColorRef fg;
NSFont *nsFont;
CGAffineTransform t;
int h;
if (rangeStart < 0 || rangeLength <= 0 ||
rangeStart + rangeLength > numBytes ||
!TkMacOSXSetupDrawingContext(drawable, gc, 1, &drawingContext)) {
return;
}
string = [[NSString alloc] initWithBytesNoCopy:(void*)source
length:numBytes encoding:NSUTF8StringEncoding freeWhenDone:NO];
if (!string) {
return;
}
context = drawingContext.context;
fg = TkMacOSXCreateCGColor(gc, gc->foreground);
attributes = [fontPtr->nsAttributes mutableCopy];
[attributes setObject:(id)fg forKey:(id)kCTForegroundColorAttributeName];
CFRelease(fg);
nsFont = [attributes objectForKey:NSFontAttributeName];
[nsFont setInContext:[NSGraphicsContext graphicsContextWithGraphicsPort:
context flipped:NO]];
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
attributedString = [[NSAttributedString alloc] initWithString:string
attributes:attributes];
typesetter = CTTypesetterCreateWithAttributedString(
(CFAttributedStringRef)attributedString);
x += macWin->xOff;
y += macWin->yOff;
h = drawingContext.portBounds.size.height;
y = h - y;
t = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, h);
if (angle != 0.0) {
t = CGAffineTransformTranslate(CGAffineTransformRotate(
CGAffineTransformTranslate(t, x, y), angle*PI/180.0), -x, -y);
}
CGContextConcatCTM(context, t);
CGContextSetTextPosition(context, x, y);
start = Tcl_NumUtfChars(source, rangeStart);
len = Tcl_NumUtfChars(source, rangeStart + rangeLength);
if (start > 0) {
CGRect clipRect = CGRectInfinite, startBounds;
line = CTTypesetterCreateLine(typesetter, CFRangeMake(0, start));
startBounds = CTLineGetImageBounds(line, context);
CFRelease(line);
clipRect.origin.x = startBounds.origin.x + startBounds.size.width;
CGContextClipToRect(context, clipRect);
}
line = CTTypesetterCreateLine(typesetter, CFRangeMake(0, len));
CTLineDraw(line, context);
CFRelease(line);
CFRelease(typesetter);
[attributedString release];
[string release];
[attributes release];
TkMacOSXRestoreDrawingContext(&drawingContext);
}
#pragma mark -
#pragma mark Accessors:
/*
*---------------------------------------------------------------------------
*
* TkMacOSXNSFontForFont --
*
* Return an NSFont for the given Tk_Font.
*
* Results:
* NSFont*.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
MODULE_SCOPE NSFont*
TkMacOSXNSFontForFont(
Tk_Font tkfont)
{
return tkfont ? ((MacFont *)tkfont)->nsFont : nil;
}
/*
*---------------------------------------------------------------------------
*
* TkMacOSXNSFontAttributesForFont --
*
* Return an NSDictionary of font attributes for the given Tk_Font.
*
* Results:
* NSFont*.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
MODULE_SCOPE NSDictionary*
TkMacOSXNSFontAttributesForFont(
Tk_Font tkfont)
{
return tkfont ? ((MacFont *)tkfont)->nsAttributes : nil;
}
/*
*---------------------------------------------------------------------------
*
* TkMacOSXIsCharacterMissing --
*
* Given a tkFont and a character determine whether the character has
* a glyph defined in the font or not.
*
* Results:
* Returns a 1 if the character is missing, a 0 if it is not.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
int
TkMacOSXIsCharacterMissing(
Tk_Font tkfont, /* The font we are looking in. */
unsigned int searchChar) /* The character we are looking for. */
{
return 0;
}
/*
*----------------------------------------------------------------------
*
* TkMacOSXFontDescriptionForNSFontAndNSFontAttributes --
*
* Get text description of a font specified by NSFont and attributes.
*
* Results:
* List object or NULL.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
MODULE_SCOPE Tcl_Obj *
TkMacOSXFontDescriptionForNSFontAndNSFontAttributes(
NSFont *nsFont,
NSDictionary *nsAttributes)
{
Tcl_Obj *objv[6];
int i = 0;
const char *familyName = [[nsFont familyName] UTF8String];
if (nsFont && familyName) {
NSFontTraitMask traits = [[NSFontManager sharedFontManager]
traitsOfFont:nsFont];
id underline = [nsAttributes objectForKey:
NSUnderlineStyleAttributeName];
id strikethrough = [nsAttributes objectForKey:
NSStrikethroughStyleAttributeName];
objv[i++] = Tcl_NewStringObj(familyName, -1);
objv[i++] = Tcl_NewIntObj([nsFont pointSize]);
#define S(s) Tcl_NewStringObj(STRINGIFY(s),(int)(sizeof(STRINGIFY(s))-1))
objv[i++] = (traits & NSBoldFontMask) ? S(bold) : S(normal);
objv[i++] = (traits & NSItalicFontMask) ? S(italic) : S(roman);
if ([underline respondsToSelector:@selector(intValue)] &&
([underline intValue] & (NSUnderlineStyleSingle |
NSUnderlineStyleThick | NSUnderlineStyleDouble))) {
objv[i++] = S(underline);
}
if ([strikethrough respondsToSelector:@selector(intValue)] &&
([strikethrough intValue] & (NSUnderlineStyleSingle |
NSUnderlineStyleThick | NSUnderlineStyleDouble))) {
objv[i++] = S(overstrike);
}
#undef S
}
return i ? Tcl_NewListObj(i, objv) : NULL;
}
/*
*----------------------------------------------------------------------
*
* TkMacOSXUseAntialiasedText --
*
* Enables or disables application-wide use of antialiased text (where
* available). Sets up a linked Tcl global variable to allow
* disabling of antialiased text from tcl.
* The possible values for this variable are:
*
* -1 - Use system default as configurable in "System Prefs" -> "General".
* 0 - Unconditionally disable antialiasing.
* 1 - Unconditionally enable antialiasing.
*
* Results:
*
* TCL_OK.
*
* Side effects:
*
* None.
*
*----------------------------------------------------------------------
*/
MODULE_SCOPE int
TkMacOSXUseAntialiasedText(
Tcl_Interp * interp, /* The Tcl interpreter to receive the
* variable.*/
int enable) /* Initial value. */
{
static Boolean initialized = FALSE;
if (!initialized) {
initialized = TRUE;
if (Tcl_CreateNamespace(interp, "::tk::mac", NULL, NULL) == NULL) {
Tcl_ResetResult(interp);
}
if (Tcl_LinkVar(interp, "::tk::mac::antialiasedtext",
(char *) &antialiasedTextEnabled,
TCL_LINK_INT) != TCL_OK) {
Tcl_ResetResult(interp);
}
}
antialiasedTextEnabled = enable;
return TCL_OK;
}
/*
* Local Variables:
* mode: objc
* c-basic-offset: 4
* fill-column: 79
* coding: utf-8
* End:
*/