summaryrefslogtreecommitdiffstats
path: root/carbon/tkMacOSXFont.c
diff options
context:
space:
mode:
Diffstat (limited to 'carbon/tkMacOSXFont.c')
-rw-r--r--carbon/tkMacOSXFont.c2678
1 files changed, 2678 insertions, 0 deletions
diff --git a/carbon/tkMacOSXFont.c b/carbon/tkMacOSXFont.c
new file mode 100644
index 0000000..7933fe3
--- /dev/null
+++ b/carbon/tkMacOSXFont.c
@@ -0,0 +1,2678 @@
+/*
+ * tkMacOSXFont.c --
+ *
+ * Contains the Macintosh implementation of the platform-independant
+ * font package interface. This version uses ATSU instead of Quickdraw.
+ *
+ * Copyright 2002-2004 Benjamin Riefenstahl, Benjamin.Riefenstahl@epost.de
+ * Copyright (c) 2006-2008 Daniel A. Steffen <das@users.sourceforge.net>
+ *
+ * Some functions were originally copied verbatim from the QuickDraw version
+ * of tkMacOSXFont.c, which had these copyright notices:
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * Copyright 2001, Apple Computer, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * Todos:
+ *
+ * - Get away from Font Manager and Quickdraw functions as much as possible,
+ * replace with ATS functions instead.
+ *
+ * - Use Font Manager functions to translate ids from ATS to Font Manager
+ * instead of just assuming that they are the same.
+ *
+ * - Get a second font register going for fonts that are not assigned to a
+ * font family by the OS. On my system I have 27 fonts of that type,
+ * Hebrew, Arabic and Hindi fonts that actually come with the system.
+ * FMGetFontFamilyInstanceFromFont() returns -981 (kFMInvalidFontFamilyErr)
+ * for these and they are not listed when enumerating families, but they
+ * are when enumerating fonts directly. The problem that the OS sees may
+ * be that at least some of them do not contain any Latin characters. Note
+ * that such fonts can not be used for controls, because controls
+ * definitely require a family id (this assertion needs testing).
+ */
+
+#include "tkMacOSXPrivate.h"
+#include "tkMacOSXFont.h"
+
+/*
+#ifdef TK_MAC_DEBUG
+#define TK_MAC_DEBUG_FONTS
+#endif
+*/
+
+/*
+ * Problem: The sum of two parts is not the same as the whole. In particular
+ * the width of two separately measured strings will usually be larger than
+ * the width of them pasted together. Tk has a design bug here, because it
+ * generally assumes that this kind of arithmetic works.
+ * To workaround this, #define TK_MAC_COALESCE_LINE to 1 below, we then avoid
+ * lines that tremble and shiver while the cursor passes through them by
+ * undercutting the system and behind the scenes pasting strings together that
+ * look like they are on the same line and adjacent and that are drawn with
+ * the same font. To do this we need some global data.
+ */
+#define TK_MAC_COALESCE_LINE 0
+
+/*
+ * 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. */
+
+ /*
+ * The ATSU view of the font and other text properties. Used for drawing
+ * and measuring.
+ */
+
+ ATSUFontID atsuFontId; /* == FMFont. */
+ ATSUTextLayout atsuLayout; /* ATSU layout object, representing the whole
+ * text that ATSU sees with some option
+ * bits. */
+ ATSUStyle atsuStyle; /* ATSU style object, representing a run of
+ * text with the same properties. */
+
+ /*
+ * The QuickDraw view of the font. Used to configure controls.
+ */
+
+ FMFontFamily qdFont; /* == FMFontFamilyId, Carbon replacement for
+ * QD face numbers. */
+ short qdSize; /* Font size in points. */
+ short qdStyle; /* QuickDraw style bits. */
+} MacFont;
+
+/*
+ * Information about font families, initialized at startup time. Font
+ * families are described by a mapping from UTF-8 names to MacOS font family
+ * IDs. The whole list is kept as the sorted array "familyList", allocated
+ * with ckrealloc().
+ *
+ * Note: This would have been easier, if we could just have used Tcl hash
+ * arrays. Unfortunately there seems to be no pre-packaged
+ * non-case-sensitive version of that available.
+ */
+
+typedef struct {
+ const char * name;
+ FMFontFamily familyId;
+} MacFontFamily;
+
+static MacFontFamily * familyList = NULL;
+static int
+ familyListNextFree = 0, /* The next free slot in familyList. */
+ familyListMaxValid = 0, /* The top of the sorted area. */
+ familyListSize = 0; /* The size of the whole array. */
+
+/*
+ * A simple one-shot sub-allocator for fast and efficient allocation of
+ * strings. Used by the familyList array for the names. These strings are
+ * only allocated once at startup and never freed. If you ever need to
+ * re-initialize this, you can just ckfree() all the StringBlocks in the list
+ * and start over.
+ */
+
+#define STRING_BLOCK_MAX (1024-8) /* Make sizeof(StringBlock) ==
+ * 1024. */
+typedef struct StringBlock {
+ struct StringBlock *next; /* Starting from "stringMemory" these
+ * blocks form a linked list. */
+ int nextFree; /* Top of the used area in the
+ * "strings" member. */
+ char strings[STRING_BLOCK_MAX]; /* The actual memory managed here. */
+} StringBlock;
+
+static StringBlock *stringMemory = NULL;
+
+#if TK_MAC_COALESCE_LINE
+static Tcl_DString currentLine; /* The current line as seen so far. This
+ * contains a Tcl_UniChar DString. */
+static int
+ currentY = -1, /* The Y position (row in pixels) of the
+ * current line. */
+ currentLeft = -1, /* The left edge (pixels) of the current
+ * line. */
+ currentRight = -1; /* The right edge (pixels) of the current
+ * line. */
+static const MacFont *currentFontPtr = NULL;
+ /* The font of the current line. */
+#endif /* TK_MAC_COALESCE_LINE */
+
+static int antialiasedTextEnabled;
+
+/*
+ * 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
+
+/*
+ * Procedures used only in this file.
+ */
+
+static void LayoutSetString(const MacFont *fontPtr,
+ const TkMacOSXDrawingContext *drawingContextPtr,
+ const UniChar * uchars, int ulen);
+
+/*
+ * The actual workers.
+ */
+
+static int MeasureStringWidth(const MacFont *fontPtr, int start, int end);
+
+#if TK_MAC_COALESCE_LINE
+static const Tcl_UniChar *UpdateLineBuffer(const MacFont *fontPtr,
+ const TkMacOSXDrawingContext *drawingContextPtr, const char *source,
+ int numBytes, int x, int y, int * offset);
+#endif /* TK_MAC_COALESCE_LINE */
+
+/*
+ * Initialization and setup of a font data structure.
+ */
+
+static const char *FamilyNameForFamilyID(FMFontFamily familyId);
+static void InitFont(FMFontFamily familyId, const char *familyName,
+ int size, int qdStyle, MacFont *fontPtr);
+static void InitATSUObjects(FMFontFamily familyId, short qdsize, short qdStyle,
+ ATSUFontID *fontIdPtr, ATSUTextLayout *layoutPtr, ATSUStyle *stylePtr);
+static void InitATSUStyle(ATSUFontID fontId, short ptSize, short qdStyle,
+ ATSUStyle style);
+static void SetFontFeatures(ATSUFontID fontId, int fixed, short size,
+ ATSUStyle style);
+static void AdjustFontHeight(MacFont *fontPtr);
+static void InitATSULayout(const TkMacOSXDrawingContext *drawingContextPtr,
+ ATSUTextLayout layout, int fixed);
+static void ReleaseFont(MacFont *fontPtr);
+
+/*
+ * Finding fonts by name.
+ */
+
+static const MacFontFamily *FindFontFamilyOrAlias(const char *name);
+static const MacFontFamily *FindFontFamilyOrAliasOrFallback(const char *name);
+
+/*
+ * Doing interesting things with font families and fonts.
+ */
+
+static void InitFontFamilies(void);
+static OSStatus GetFontFamilyName(FMFontFamily fontFamily, char *name,
+ int numBytes);
+
+/*
+ * Accessor functions and internal utilities for the font family list.
+ */
+
+static const MacFontFamily *AddFontFamily(const char *name,
+ FMFontFamily familyId);
+static const MacFontFamily *FindFontFamily(const char *name);
+static Tcl_Obj *EnumFontFamilies(void);
+
+static OSStatus FontFamilyEnumCallback(ATSFontFamilyRef family, void *refCon);
+static void SortFontFamilies(void);
+static int CompareFontFamilies(const void *vp1, const void *vp2);
+static const char *AddString(const char *in);
+
+static OSStatus GetThemeFontAndFamily(const ThemeFontID themeFontId,
+ FMFontFamily *fontFamily, unsigned char *fontName, SInt16 *fontSize,
+ Style *fontStyle);
+static void InitSystemFonts(TkMainInfo *mainPtr);
+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);
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * 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.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Initialization of variables local to this file.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+void
+TkpFontPkgInit(
+ TkMainInfo *mainPtr) /* The application being created. */
+{
+ InitFontFamilies();
+ InitSystemFonts(mainPtr);
+
+#if TK_MAC_COALESCE_LINE
+ Tcl_DStringInit(&currentLine);
+#endif
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * InitSystemFonts --
+ *
+ * Initialize named system fonts.
+ *
+ * Results:
+ *
+ * None.
+ *
+ * Side effects:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static void
+InitSystemFonts(
+ TkMainInfo *mainPtr)
+{
+ Tcl_Interp *interp = mainPtr->interp;
+ Tk_Window tkwin = (Tk_Window) mainPtr->winPtr;
+ const struct SystemFontMapEntry *systemFont = systemFontMap;
+ TkFontAttributes fa;
+
+ /* force this for now */
+ if (!mainPtr->winPtr->mainPtr) {
+ mainPtr->winPtr->mainPtr = mainPtr;
+ }
+ TkInitFontAttributes(&fa);
+ while (systemFont->systemName) {
+ Str255 fontName;
+ SInt16 fontSize;
+ Style fontStyle;
+
+ if (GetThemeFont(systemFont->id, smSystemScript, fontName,
+ &fontSize, &fontStyle) == noErr) {
+ CopyPascalStringToC(fontName, (char*)fontName);
+ fa.family = Tk_GetUid((char*)fontName);
+ fa.size = fontSize;
+ fa.weight = (fontStyle & bold) ? TK_FW_BOLD : TK_FW_NORMAL;
+ fa.slant = (fontStyle & italic) ? TK_FS_ITALIC : TK_FS_ROMAN;
+ fa.underline = ((fontStyle & underline) != 0);
+ CreateNamedSystemFont(interp, tkwin, systemFont->systemName, &fa);
+ if (systemFont->tkName) {
+ CreateNamedSystemFont(interp, tkwin, systemFont->tkName, &fa);
+ }
+ if (systemFont->tkName1) {
+ CreateNamedSystemFont(interp, tkwin, systemFont->tkName1, &fa);
+ }
+ }
+ systemFont++;
+ }
+ fa.family = Tk_GetUid("monaco");
+ fa.size = 11;
+ fa.weight = TK_FW_NORMAL;
+ fa.slant = TK_FS_ROMAN;
+ fa.underline = 0;
+ CreateNamedSystemFont(interp, tkwin, "TkFixedFont", &fa);
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * 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);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetThemeFontAndFamily --
+ *
+ * Wrapper around the GetThemeFont and FMGetFontFamilyFromName APIs.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+OSStatus
+GetThemeFontAndFamily(
+ const ThemeFontID themeFontId,
+ FMFontFamily* fontFamily,
+ unsigned char *fontName,
+ SInt16 *fontSize,
+ Style *fontStyle)
+{
+ OSStatus err = ChkErr(GetThemeFont, themeFontId, smSystemScript, fontName,
+ fontSize, fontStyle);
+
+ if (err == noErr) {
+ *fontFamily = FMGetFontFamilyFromName(fontName);
+ if (*fontFamily == kInvalidFontFamily) {
+ err = kFMInvalidFontFamilyErr;
+ TkMacOSXDbgMsg("FMGetFontFamilyFromName failed.");
+ }
+ }
+
+ return err;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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. */
+{
+ ThemeFontID themeFontId;
+ FMFontFamily fontFamily;
+ Str255 fontName;
+ SInt16 fontSize;
+ Style fontStyle;
+ MacFont *fontPtr;
+
+ 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;
+ }
+ if (GetThemeFontAndFamily(themeFontId, &fontFamily, fontName, &fontSize,
+ &fontStyle) != noErr) {
+ return NULL;
+ }
+ CopyPascalStringToC(fontName, (char*)fontName);
+
+ fontPtr = ckalloc(sizeof(MacFont));
+ InitFont(fontFamily, (char*)fontName, fontSize, fontStyle, 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. */
+{
+ short qdStyle;
+ FMFontFamily familyId;
+ const char *name;
+ const MacFontFamily *familyPtr;
+ MacFont *fontPtr;
+
+ familyId = GetAppFont();
+ name = NULL;
+ qdStyle = 0;
+
+ if (faPtr->family != NULL) {
+ familyPtr = FindFontFamilyOrAliasOrFallback(faPtr->family);
+ if (familyPtr != NULL) {
+ name = familyPtr->name;
+ familyId = familyPtr->familyId;
+ }
+ }
+
+ if (faPtr->weight != TK_FW_NORMAL) {
+ qdStyle |= bold;
+ }
+ if (faPtr->slant != TK_FS_ROMAN) {
+ qdStyle |= italic;
+ }
+ if (faPtr->underline) {
+ qdStyle |= underline;
+ }
+ if (tkFontPtr == NULL) {
+ fontPtr = ckalloc(sizeof(MacFont));
+ } else {
+ fontPtr = (MacFont *) tkFontPtr;
+ ReleaseFont(fontPtr);
+ }
+ InitFont(familyId, name, TkFontGetPoints(tkwin, faPtr->size),
+ qdStyle, 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. */
+{
+ ReleaseFont((MacFont *) tkFontPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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_SetObjResult(interp, EnumFontFamilies());
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * 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. */
+{
+ /* We don't know much about our fallback fonts, ATSU does all that for
+ * us. We could use ATSUMatchFont to implement this function. But as
+ * the information is only used for testing, such an effort seems not
+ * very useful. */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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 */
+ Tcl_UniChar c, /* Character of interest */
+ TkFontAttributes* faPtr) /* Output: Font attributes */
+{
+ const MacFont * fontPtr = (const MacFont *) tkfont;
+ UniChar uchar = c;
+ TkMacOSXDrawingContext drawingContext;
+ OSStatus err;
+ ATSUFontID fontId;
+ UniCharArrayOffset changedOffset;
+ UniCharCount changedLength;
+
+ /*
+ * Most of the attributes are just copied from the base font. This
+ * assumes that all fonts can have all attributes.
+ */
+
+ *faPtr = fontPtr->font.fa;
+
+ /*
+ * But the name of the actual font may still differ, so we activate the
+ * string as an ATSU layout and ask ATSU about the fallback.
+ */
+ if (!TkMacOSXSetupDrawingContext(Tk_WindowId(tkwin), NULL, 1,
+ &drawingContext)) {
+ Tcl_Panic("TkpGetFontAttrsForChar: drawingContext not setup");
+ }
+
+ LayoutSetString(fontPtr, &drawingContext, &uchar, 1);
+
+ fontId = fontPtr->atsuFontId;
+ err = ATSUMatchFontsToText(
+ fontPtr->atsuLayout, 0, 1,
+ &fontId, &changedOffset, &changedLength);
+ if (err != kATSUFontsMatched && err != noErr) {
+ TkMacOSXDbgMsg("Can't match \\u%04X", (unsigned) c);
+ }
+
+ if (err == kATSUFontsMatched) {
+ /*
+ * A fallback was used and the actual font is in fontId. Determine
+ * the name.
+ */
+
+ FMFontFamily fontFamilyId;
+ FMFontStyle fontStyle;
+ int i;
+
+ err = ChkErr(FMGetFontFamilyInstanceFromFont, fontId, &fontFamilyId,
+ &fontStyle);
+ if (err == noErr) {
+ /*
+ * Find the canonical name in our global list.
+ */
+
+ for (i=0; i<familyListMaxValid; ++i) {
+ if (fontFamilyId == familyList[i].familyId) {
+ faPtr->family = familyList[i].name;
+ break;
+ }
+ }
+ if (i >= familyListMaxValid) {
+ TkMacOSXDbgMsg("Can't find font %d for \\u%04X", fontFamilyId,
+ (unsigned) c);
+ }
+ }
+ }
+
+ TkMacOSXRestoreDrawingContext(&drawingContext);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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;
+ int curX = -1, curByte = 0;
+ UniChar *uchars;
+ int ulen;
+ UniCharArrayOffset urstart, urlen, urend;
+ Tcl_DString ucharBuffer;
+ int forceCharacterMode = 0;
+
+ /*
+ * Sanity checks.
+ */
+
+ if (rangeStart < 0 || (rangeStart+rangeLength) > numBytes) {
+ TkMacOSXDbgMsg("Bad parameters");
+ *lengthPtr = 0;
+ return 0;
+ }
+
+ /*
+ * Get simple no-brainers out of the way.
+ */
+
+ if (rangeLength == 0 || (maxLength == 0 && !(flags & TK_AT_LEAST_ONE))) {
+ *lengthPtr = 0;
+ return 0;
+ }
+
+ Tcl_DStringInit(&ucharBuffer);
+ uchars = Tcl_UtfToUniCharDString(source, numBytes, &ucharBuffer);
+ ulen = Tcl_DStringLength(&ucharBuffer) / sizeof(Tcl_UniChar);
+ LayoutSetString(fontPtr, NULL, uchars, ulen);
+
+ urstart = Tcl_NumUtfChars(source, rangeStart);
+ urlen = Tcl_NumUtfChars(source+rangeStart,rangeLength);
+ urend = urstart + urlen;
+
+ if (maxLength < 0) {
+ curX = MeasureStringWidth(fontPtr, urstart, urend);
+ curByte = rangeLength;
+ } else {
+ UniCharArrayOffset offset = 0;
+ OSStatus err;
+
+ /*
+ * Have some upper limit on the size actually used.
+ */
+
+ if (maxLength > 32767) {
+ maxLength = 32767;
+ }
+
+ offset = urstart;
+ err = noErr;
+
+ if (maxLength > 1) {
+ /*
+ * Let the system do some work by calculating a line break.
+ *
+ * Somehow ATSUBreakLine seems to assume that it needs at least
+ * one pixel padding. So we add one to the limit. Note also
+ * that ATSUBreakLine sometimes runs into an endless loop when
+ * the third parameter is equal or less than IntToFixed(2), so we
+ * need at least IntToFixed(3) (at least that's the current state
+ * of my knowledge).
+ */
+
+ err = ATSUBreakLine(fontPtr->atsuLayout, urstart,
+ IntToFixed(maxLength+1), false, /* !iUseAsSoftLineBreak */
+ &offset);
+
+ /*
+ * There is no way to signal an error from this routine, so we
+ * use predefined offset=urstart and otherwise ignore the
+ * possibility.
+ */
+
+ if ((err != noErr) && (err != kATSULineBreakInWord)) {
+ TkMacOSXDbgMsg("ATSUBreakLine failed: %ld for '%.*s'", err,
+ rangeLength, source+rangeStart);
+ }
+
+#ifdef TK_MAC_DEBUG_FONTS
+ TkMacOSXDbgMsg("measure: '%.*s', break offset=%ld, errcode=%ld",
+ rangeLength, source+rangeStart, offset, err);
+#endif
+
+ /*
+ * ATSUBreakLine includes the whitespace that separates words,
+ * but we don't want that. Besides, ATSUBreakLine thinks that
+ * spaces don't occupy pixels at the end of the break, which is
+ * also something we like to decide for ourself.
+ */
+
+ while ((offset > urstart) && (uchars[offset-1] == ' ')) {
+ offset--;
+ }
+ }
+
+ /*
+ * Fix up left-overs for the TK_WHOLE_WORDS case.
+ */
+
+ if (flags & TK_WHOLE_WORDS) {
+ if ((flags & TK_AT_LEAST_ONE) && ((offset == urstart)
+ || ((offset != urend) && (uchars[offset] != ' ')))) {
+ /*
+ * With TK_AT_LEAST_ONE, if we are the the start of the
+ * range, we need to add at least one character. If we are
+ * not at the end of a word, we must be in the middle of the
+ * first word still and we want to just use what we have so
+ * far. In both cases we still need to find the right
+ * character boundary, so we set a flag that gets us into the
+ * code for character mode below.
+ */
+
+ forceCharacterMode = 1;
+
+ } else {
+ /*
+ * If we are not at the end of a word, we must be in the
+ * middle of the first word still. Return 0.
+ */
+
+ if ((offset != urend) && (uchars[offset] != ' ')) {
+ offset = urstart;
+ curX = 0;
+ }
+ }
+ }
+
+ if (offset > urend) {
+ offset = urend;
+ }
+
+ /*
+ * If "flags" says that we don't actually want a word break, we need
+ * to find the next character break ourself, as ATSUBreakLine will
+ * only give us word breaks. Do a simple linear search.
+ *
+ * Even do this, if ATSUBreakLine returned kATSULineBreakInWord,
+ * because we have not accounted correctly for all of the flags yet,
+ * like TK_AT_LEAST_ONE.
+ */
+
+ if ((!(flags & TK_WHOLE_WORDS) || forceCharacterMode) && (offset <= urend)) {
+ UniCharArrayOffset lastOffset = offset;
+ UniCharArrayOffset nextoffset;
+ int lastX = -1;
+ int wantonemorechar = -1; /* undecided */
+
+ while (offset <= urend) {
+ if (flags & TK_ISOLATE_END) {
+ LayoutSetString(fontPtr, NULL, uchars, offset);
+ }
+ curX = MeasureStringWidth(fontPtr, urstart, offset);
+
+#ifdef TK_MAC_DEBUG_FONTS
+ TkMacOSXDbgMsg("measure: '%.*s', try until=%ld, width=%d",
+ rangeLength, source+rangeStart, offset, curX);
+#endif
+
+ if (curX > maxLength) {
+ /*
+ * Even if we are over the limit, we may want another
+ * character in some situations. Than we keep looking
+ * for one more character.
+ */
+
+ if (wantonemorechar == -1) {
+ wantonemorechar = ((flags & TK_AT_LEAST_ONE) &&
+ (lastOffset == urstart)) ||
+ ((flags & TK_PARTIAL_OK) &&
+ (lastX != maxLength));
+ if (!wantonemorechar) {
+ break;
+ }
+ lastX = curX;
+ }
+
+ /*
+ * There may belong combining marks to this character.
+ * Wait for a new curX to collect them all.
+ */
+
+ if (lastX != curX) {
+ break;
+ }
+ }
+
+ /*
+ * Save this position, so we can come back to it.
+ */
+
+ lastX = curX;
+ lastOffset = offset;
+
+ /*
+ * Increment offset by one character, taking combining marks
+ * into account.
+ */
+
+ if (offset >= urend) {
+ break;
+ }
+ nextoffset = 0;
+ if (flags & TK_ISOLATE_END) {
+ LayoutSetString(fontPtr, NULL, uchars, ulen);
+ }
+ err = ChkErr(ATSUNextCursorPosition, fontPtr->atsuLayout,
+ offset, kATSUByCluster, &nextoffset);
+ if (err != noErr) {
+ break;
+ }
+ if (nextoffset <= offset) {
+#ifdef TK_MAC_DEBUG_FONTS
+ TkMacOSXDbgMsg("ATSUNextCursorPosition: Can't move further"
+ " (shouldn't happen, bad data?)");
+#endif
+ break;
+ }
+
+ offset = nextoffset;
+ }
+
+ /*
+ * We have overshot one character, so backup one position.
+ */
+
+ curX = lastX;
+ offset = lastOffset;
+ }
+
+ if (curX < 0) {
+ if (flags & TK_ISOLATE_END) {
+ LayoutSetString(fontPtr, NULL, uchars, offset);
+ }
+ curX = MeasureStringWidth(fontPtr, urstart, offset);
+ }
+
+ curByte = Tcl_UtfAtIndex(source, offset) - source;
+ curByte -= rangeStart;
+ }
+
+ Tcl_DStringFree(&ucharBuffer);
+
+#ifdef TK_MAC_DEBUG_FONTS
+ TkMacOSXDbgMsg("measure: '%.*s', maxLength=%d, flags=%s%s%s%s "
+ "-> width=%d, bytes=%d",
+ 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 " : "",
+ curX, curByte);
+#endif
+
+ *lengthPtr = curX;
+ return curByte;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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;
+ MacDrawable *macWin = (MacDrawable *) drawable;
+ Fixed fx, fy;
+ int ulen, urstart, urlen;
+ const UniChar * uchars;
+ int lineOffset;
+ TkMacOSXDrawingContext drawingContext;
+#if !TK_MAC_COALESCE_LINE
+ Tcl_DString runString;
+#endif
+
+ if (!TkMacOSXSetupDrawingContext(drawable, gc, tkMacOSXUseCGDrawing,
+ &drawingContext)) {
+ return;
+ }
+
+#if 0
+ /*
+ * TODO: implement stippled text drawing
+ */
+
+ if ((gc->fill_style == FillStippled
+ || gc->fill_style == FillOpaqueStippled)
+ && gc->stipple != None) {
+ #error Stippling not implemented
+ }
+#endif
+
+ x += macWin->xOff;
+ y += macWin->yOff;
+ /* Turn the y coordinate upside-down for Quarz drawing. */
+ if (drawingContext.context) {
+ CGContextConcatCTM(drawingContext.context, CGAffineTransformMake(1.0,
+ 0.0, 0.0, -1.0, 0.0, drawingContext.portBounds.bottom -
+ drawingContext.portBounds.top));
+ y = drawingContext.portBounds.bottom -
+ drawingContext.portBounds.top - y;
+ }
+ fy = IntToFixed(y);
+
+#if TK_MAC_COALESCE_LINE
+ UpdateLineBuffer(
+ fontPtr, &drawingContext, source, numBytes, x, y, &lineOffset);
+
+ fx = IntToFixed(currentLeft);
+
+ uchars = (const Tcl_UniChar*) Tcl_DStringValue(&currentLine);
+ ulen = Tcl_DStringLength(&currentLine) / sizeof(uchars[0]);
+#else
+ lineOffset = 0;
+ fx = IntToFixed(x);
+
+ Tcl_DStringInit(&runString);
+ uchars = Tcl_UtfToUniCharDString(source, numBytes, &runString);
+ ulen = Tcl_DStringLength(&runString) / sizeof(uchars[0]);
+
+ LayoutSetString(fontPtr, &drawingContext, uchars, ulen);
+#endif
+
+ urstart = Tcl_NumUtfChars(source, rangeStart);
+ urlen = Tcl_NumUtfChars(source+rangeStart,rangeLength);
+
+ /*
+ * Rotate the coordinate system for Quarz drawing.
+ */
+
+ if (drawingContext.context && angle != 0.0) {
+ CGContextConcatCTM(drawingContext.context, CGAffineTransformTranslate(
+ CGAffineTransformRotate(CGAffineTransformMakeTranslation(
+ x, y), angle * PI/180.0), -x, -y));
+ }
+
+ ChkErr(ATSUDrawText, fontPtr->atsuLayout, lineOffset+urstart, urlen, fx,
+ fy);
+
+#if !TK_MAC_COALESCE_LINE
+ Tcl_DStringFree(&runString);
+#endif
+
+ TkMacOSXRestoreDrawingContext(&drawingContext);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MeasureStringWidth --
+ *
+ * Low-level measuring of strings.
+ *
+ * Results:
+ * The width of the string in pixels.
+ *
+ * Side effects:
+ * None.
+ *
+ * Assumptions:
+ * fontPtr->atsuLayout is setup with the actual string data to measure.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+MeasureStringWidth(
+ const MacFont *fontPtr, /* Contains font, ATSU layout and string data
+ * to measure. */
+ int start, int end) /* Start and end positions to measure in that
+ * string. */
+{
+ /*
+ * This implementation of measuring via ATSUGetGlyphBounds() does not
+ * quite conform with the specification given for [font measure]:
+ *
+ * The return value is the total width in pixels of text, not
+ * including the extra pixels used by highly exagerrated characters
+ * such as cursive "f".
+ *
+ * Instead the result of ATSUGetGlyphBounds() *does* include these
+ * "extra pixels".
+ */
+
+ ATSTrapezoid bounds;
+ ItemCount numBounds;
+
+ if (end <= start) {
+ return 0;
+ }
+
+ bounds.upperRight.x = bounds.upperLeft.x = 0;
+ ChkErr(ATSUGetGlyphBounds, fontPtr->atsuLayout, 0, 0, start, end-start,
+ kATSUseFractionalOrigins, 1, &bounds, &numBounds);
+#ifdef TK_MAC_DEBUG_FONTS
+ if (numBounds < 1 || numBounds > 1) {
+ TkMacOSXDbgMsg("ATSUGetGlyphBounds: %s output",
+ numBounds < 1 ? "No " : "More");
+ }
+#endif
+
+ return FixedToInt(bounds.upperRight.x - bounds.upperLeft.x);
+}
+
+#if TK_MAC_COALESCE_LINE
+/*
+ *-------------------------------------------------------------------------
+ *
+ * UpdateLineBuffer --
+ *
+ * See the general dicussion of TK_MAC_COALESCE_LINE on the header
+ * pages. This function maintains the data for this feature.
+ *
+ * Results:
+ *
+ * The Tcl_UniChar string of the whole line as seen so far.
+ *
+ * Side effects:
+ * "*offset" is filled with the index of the first new character in
+ * "currentLine". The globals currentLine, currentY, currentLeft,
+ * currentRight and currentFontPtr are updated as necessary.
+ *
+ * The currentLine string is set as the current text in
+ * fontPtr->atsuLayout (see LayoutSetString()).
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static const Tcl_UniChar *
+UpdateLineBuffer(
+ const MacFont *fontPtr, /* The font to be used for the new piece of
+ * text. */
+ const TkMacOSXDrawingContext *drawingContextPtr,
+ /* The Quarz drawing parameters. Needed for
+ * measuring the new piece. */
+ const char *source, /* A new piece of line to be added. */
+ int numBytes, /* Length of the new piece. */
+ int x, int y, /* Position of the new piece in the window. */
+ int *offset) /* Filled with the offset of the new piece in
+ * currentLine. */
+{
+ const Tcl_UniChar * uchars;
+ int ulen;
+
+ if (y != currentY
+ || x < currentRight-1 || x > currentRight+2
+ || currentFontPtr != fontPtr) {
+ Tcl_DStringFree(&currentLine);
+ Tcl_DStringInit(&currentLine);
+ currentY = y;
+ currentLeft = x;
+ currentFontPtr = fontPtr;
+ *offset = 0;
+ } else {
+ *offset = Tcl_DStringLength(&currentLine) / 2;
+ }
+
+ Tcl_UtfToUniCharDString(source, numBytes, &currentLine);
+ uchars = (const Tcl_UniChar*) Tcl_DStringValue(&currentLine);
+ ulen = Tcl_DStringLength(&currentLine) / sizeof(*uchars);
+ LayoutSetString(fontPtr, drawingContextPtr, uchars, ulen);
+ currentRight = x + MeasureStringWidth(fontPtr, *offset, ulen);
+
+ return uchars;
+}
+#endif /* TK_MAC_COALESCE_LINE */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FamilyNameForFamilyID --
+ *
+ * Helper for InitFont() and TkMacOSXFontDescriptionForFMFontInfo().
+ * Retrieves font family names for a given font family ID.
+ *
+ * Results:
+ * Font family name or NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static const char *
+FamilyNameForFamilyID(
+ FMFontFamily familyId)
+{
+ OSStatus err;
+ char name[256] = "";
+ const MacFontFamily * familyPtr = NULL;
+
+ err = ChkErr(GetFontFamilyName, familyId, name, sizeof(name));
+ if (err == noErr) {
+ /*
+ * We find the canonical font name, so we can avoid unnecessary
+ * memory management.
+ */
+
+ familyPtr = FindFontFamily(name);
+#ifdef TK_MAC_DEBUG_FONTS
+ if (!familyPtr) {
+ TkMacOSXDbgMsg("Font family '%s' not found", name);
+ }
+#endif
+ }
+ return familyPtr ? familyPtr->name : NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * InitFont --
+ *
+ * Helper for TkpGetNativeFont() and TkpGetFontFromAttributes().
+ * Initializes the memory for a MacFont that wraps the
+ * platform-specific data.
+ *
+ * The caller is responsible for initializing the fields of the TkFont
+ * that are used exclusively by the generic TkFont code, and for
+ * releasing those fields before calling TkpDeleteFont().
+ *
+ * Results:
+ * Fills the MacFont structure.
+ *
+ * Side effects:
+ * Memory allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static void
+InitFont(
+ FMFontFamily familyId, /* The font family to initialize for. */
+ const char * familyName, /* The font family name, if known. Otherwise
+ * this can be NULL. */
+ int size, /* Point size for the font. */
+ int qdStyle, /* QuickDraw style bits. */
+ MacFont * fontPtr) /* Filled with information constructed from the
+ * above arguments. */
+{
+ FontInfo fi;
+ TkFontAttributes * faPtr;
+ TkFontMetrics * fmPtr;
+ int periodWidth, wWidth;
+
+ if (size == 0) {
+ size = GetDefFontSize();
+ }
+ ChkErr(FetchFontInfo, familyId, size, qdStyle, &fi);
+ if (!familyName) {
+ familyName = FamilyNameForFamilyID(familyId);
+ }
+
+ fontPtr->font.fid = (Font) fontPtr;
+
+ faPtr = &fontPtr->font.fa;
+ faPtr->family = familyName;
+ faPtr->size = size;
+ faPtr->weight = (qdStyle & bold) ? TK_FW_BOLD : TK_FW_NORMAL;
+ faPtr->slant = (qdStyle & italic) ? TK_FS_ITALIC : TK_FS_ROMAN;
+ faPtr->underline = ((qdStyle & underline) != 0);
+ faPtr->overstrike = 0;
+
+ fmPtr = &fontPtr->font.fm;
+
+ /*
+ * Note: Macs measure the line height as ascent + descent +
+ * leading. Leading as a separate entity does not exist in X11
+ * and Tk. We add it to the ascent at the moment, because adding
+ * it to the descent, as the Mac docs would indicate, would change
+ * the position of self-drawn underlines.
+ */
+
+ fmPtr->ascent = fi.ascent + fi.leading;
+ fmPtr->descent = fi.descent;
+ fmPtr->maxWidth = fi.widMax;
+
+ fontPtr->qdFont = familyId;
+ fontPtr->qdSize = size;
+ fontPtr->qdStyle = (short) qdStyle;
+
+ InitATSUObjects(familyId, size, qdStyle, &fontPtr->atsuFontId,
+ &fontPtr->atsuLayout, &fontPtr->atsuStyle);
+
+ Tk_MeasureChars((Tk_Font)fontPtr, ".", 1, -1, 0, &periodWidth);
+ Tk_MeasureChars((Tk_Font)fontPtr, "W", 1, -1, 0, &wWidth);
+ fmPtr->fixed = periodWidth == wWidth;
+
+ SetFontFeatures(fontPtr->atsuFontId, fmPtr->fixed, size,
+ fontPtr->atsuStyle);
+
+ AdjustFontHeight(fontPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * InitATSUObjects --
+ *
+ * Helper for InitFont(). Initializes the ATSU data handles for a
+ * MacFont.
+ *
+ * Results:
+ * Sets up all we know and can do at this point in time in fontIdPtr,
+ * layoutPtr and stylePtr.
+ *
+ * Side effects:
+ * Allocates data structures inside of ATSU.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static void
+InitATSUObjects(
+ FMFontFamily familyId, /* The font family to use. */
+ short ptSize, short qdStyles,
+ /* The additional font parameters. */
+ ATSUFontID *fontIdPtr, /* Filled with the font id. */
+ ATSUTextLayout *layoutPtr, /* Filled with the ATSU layout handle. */
+ ATSUStyle *stylePtr) /* Filled with the ATSU style handle,
+ * configured with all parameters. */
+{
+ FMFontStyle stylesDone, stylesLeft;
+
+ /*
+ * Defaults in case of error.
+ */
+
+ *fontIdPtr = GetAppFont();
+ *stylePtr = 0;
+ *layoutPtr = 0;
+
+ /*
+ * Generate a font id from family id and QD style bits.
+ */
+
+ ChkErr(FMGetFontFromFontFamilyInstance, familyId, qdStyles, fontIdPtr,
+ &stylesDone);
+
+ /*
+ * We see what style bits are left and tell ATSU to synthesize what's
+ * left like QD does it.
+ */
+
+ stylesLeft = qdStyles & ~(unsigned)stylesDone;
+
+ /*
+ * Create the style and set its attributes.
+ */
+
+ ChkErr(ATSUCreateStyle, stylePtr);
+ InitATSUStyle(*fontIdPtr, ptSize, stylesLeft, *stylePtr);
+
+ /*
+ * Create the layout. Note: We can't set the layout attributes here,
+ * because the text and the style must be set first.
+ */
+
+ ChkErr(ATSUCreateTextLayout, layoutPtr);
+ /*InitATSULayout(*layoutPtr);*/
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * InitATSUStyle --
+ *
+ * Helper for InitATSUObjects(). Initializes the ATSU style for a
+ * MacFont.
+ *
+ * Results:
+ * Sets up all parameters needed for an ATSU style.
+ *
+ * Side effects:
+ * Allocates data structures for the style inside of ATSU.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static void
+InitATSUStyle(
+ ATSUFontID fontId, /* The font id to use. */
+ short ptSize, short qdStyles,
+ /* Additional font parameters. */
+ ATSUStyle style) /* The style handle to configure. */
+{
+ /*
+ * Attributes for the style.
+ */
+
+ Fixed fsize = IntToFixed(ptSize);
+ Boolean
+ isBold = (qdStyles&bold) != 0,
+ isUnderline = (qdStyles&underline) != 0,
+ isItalic = (qdStyles&italic) != 0;
+
+ ATSStyleRenderingOptions options =
+ antialiasedTextEnabled == -1 ? kATSStyleNoOptions :
+ antialiasedTextEnabled == 0 ? kATSStyleNoAntiAliasing :
+ kATSStyleApplyAntiAliasing;
+
+ static const ATSUAttributeTag styleTags[] = {
+ kATSUFontTag, kATSUSizeTag,
+ kATSUQDBoldfaceTag, kATSUQDItalicTag, kATSUQDUnderlineTag,
+ kATSUStyleRenderingOptionsTag,
+ };
+ static const ByteCount styleSizes[] = {
+ sizeof(ATSUFontID), sizeof(Fixed),
+ sizeof(Boolean), sizeof(Boolean), sizeof(Boolean),
+ sizeof(ATSStyleRenderingOptions),
+ };
+ const ATSUAttributeValuePtr styleValues[] = {
+ &fontId, &fsize,
+ &isBold, &isItalic, &isUnderline,
+ &options,
+ };
+
+ ChkErr(ATSUSetAttributes, style, sizeof(styleTags)/sizeof(styleTags[0]),
+ styleTags, styleSizes, styleValues);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SetFontFeatures --
+ *
+ * Helper for InitFont(). Request specific font features of the ATSU
+ * style object for a MacFont.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Specific font features are enabled on the ATSU style object.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static void
+SetFontFeatures(
+ ATSUFontID fontId, /* The font id to use. */
+ int fixed, /* Is this a fixed font? */
+ short size, /* Size of the font */
+ ATSUStyle style) /* The style handle to configure. */
+{
+ /*
+ * Don't use the standard latin ligatures, if this is determined to be a
+ * fixed-width font.
+ */
+
+ static const ATSUFontFeatureType fixed_featureTypes[] = {
+ kLigaturesType, kLigaturesType
+ };
+ static const ATSUFontFeatureSelector fixed_featureSelectors[] = {
+ kCommonLigaturesOffSelector, kRareLigaturesOffSelector
+ };
+
+ if (fixed) {
+ ChkErr(ATSUSetFontFeatures, style, sizeof(fixed_featureTypes) /
+ sizeof(fixed_featureTypes[0]), fixed_featureTypes,
+ fixed_featureSelectors);
+ if (size <= 10) {
+ /*
+ * Disable antialiasing of fixed-width fonts with sizes <= 10
+ */
+
+ const ATSStyleRenderingOptions options = kATSStyleNoAntiAliasing;
+ const ATSUAttributeTag styleTag = kATSUStyleRenderingOptionsTag;
+ const ByteCount styleSize = sizeof(ATSStyleRenderingOptions);
+ const ConstATSUAttributeValuePtr styleValue = &options;
+
+ ChkErr(ATSUSetAttributes, style, 1, &styleTag, &styleSize,
+ (ATSUAttributeValuePtr*) &styleValue);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AdjustFontHeight --
+ *
+ * Helper for InitFont(). Check font height against some real world
+ * examples.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The metrics in fontPtr->font.fm are adjusted so that typical combined
+ * characters fit into ascent+descent.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static void
+AdjustFontHeight(
+ MacFont * fontPtr)
+{
+ /*
+ * The standard values for ascent, descent and leading as determined in
+ * InitFont do not take composition into account, they are designed for
+ * plain ASCII characters. This code measures the actual size of some
+ * typical composed characters from the Latin-1 range and corrects these
+ * factors, especially the ascent.
+ *
+ * A font requested with a pixel size may thus have a larger line height
+ * than requested.
+ *
+ * An alternative would be to instruct ATSU to shrink oversized combined
+ * characters. I think I have seen that feature somewhere, but I can't
+ * find it now [BR].
+ */
+
+ static const UniChar chars[]
+ /* Auml, Aacute, Acirc, Atilde, Ccedilla */
+ = {0x00C4, 0x00C1, 0x00C2, 0x00C3, 0x00C7};
+ static const int charslen = sizeof(chars) / sizeof(chars[0]);
+ Rect size;
+ OSStatus err;
+
+ LayoutSetString(fontPtr, NULL, chars, charslen);
+
+ size.top = size.bottom = 0;
+ err = ChkErr(ATSUMeasureTextImage, fontPtr->atsuLayout, 0, charslen, 0, 0,
+ &size);
+
+ if (err == noErr) {
+ TkFontMetrics * fmPtr = &fontPtr->font.fm;
+ int ascent = -size.top;
+ int descent = size.bottom;
+
+ if (ascent > fmPtr->ascent) {
+ fmPtr->ascent = ascent;
+ }
+ if (descent > fmPtr->descent) {
+ fmPtr->descent = descent;
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * InitATSULayout --
+ *
+ * Helper for LayoutSetString(). Initializes the ATSU layout
+ * object for a MacFont and a specific string.
+ *
+ * Results:
+ * Sets up all parameters needed for an ATSU layout object.
+ *
+ * Side effects:
+ * Allocates data structures for the layout object inside of ATSU.
+ *
+ * Assumptions:
+ * The actual string data and style information is already set by
+ * ATSUSetTextPointerLocation() and ATSUSetRunStyle() (see
+ * LayoutSetString()).
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static void
+InitATSULayout(
+ const TkMacOSXDrawingContext *drawingContextPtr,
+ /* Specifies the CGContext to use. */
+ ATSUTextLayout layout, /* The layout object to configure. */
+ int fixed) /* Is this a fixed font? */
+{
+ /*
+ * Attributes for the layout.
+ */
+
+ ATSLineLayoutOptions layoutOptions = 0
+#if TK_MAC_COALESCE_LINE
+ /*
+ * Options to use unconditionally when we try to do coalescing.
+ */
+ | kATSLineDisableAllLayoutOperations
+ | kATSLineFractDisable
+ | kATSLineUseDeviceMetrics
+#endif
+ ;
+ CGContextRef context = drawingContextPtr ?
+ drawingContextPtr->context : NULL;
+
+ static const ATSUAttributeTag layoutTags[] = {
+ kATSUCGContextTag,
+ kATSULineLayoutOptionsTag,
+ };
+ static const ByteCount layoutSizes[] = {
+ sizeof(CGContextRef),
+ sizeof(ATSLineLayoutOptions),
+ };
+ const ATSUAttributeValuePtr layoutValues[] = {
+ &context,
+ &layoutOptions,
+ };
+
+ /*
+ * Ensure W(abcdefg) == W(a)*7 for fixed fonts (Latin scripts only).
+ */
+
+ if (fixed) {
+ layoutOptions |= kATSLineFractDisable | kATSLineUseDeviceMetrics;
+ }
+
+ ChkErr(ATSUSetLayoutControls, layout, sizeof(layoutTags) /
+ sizeof(layoutTags[0]), layoutTags, layoutSizes, layoutValues);
+ ChkErr(ATSUSetTransientFontMatching, layout, true);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LayoutSetString --
+ *
+ * Setup the MacFont for a specific string.
+ *
+ * Results:
+ * Sets up all parameters so that ATSU can work with the objects in
+ * MacFont.
+ *
+ * Side effects:
+ * Sets parameters on the layout object fontPtr->atsuLayout.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+LayoutSetString(
+ const MacFont *fontPtr, /* The fontPtr to configure. */
+ const TkMacOSXDrawingContext *drawingContextPtr,
+ /* For the CGContext to be used.*/
+ const UniChar *uchars, int ulen)
+ /* The UniChar string to set into
+ * fontPtr->atsuLayout. */
+{
+ ChkErr(ATSUSetTextPointerLocation, fontPtr->atsuLayout, uchars,
+ kATSUFromTextBeginning, ulen, ulen);
+
+ /*
+ * Styles can only be set after the text is set.
+ */
+
+ ChkErr(ATSUSetRunStyle, fontPtr->atsuLayout, fontPtr->atsuStyle,
+ kATSUFromTextBeginning, kATSUToTextEnd);
+
+ /*
+ * Layout attributes can only be set after the styles are set.
+ */
+
+ InitATSULayout(drawingContextPtr, fontPtr->atsuLayout,
+ fontPtr->font.fm.fixed);
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * ReleaseFont --
+ *
+ * Called to release the Macintosh-specific contents of a TkFont. The
+ * caller is responsible for freeing the memory used by the font
+ * itself.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static void
+ReleaseFont(
+ MacFont *fontPtr) /* The font to delete. */
+{
+ ATSUDisposeTextLayout(fontPtr->atsuLayout);
+ ATSUDisposeStyle(fontPtr->atsuStyle);
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * FindFontFamilyOrAlias, FindFontFamilyOrAliasOrFallback --
+ *
+ * Determine if any physical screen font exists on the system with the
+ * given family name. If the family exists, then it should be possible
+ * to construct some physical screen font with that family name.
+ *
+ * FindFontFamilyOrAlias also considers font aliases as determined by
+ * TkFontGetAliasList().
+ *
+ * FindFontFamilyOrAliasOrFallback also considers font aliases as
+ * determined by TkFontGetFallbacks().
+ *
+ * The overall algorithm to get the closest font to the one requested is
+ * this:
+ *
+ * try fontname
+ * try all aliases for fontname
+ * foreach fallback for fontname
+ * try the fallback
+ * try all aliases for the fallback
+ *
+ * Results:
+ *
+ * The return value is NULL if the specified font family does not exist,
+ * a valid MacFontFamily* otherwise.
+ *
+ * Side effects:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static const MacFontFamily *
+FindFontFamilyOrAlias(
+ const char *name) /* Name or alias name of the font to find. */
+{
+ const MacFontFamily * familyPtr;
+ const char *const * aliases;
+ int i;
+
+ familyPtr = FindFontFamily(name);
+ if (familyPtr != NULL) {
+ return familyPtr;
+ }
+
+ aliases = TkFontGetAliasList(name);
+ if (aliases != NULL) {
+ for (i = 0; aliases[i] != NULL; i++) {
+ familyPtr = FindFontFamily(aliases[i]);
+ if (familyPtr != NULL) {
+ return familyPtr;
+ }
+ }
+ }
+ return NULL;
+}
+
+static const MacFontFamily *
+FindFontFamilyOrAliasOrFallback(
+ const char *name) /* Name or alias name of the font to find. */
+{
+ const MacFontFamily * familyPtr;
+ const char * fallback;
+ const char *const *const * fallbacks;
+ int i, j;
+
+ familyPtr = FindFontFamilyOrAlias(name);
+ if (familyPtr != NULL) {
+ return familyPtr;
+ }
+ fallbacks = TkFontGetFallbacks();
+ for (i = 0; fallbacks[i] != NULL; i++) {
+ for (j = 0; (fallback = fallbacks[i][j]) != NULL; j++) {
+ if (strcasecmp(name, fallback) == 0) {
+ for (j = 0; (fallback = fallbacks[i][j]) != NULL; j++) {
+ familyPtr = FindFontFamilyOrAlias(fallback);
+ if (familyPtr != NULL) {
+ return familyPtr;
+ }
+ }
+ }
+ break; /* benny: This "break" is a carry-over from
+ * tkMacOSXFont.c, but what is actually its purpose
+ * ???? */
+ }
+ }
+
+
+ /*
+ * FIXME: We would have liked to recover by re-enumerating fonts. But
+ * that doesn't work, because Carbon seems to cache the inital list of
+ * fonts. Fonts newly installed don't show up with
+ * FMCreateFontFamilyIterator()/FMGetNextFontFamily() without a restart
+ * of the app. Similar problem with fonts removed.
+ */
+
+#ifdef TK_MAC_DEBUG_FONTS
+ TkMacOSXDbgMsg("Font family '%s' not found", name);
+#endif
+
+ return NULL;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * InitFontFamilies --
+ *
+ * Helper to TkpFontPkgInit. Use the Font Manager to fill in the
+ * familyList global array.
+ *
+ * Results:
+ *
+ * None.
+ *
+ * Side effects:
+ *
+ * Allocates memory.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static void
+InitFontFamilies(void)
+{
+ FMFontFamily fontFamily;
+ Str255 fontName;
+ SInt16 fontSize;
+ Style fontStyle;
+
+ /*
+ * Has this been called before?
+ */
+
+ if (familyListNextFree > 0) {
+ return;
+ }
+
+ ChkErr(ATSFontFamilyApplyFunction, FontFamilyEnumCallback,NULL);
+
+ if (GetThemeFontAndFamily(kThemeSystemFont, &fontFamily, fontName,
+ &fontSize, &fontStyle) == noErr) {
+ AddFontFamily(SYSTEMFONT_NAME, fontFamily);
+ }
+ if (GetThemeFontAndFamily(kThemeApplicationFont, &fontFamily, fontName,
+ &fontSize, &fontStyle) == noErr) {
+ AddFontFamily(APPLFONT_NAME, fontFamily);
+ }
+ if (GetThemeFontAndFamily(kThemeMenuItemFont, &fontFamily, fontName,
+ &fontSize, &fontStyle) == noErr) {
+ AddFontFamily(MENUITEMFONT_NAME, fontFamily);
+ }
+
+ SortFontFamilies();
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * FontFamilyEnumCallback --
+ *
+ * Callback for ATSFontFamilyApplyFunction().
+ *
+ * Results:
+ *
+ * noErr.
+ *
+ * Side effects:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static OSStatus
+FontFamilyEnumCallback(
+ ATSFontFamilyRef family,
+ void *refCon)
+{
+ OSStatus err;
+ char name[260] = "";
+
+ (void) refCon;
+
+ err = ChkErr(GetFontFamilyName, family, name, sizeof(name));
+ if (err == noErr) {
+ AddFontFamily(name, family);
+ }
+
+ return noErr;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * GetFontFamilyName --
+ *
+ * Use the Font Manager to get the name of a given FMFontfamily. This
+ * currently gets the standard, non-localized QuickDraw name. Other
+ * names would be possible, see docs for ATSUFindFontName for a
+ * selection. The MacOSX font selector seems to use the localized
+ * family name given by ATSUFindFontName(kFontFamilyName), but that API
+ * doesn't give us a name at all for some fonts.
+ *
+ * Results:
+ * An OS error code, noErr on success. name is filled with the
+ * resulting name.
+ *
+ * Side effects:
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static OSStatus
+GetFontFamilyName(
+ FMFontFamily fontFamily, /* The font family for which to find the
+ * name. */
+ char * name, int numBytes) /* Filled with the result. */
+{
+ OSStatus err;
+ Str255 nativeName;
+ CFStringRef cfString;
+ TextEncoding encoding;
+ ScriptCode nameencoding;
+
+ nativeName[0] = 0;
+ name[0] = 0;
+ err = ChkErr(FMGetFontFamilyName, fontFamily, nativeName);
+ if (err != noErr) {
+ return err;
+ }
+
+ /*
+ * QuickDraw font names are encoded with the script that the font uses.
+ * So we determine that encoding and than we reencode the name. We
+ * pre-set the encoding with the default value, so we do not need to
+ * check result codes here.
+ */
+
+ encoding = kTextEncodingMacRoman;
+ ChkErr(FMGetFontFamilyTextEncoding, fontFamily, &encoding);
+ nameencoding = encoding;
+ ChkErr(RevertTextEncodingToScriptInfo, encoding, &nameencoding, NULL,
+ NULL);
+
+ /*
+ * Note: We could use Tcl facilities to do the re-encoding here. We'd
+ * have to maintain tables to map OS encoding codes to Tcl encoding names
+ * like tkMacOSXFont.c did. Using native re-encoding directly instead is
+ * a lot easier and future-proof than that. There is one snag, though: I
+ * have seen CFStringGetCString() crash with invalid encoding ids. But
+ * than if that happens it would be a bug in
+ * FMGetFontFamilyTextEncoding() or RevertTextEncodingToScriptInfo().
+ * Another problem is that users have seen CFStringCreate return null
+ * (Bug #2548661). This is due to font names with a bad encoding.
+ */
+
+ cfString = CFStringCreateWithPascalStringNoCopy(
+ NULL, nativeName, nameencoding, kCFAllocatorNull);
+ if (cfString == NULL) {
+ TkMacOSXDbgMsg("CFStringCreate: "
+ "'%.*s' could not be decoded with encoding %d",
+ nativeName[0], nativeName+1, (int) nameencoding);
+ return kTextMalformedInputErr;
+ }
+
+ CFStringGetCString(cfString, name, numBytes, kCFStringEncodingUTF8);
+ CFRelease(cfString);
+
+ return noErr;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * FindFontFamily --
+ *
+ * Find the font family with the given name in the global familyList.
+ * Uses bsearch() for convenient access. Comparision is done
+ * non-case-sensitively with CompareFontFamilies() which see.
+ *
+ * Results:
+ *
+ * MacFontFamily: A pair of family id and the actual name registered for
+ * the font.
+ *
+ * Side effects:
+ *
+ * None.
+ *
+ * Assumption:
+ *
+ * Requires the familyList array to be sorted.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static const MacFontFamily *
+FindFontFamily(
+ const char *name) /* The family name. Note: Names are compared
+ * non-case-sensitive. */
+{
+ const MacFontFamily key = {name,-1};
+
+ if(familyListMaxValid <= 0) {
+ return NULL;
+ }
+
+ return bsearch(&key, familyList, familyListMaxValid, sizeof(*familyList),
+ CompareFontFamilies);
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * EnumFontFamilies --
+ *
+ * Create a Tcl list with the registered names in the global familyList.
+ *
+ * Results:
+ * A Tcl list of names.
+ *
+ * Side effects:
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+EnumFontFamilies(void)
+{
+ int i;
+ Tcl_Obj * tclList;
+
+ tclList = Tcl_NewListObj(0, NULL);
+ for (i=0; i<familyListMaxValid; ++i) {
+ Tcl_ListObjAppendElement(NULL, tclList,
+ Tcl_NewStringObj(familyList[i].name, -1));
+ }
+
+ return tclList;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * AddFontFamily --
+ *
+ * Register a font family in familyList. Until SortFontFamilies() is
+ * called, this is not actually available for FindFontFamily().
+ *
+ * Results:
+ *
+ * MacFontFamily: The new pair of family id and the actual name
+ * registered for the font.
+ *
+ * Side effects:
+ *
+ * New entry in familyList and familyListNextFree updated.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static const MacFontFamily *
+AddFontFamily(
+ const char *name, /* Font family name to register. */
+ FMFontFamily familyId) /* Font family id to register. */
+{
+ MacFontFamily * familyPtr;
+
+ if (familyListNextFree >= familyListSize) {
+ familyListSize += 100;
+ familyList = ckrealloc(familyList,
+ familyListSize * sizeof(*familyList));
+ }
+
+ familyPtr = familyList + familyListNextFree;
+ ++familyListNextFree;
+
+ familyPtr->name = AddString(name);
+ familyPtr->familyId = familyId;
+
+ return familyPtr;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * SortFontFamilies --
+ *
+ * Sort the entries in familyList. Only after calling
+ * SortFontFamilies(), the new families registered with AddFontFamily()
+ * are actually available for FindFontFamily(), because FindFontFamily()
+ * requires the array to be sorted.
+ *
+ * Results:
+ *
+ * None.
+ *
+ * Side effects:
+ *
+ * familyList is sorted and familyListMaxValid is updated.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static void
+SortFontFamilies(void)
+{
+ if (familyListNextFree > 0) {
+ qsort(familyList, familyListNextFree, sizeof(*familyList),
+ CompareFontFamilies);
+ }
+ familyListMaxValid = familyListNextFree;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * CompareFontFamilies --
+ *
+ * Comparison function used by SortFontFamilies() and FindFontFamily().
+ *
+ * Results:
+ * Result as required to generate a stable sort order for bsearch() and
+ * qsort(). The ordering is not case-sensitive as far as
+ * Tcl_UtfNcasecmp() (which see) can provide that.
+ *
+ * Note: It would be faster to compare first the length and the actual
+ * strings only as a tie-breaker, but than the ordering wouldn't look so
+ * pretty in [font families] ;-).
+ *
+ * Side effects:
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static int
+CompareFontFamilies(
+ const void * vp1,
+ const void * vp2)
+{
+ const char * name1;
+ const char * name2;
+ int len1, len2, diff;
+
+ name1 = ((const MacFontFamily *) vp1)->name;
+ name2 = ((const MacFontFamily *) vp2)->name;
+
+ len1 = Tcl_NumUtfChars(name1, -1);
+ len2 = Tcl_NumUtfChars(name2, -1);
+
+ diff = Tcl_UtfNcasecmp(name1, name2, len1<len2 ? len1 : len2);
+
+ return diff == 0 ? len1-len2 : diff;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * AddString --
+ *
+ * Helper for AddFontFamily(). Allocates a string in the one-shot
+ * allocator.
+ *
+ * Results:
+ * A duplicated string in the one-shot allocator.
+ *
+ * Side effects:
+ * May allocate a new memory block.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static const char *
+AddString(
+ const char *in) /* String to add, zero-terminated. */
+{
+ int len;
+ char *result;
+
+ len = strlen(in) +1;
+
+ if (stringMemory == NULL
+ || (stringMemory->nextFree+len) > STRING_BLOCK_MAX) {
+ StringBlock *newblock = ckalloc(sizeof(StringBlock));
+
+ newblock->next = stringMemory;
+ newblock->nextFree = 0;
+ stringMemory = newblock;
+ }
+
+ result = stringMemory->strings + stringMemory->nextFree;
+ stringMemory->nextFree += len;
+
+ memcpy(result, in, len);
+
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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. */
+{
+ /* Background: This function is private and only used in
+ * tkMacOSXMenu.c:FindMarkCharacter().
+ *
+ * We could use ATSUMatchFont() to implement. We'd have to change the
+ * definition of the encoding of the parameter searchChar from MacRoman
+ * to UniChar for that.
+ *
+ * The system uses font fallback for controls, so we don't really need
+ * this. */
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXInitControlFontStyle --
+ *
+ * This procedure sets up the appropriate ControlFontStyleRec
+ * for a Mac control.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkMacOSXInitControlFontStyle(
+ Tk_Font tkfont, /* Tk font object to use for the control. */
+ ControlFontStylePtr fsPtr) /* The style object to configure. */
+{
+ const MacFont * fontPtr = (MacFont *) tkfont;
+
+ fsPtr->flags = kControlUseFontMask | kControlUseSizeMask |
+ kControlUseFaceMask | kControlUseJustMask;
+ fsPtr->font = fontPtr->qdFont;
+ fsPtr->size = fontPtr->qdSize;
+ fsPtr->style = fontPtr->qdStyle;
+ fsPtr->just = teCenter;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXFMFontInfoForFont --
+ *
+ * Retrieve FontManager/ATSUI font information for a Tk font.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+MODULE_SCOPE void
+TkMacOSXFMFontInfoForFont(
+ Tk_Font tkfont,
+ FMFontFamily *fontFamilyPtr,
+ FMFontStyle *fontStylePtr,
+ FMFontSize *fontSizePtr,
+ ATSUStyle *fontATSUStylePtr)
+{
+ const MacFont * fontPtr = (MacFont *) tkfont;
+
+ if (fontFamilyPtr) {
+ *fontFamilyPtr = fontPtr->qdFont;
+ }
+ if (fontStylePtr) {
+ *fontStylePtr = fontPtr->qdStyle;
+ }
+ if (fontSizePtr) {
+ *fontSizePtr = fontPtr->qdSize;
+ }
+ if (fontATSUStylePtr) {
+ *fontATSUStylePtr = fontPtr->atsuStyle;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXFontDescriptionForFMFontInfo --
+ *
+ * Get text description of a font specified by FontManager info.
+ *
+ * Results:
+ * List object or NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+MODULE_SCOPE Tcl_Obj *
+TkMacOSXFontDescriptionForFMFontInfo(
+ FMFontFamily fontFamily,
+ FMFontStyle fontStyle,
+ FMFontSize fontSize,
+ FMFont fontID)
+{
+ Tcl_Obj *objv[6];
+ int i = 0;
+
+ if (fontFamily != kInvalidFontFamily && fontStyle != -1) {
+ const char *familyName = FamilyNameForFamilyID(fontFamily);
+
+ if (familyName) {
+ objv[i++] = Tcl_NewStringObj(familyName, -1);
+ objv[i++] = Tcl_NewIntObj(fontSize);
+#define S(s) Tcl_NewStringObj(STRINGIFY(s),(int)(sizeof(STRINGIFY(s))-1))
+ objv[i++] = (fontStyle & bold) ? S(bold) : S(normal);
+ objv[i++] = (fontStyle & italic) ? S(italic) : S(roman);
+ if (fontStyle & underline) objv[i++] = S(underline);
+ /*if (fontStyle & overstrike) objv[i++] = S(overstrike);*/
+#undef S
+ }
+ } else if (fontID != kInvalidFont) {
+ CFStringRef fontName = NULL;
+ Tcl_Obj *fontNameObj = NULL;
+
+ ChkErr(ATSFontGetName, FMGetATSFontRefFromFont(fontID),
+ kATSOptionFlagsDefault, &fontName);
+ if (fontName) {
+ fontNameObj = TkMacOSXGetStringObjFromCFString(fontName);
+ CFRelease(fontName);
+ }
+ if (fontNameObj) {
+ objv[i++] = fontNameObj;
+ objv[i++] = Tcl_NewIntObj(fontSize);
+ }
+ }
+ 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: c
+ * c-basic-offset: 4
+ * fill-column: 79
+ * coding: utf-8
+ * End:
+ */