/*
 * tkMacOSXFont.c --
 *
 *        Contains the Macintosh implementation of the platform-independant
 *        font package interface.
 *
 * 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.
 *
 * RCS: @(#) $Id: tkMacOSXFont.c,v 1.3.2.8 2006/03/28 02:44:13 das Exp $
 */

#include "tkMacOSXInt.h"
#include "tkMacOSXFont.h"

#include "tclInt.h" /* for Tcl_CreateNamespace() */

/*
 * For doing things with Mac strings and Fixed numbers.  This probably should move 
 * the mac header file.
 */

#ifndef StrLength
#define StrLength(s)  (*((unsigned char *) (s)))
#endif
#define pstrcmp(s1, s2) RelString((s1), (s2), 1, 1)

#ifndef Fixed2Int
#define Fixed2Int(f) ((f) >> 16)
#define Int2Fixed(i) ((i) << 16)
#endif

/*
 * The following structure represents a font family.  It is assumed that
 * all screen fonts constructed from the same "font family" share certain
 * properties; all screen fonts with the same "font family" point to a
 * shared instance of this structure.  The most important shared property
 * is the character existence metrics, used to determine if a screen font
 * can display a given Unicode character.
 *
 * Under Macintosh, a "font family" is uniquely identified by its face number.
 *
 * The following structure represents Macintosh's implementation of a font
 * object.
 */

typedef struct MacFont {
    TkFont font;                /* Stuff used by generic font package.  Must
                                 * be first in structure. */
    ATSUStyle atsuStyle;	/* ATSU text style (including font ID). */
    FMFontFamily faceNum;       /* Unique face number key for this FontFamily. */
    short size;                 /* Font size in pixels, constructed from
                                 * font attributes. */
    short style;                /* Style bits, constructed from font
                                 * attributes. */
} MacFont;

/*
 * The following structure is used to map between the UTF-8 name for a font and
 * the name that the Macintosh uses to refer to the font, in order to determine
 * if a font exists.  The Macintosh names for fonts are stored in the encoding 
 * of the font itself.
 */
 
typedef struct FontNameMap {
    Tk_Uid utfName;              /* The name of the font in UTF-8. */
    StringPtr nativeName;        /* The name of the font in the font's encoding. */
    FMFontFamily faceNum;        /* Unique face number for this font. */
} FontNameMap;

/*
 * Information cached about the system at startup time.
 */
 
static FontNameMap *gFontNameMap = NULL;
static GWorldPtr gWorld = NULL;

/*
 * Procedures used only in this file.
 */

static void InitFont(Tk_Window tkwin, int family, int size, int style, MacFont *fontPtr);
static void MultiFontDrawText(MacFont *fontPtr, CONST char *source, int numBytes, int x, int y);

static void BreakLine(MacFont *fontPtr, int flags, CONST char *source, int *numBytes, int *widthPtr);
static int GetFamilyNum(CONST char *faceName, short *familyPtr);
static int GetFamilyOrAliasNum(CONST char *faceName, short *familyPtr);
static Tk_Uid GetUtfFaceName(StringPtr faceNameStr);
static int GetUtf8TextWidth(char *source, int numBytes, MacFont *fontPtr);

/*
 *-------------------------------------------------------------------------
 * 
 * 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:
 *        See comments below.
 *
 *-------------------------------------------------------------------------
 */

void
TkpFontPkgInit(mainPtr)
    TkMainInfo *mainPtr;        /* The application being created. */
{
    FMFontFamilyIterator fontFamilyIterator;
    FMFontFamily         fontFamily;
    FontNameMap *tmpFontNameMap, *mapPtr;
    int i, numFonts, isSymbol;
    Str255 nativeName;
    
    if (gWorld == NULL) {
        Rect rect = {0, 0, 1, 1};
        SetFractEnable(0);
        /*
         * Used for saving and restoring state while drawing and measuring.
         */
        if (NewGWorld(&gWorld, 0, &rect, NULL, NULL, 0) != noErr) {
            Tcl_Panic("TkpFontPkgInit: NewGWorld failed");
        }
        /*
         * The name of each font is stored in the encoding of that font.
         * How would we translate a name from UTF-8 into the native encoding
         * of the font unless we knew the encoding of that font?  We can't.
         * So, precompute the UTF-8 and native names of all fonts on the 
         * system.  The when the user asks for font by its UTF-8 name, we
         * lookup the name in that table and really ask for the font by its
         * native name.  Any unknown UTF-8 names will be mapped to the system 
         * font.
         */
        FMCreateFontFamilyIterator (NULL, NULL, kFMDefaultOptions, &fontFamilyIterator);
        numFonts = 0;
        while (FMGetNextFontFamily(&fontFamilyIterator, &fontFamily) != kFMIterationCompleted) {
            numFonts++;
        }
        tmpFontNameMap = (FontNameMap *) ckalloc(sizeof(FontNameMap) * numFonts + 1);
        mapPtr = tmpFontNameMap;
        FMResetFontFamilyIterator(NULL, NULL, kFMDefaultOptions, &fontFamilyIterator);
        i = 0;
        while (FMGetNextFontFamily(&fontFamilyIterator, &fontFamily) != kFMIterationCompleted) {
	    char name[256];

            mapPtr->faceNum = fontFamily;
            FMGetFontFamilyName(fontFamily, nativeName);
            TextEncoding encoding = kTextEncodingMacRoman;
    	    FMGetFontFamilyTextEncoding(fontFamily, &encoding);
	    ScriptCode script;
   	    RevertTextEncodingToScriptInfo(encoding, &script, NULL, NULL);
	    CFStringRef cfString = CFStringCreateWithPascalStringNoCopy(NULL, nativeName, script, kCFAllocatorNull);
	    CFStringGetCString(cfString, name, sizeof(name), kCFStringEncodingUTF8);
   	    CFRelease(cfString);
            mapPtr->utfName = (char *)ckalloc(strlen(name) + 1);
            strcpy(mapPtr->utfName, name);
            mapPtr->nativeName = (StringPtr) ckalloc(StrLength(nativeName) + 1);
            memcpy(mapPtr->nativeName, nativeName, StrLength(nativeName) + 1);
            mapPtr++;
            i++;
        }
        FMDisposeFontFamilyIterator (&fontFamilyIterator);
               
        mapPtr->utfName = NULL;
        mapPtr->nativeName = NULL;
        mapPtr->faceNum = 0;

        gFontNameMap = tmpFontNameMap;
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * 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. */
{
    SInt16 family;
    MacFont *fontPtr;
    
    if (strcmp(name, "system") == 0) {
        family = GetSysFont();
    } else if (strcmp(name, "application") == 0) {
        family = GetAppFont();
    } else {
        return NULL;
    }
    
    fontPtr = (MacFont *) ckalloc(sizeof(MacFont));
    InitFont(tkwin, family, 0, 0, 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 faceNum, style;
    int i, j;
    CONST char *faceName, *fallback;
    char ***fallbacks;
    MacFont *fontPtr;
        
    /*
     * Algorithm to get the closest font to the one requested.
     *
     * try fontname
     * try all aliases for fontname
     * foreach fallback for fontname
     *            try the fallback
     *            try all aliases for the fallback
     */
     
    faceNum = 0;
    faceName = faPtr->family;
    if (faceName != NULL) {
        if (GetFamilyOrAliasNum(faceName, &faceNum) != 0) {
            goto found;
        }
        fallbacks = TkFontGetFallbacks();
        for (i = 0; fallbacks[i] != NULL; i++) {
            if (fallbacks[i][0] != NULL && strcasecmp(faceName, fallbacks[i][0]) == 0) {
                for (j = 0; (fallback = fallbacks[i][j]) != NULL; j++) {
                    if (GetFamilyOrAliasNum(fallback, &faceNum) != 0) {
                        goto found;
                    }
                }
            }
        }
    }
    
    found:    
    style = 0;
    if (faPtr->weight != TK_FW_NORMAL) {
        style |= bold;
    }
    if (faPtr->slant != TK_FS_ROMAN) {
        style |= italic;
    }
    if (faPtr->underline) {
        style |= underline;
    }
    if (tkFontPtr == NULL) {
        fontPtr = (MacFont *) ckalloc(sizeof(MacFont));
    } else {
        fontPtr = (MacFont *) tkFontPtr;
    }
    InitFont(tkwin, faceNum, faPtr->size, style, 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:
 *        None.
 *
 * Side effects:
 *        TkFont is deallocated.
 *
 *---------------------------------------------------------------------------
 */

void
TkpDeleteFont(
    TkFont *tkFontPtr)                /* Token of font to be deleted. */
{
    MacFont *fontPtr;
    
    fontPtr = (MacFont *) tkFontPtr;
    ATSUDisposeStyle(fontPtr->atsuStyle);
}

/*
 *---------------------------------------------------------------------------
 *
 * 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. */
{    
    FontNameMap *mapPtr;
    Tcl_Obj *resultPtr, *strPtr;
        
    resultPtr = Tcl_GetObjResult(interp);
    for (mapPtr = gFontNameMap; mapPtr->utfName != NULL; mapPtr++) {
        strPtr = Tcl_NewStringObj(mapPtr->utfName, -1);
        Tcl_ListObjAppendElement(NULL, resultPtr, strPtr);
    }
}

/*
 *-------------------------------------------------------------------------
 *
 * 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(interp, tkfont)
    Tcl_Interp *interp;            /* Interp to hold result. */
    Tk_Font tkfont;                /* Font object to query. */
{
    Tcl_Obj *resultPtr, *strPtr;
    MacFont *fontPtr;
    Str255 nativeName;

    resultPtr = Tcl_GetObjResult(interp);    
    fontPtr = (MacFont *) tkfont;
    FMGetFontFamilyName(fontPtr->faceNum, nativeName);
    strPtr = Tcl_NewStringObj(GetUtfFaceName(nativeName), -1);
    Tcl_ListObjAppendElement(NULL, resultPtr, strPtr);
}

/*
 *---------------------------------------------------------------------------
 *
 *  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.
 *
 * 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.
 *
 *---------------------------------------------------------------------------
 */

static ATSUAttributeTag lineLayoutOptions = kATSLineUseDeviceMetrics;
static ATSUAttributeTag lTags[1] = { kATSULineLayoutOptionsTag };
static ByteCount lSizes[1] = { sizeof(ATSUAttributeTag) };
static ATSUAttributeValuePtr lValues[1] = { &lineLayoutOptions };

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. */
{
    MacFont *fontPtr;
    CGrafPtr saveWorld;
    GDHandle saveDevice;

    fontPtr = (MacFont *) tkfont;
    GetGWorld(&saveWorld, &saveDevice);
    SetGWorld(gWorld, NULL);
    if (numBytes <= 0) {
        *lengthPtr = 0;
    } else if (maxLength < 0) {
        *lengthPtr = GetUtf8TextWidth(source, numBytes, fontPtr);
    } else {
        if (maxLength > 32767) {
            maxLength = 32767;
        }
	*lengthPtr = maxLength;
        BreakLine(fontPtr, flags, source, &numBytes, lengthPtr);
    }
    SetGWorld(saveWorld, saveDevice);
    return numBytes;
}

/*
 *---------------------------------------------------------------------------
 *
 * BreakLine --
 *
 *        Determine where the given line of text should be broken so that it
 *        fits in the specified range.  Before calling this function, the 
 *        font values and graphics port must be set.
 *
 *---------------------------------------------------------------------------
 */

static void 
BreakLine(
    MacFont *fontPtr,           /* The font to use. */
    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. */                                 
    CONST char *source,         /* UTF-8 string to be displayed.  Need not be
                                 * '\0' terminated. */
    int *numBytesPtr,           /* Maximum number of bytes to consider
                                 * from source string, output as bytes
				 * to display. */
    int *widthPtr)              /* On input, specifies size of range into 
                                 * which characters from source buffer should
                                 * be fit.  On output, filled with how much
                                 * space is used. */
{
    int totalWidth = GetUtf8TextWidth(source, *numBytesPtr, fontPtr);
    if (totalWidth <= *widthPtr) {
        *widthPtr = totalWidth;
        return;
    }

    int textOffset = 0;
    Boolean leadingEdge = false;
    Tcl_DString runString;
    Tcl_DStringInit(&runString);
    Tcl_UtfToUniCharDString(source, *numBytesPtr, &runString);
    int utf16length = Tcl_DStringLength(&runString)/2;
    ATSUTextLayout mLayout;
    ATSUCreateTextLayout(&mLayout);
    ATSUSetTextPointerLocation(mLayout, Tcl_DStringValue(&runString), kATSUFromTextBeginning, kATSUToTextEnd, utf16length);
    ATSUSetRunStyle(mLayout, fontPtr->atsuStyle, kATSUFromTextBeginning, kATSUToTextEnd);
    ATSUSetTransientFontMatching(mLayout, true);
    ATSUSetLayoutControls(mLayout, 1, lTags, lSizes, lValues);
    if (flags & TK_WHOLE_WORDS) {
        ATSUBreakLine(mLayout, kATSUFromTextBeginning, Int2Fixed(*widthPtr), false, &textOffset);
    } else {
	int secondaryOffset;
	ATSUPositionToOffset(mLayout, Int2Fixed(*widthPtr), Int2Fixed(((int)fontPtr->size/2)), &textOffset, &leadingEdge, &secondaryOffset);
    }
    ATSUDisposeTextLayout(mLayout);
    Tcl_DStringFree(&runString);

    if ((flags & TK_WHOLE_WORDS) == 0) {
        if ((flags & TK_PARTIAL_OK) && (leadingEdge != 0)) {
            textOffset++;
        } else if (((flags & TK_PARTIAL_OK) == 0) && (leadingEdge == 0)) {
            textOffset--;
        }
    }
    if (textOffset < 0) {
        textOffset = 0;
    }
    if ((textOffset == 0) && (*numBytesPtr > 0) 
                && (flags & TK_AT_LEAST_ONE)) {
        textOffset++;
    }
    if (textOffset > utf16length) {
	textOffset = utf16length;
    }
    *numBytesPtr = (char *)(Tcl_UtfAtIndex(source, textOffset)) - (char *)source;
    if (textOffset == 0) {
        *widthPtr = 0;
	return;
    }
    *widthPtr = GetUtf8TextWidth(source, *numBytesPtr, fontPtr);
}

static int GetUtf8TextWidth(char *source, int numBytes, MacFont *fontPtr) {
    int length = 0;
    if (numBytes > 0) {
	Tcl_DString runString;
	Tcl_DStringInit(&runString);
	Tcl_UtfToUniCharDString(source, numBytes, &runString);
	ATSUTextLayout mLayout;
	ATSUCreateTextLayout(&mLayout);
	ATSUSetTextPointerLocation(mLayout, Tcl_DStringValue(&runString), kATSUFromTextBeginning, kATSUToTextEnd, Tcl_DStringLength(&runString)/2);
        ATSUSetRunStyle(mLayout, fontPtr->atsuStyle, kATSUFromTextBeginning, kATSUToTextEnd);
	ATSUSetTransientFontMatching(mLayout, true);
	ATSUSetLayoutControls(mLayout, 1, lTags, lSizes, lValues);
	ItemCount numBounds;
	ATSUGetGlyphBounds(mLayout, Int2Fixed(0), Int2Fixed(0), kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 0, NULL, &numBounds);
	ATSTrapezoid *res = (ATSTrapezoid *) ckalloc(sizeof(ATSTrapezoid) * numBounds);
	ATSUGetGlyphBounds(mLayout, Int2Fixed(0), Int2Fixed(0), kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, numBounds, res, &numBounds);
	length = Fixed2Int(res[numBounds - 1].upperRight.x);
	ckfree(res);
	ATSUDisposeTextLayout(mLayout);
	Tcl_DStringFree(&runString);
	return length;
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_DrawChars --
 *
 *        Draw a string of characters on the screen.  
 *
 * 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
                                  * string when drawing. */
{
    MacFont *fontPtr;
    MacDrawable *macWin;
    RGBColor macColor, origColor;
    GWorldPtr destPort;
    CGrafPtr saveWorld;
    GDHandle saveDevice;
    short txFont, txFace, txSize;
    BitMapPtr stippleMap;
    Rect      portRect;

    fontPtr = (MacFont *) tkfont;
    macWin = (MacDrawable *) drawable;

    destPort = TkMacOSXGetDrawablePort(drawable);
    GetPortBounds(destPort, &portRect);
    GetGWorld(&saveWorld, &saveDevice);
    SetGWorld(destPort, NULL);
    
    TkMacOSXSetUpClippingRgn(drawable);
    TkMacOSXSetUpGraphicsPort(gc, destPort);

    txFont = GetPortTextFont(destPort);
    txFace = GetPortTextFace(destPort);
    txSize = GetPortTextSize(destPort);
    GetForeColor(&origColor);
    
    if ((gc->fill_style == FillStippled
            || gc->fill_style == FillOpaqueStippled)
            && gc->stipple != None) {
        Pixmap pixmap;
        GWorldPtr bufferPort;
        Pattern   white;

        stippleMap = TkMacOSXMakeStippleMap(drawable, gc->stipple);

        pixmap = Tk_GetPixmap(display, drawable,         
            stippleMap->bounds.right, stippleMap->bounds.bottom, 0);
                
        bufferPort = TkMacOSXGetDrawablePort(pixmap);
        SetGWorld(bufferPort, NULL);
        
        if (TkSetMacColor(gc->foreground, &macColor) == true) {
            RGBForeColor(&macColor);
        }
        GetQDGlobalsWhite(&white);
        ShowPen();
        FillRect(&stippleMap->bounds, &white);
        MultiFontDrawText(fontPtr, source, numBytes, 0, 0);
        HidePen();

        SetGWorld(destPort, NULL);
        CopyDeepMask(GetPortBitMapForCopyBits(bufferPort), stippleMap, 
            GetPortBitMapForCopyBits(destPort), &stippleMap->bounds,
            &stippleMap->bounds, &portRect,
            srcOr, NULL);
        
        /* TODO: this doesn't work quite right - it does a blend.   you can't
         * draw white text when you have a stipple.
         */
                
        Tk_FreePixmap(display, pixmap);
        ckfree(stippleMap->baseAddr);
        ckfree((char *)stippleMap);
    } else {        
        if (TkSetMacColor(gc->foreground, &macColor) == true) {
            RGBForeColor(&macColor);
        }
        ShowPen();
        MultiFontDrawText(fontPtr, source, numBytes, macWin->xOff + x,
                macWin->yOff + y);
        HidePen();
    }

    TextFont(txFont);
    TextSize(txSize);
    TextFace(txFace);
    RGBForeColor(&origColor);
    SetGWorld(saveWorld, saveDevice);
}

/*
 *-------------------------------------------------------------------------
 *
 * MultiFontDrawText --
 *
 *        Helper function for Tk_DrawChars.  Draws characters, using the 
 *        various screen fonts in fontPtr to draw multilingual characters.
 *        Note: No bidirectional support.
 *
 * Results:
 *        None.
 *
 * Side effects:
 *        Information gets drawn on the screen.  
 *        Contents of fontPtr may be modified if more subfonts were loaded 
 *        in order to draw all the multilingual characters in the given 
 *        string.
 *
 *-------------------------------------------------------------------------
 */

static void
MultiFontDrawText(
    MacFont *fontPtr,           /* Contains set of fonts to use when drawing
                                 * following string. */
    CONST char *source,         /* Potentially multilingual UTF-8 string. */
    int numBytes,               /* Length of string in bytes. */
    int x, int y)               /* Coordinates at which to place origin *
                                 * of string when drawing. */
{
    if (numBytes > 0) {
	Tcl_DString runString;
	Tcl_DStringInit(&runString);
	Tcl_UtfToUniCharDString(source, numBytes, &runString);
        ATSUTextLayout mLayout;
        ATSUCreateTextLayout(&mLayout);
        ATSUSetTextPointerLocation(mLayout, Tcl_DStringValue(&runString), kATSUFromTextBeginning, kATSUToTextEnd, Tcl_DStringLength(&runString)/2);
        ATSUSetRunStyle(mLayout, fontPtr->atsuStyle, kATSUFromTextBeginning, kATSUToTextEnd);
        ATSUSetTransientFontMatching(mLayout, true);
        ATSUSetLayoutControls(mLayout, 1, lTags, lSizes, lValues);
        Fixed fx = Int2Fixed(x);
        Fixed fy = Int2Fixed(y);
        ATSUDrawText(mLayout, kATSUFromTextBeginning, kATSUToTextEnd, fx, fy);
        ATSUDisposeTextLayout(mLayout);
        Tcl_DStringFree(&runString);
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * TkMacOSXIsCharacterMissing --
 *
 *        Given a tkFont and a character determines whether the character has
 *        a glyph defined in the font or not. Note that this is potentially
 *        not compatible with Mac OS 8 as it looks at the font handle
 *        structure directly. Looks into the character array of the font
 *        handle to determine whether the glyph is defined 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. */
{
    /* Of course not, we'll find it via fallbacks anyway! :-) */
    return 0;
}

/*
 *---------------------------------------------------------------------------
 *
 * 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(
    Tk_Window tkwin,            /* For display where font will be used. */
    int faceNum,                /* Macintosh font number. */
    int size,                   /* Point size for Macintosh font. */
    int style,                  /* Macintosh style bits. */
    MacFont *fontPtr)           /* Filled with information constructed from
                                 * the above arguments. */
{
    Str255 nativeName;
    FontInfo fi;
    TkFontAttributes *faPtr;
    TkFontMetrics *fmPtr;
    CGrafPtr saveWorld;
    GDHandle saveDevice;
    short pixels;

    if (size == 0) {
            size = -GetDefFontSize();
    }
    pixels = (short) TkFontGetPixels(tkwin, size);
    
    GetGWorld(&saveWorld, &saveDevice);
    SetGWorld(gWorld, NULL);
    TextFont(faceNum);
    
    
    TextSize(pixels);
    TextFace(style);

    GetFontInfo(&fi);
    FMGetFontFamilyName(faceNum, nativeName);
    fontPtr->font.fid        = (Font) fontPtr;
 
    faPtr = &fontPtr->font.fa;
    faPtr->family = GetUtfFaceName(nativeName);
    faPtr->size = TkFontGetPoints(tkwin, size);
    faPtr->weight = (style & bold) ? TK_FW_BOLD : TK_FW_NORMAL;
    faPtr->slant = (style & italic) ? TK_FS_ITALIC : TK_FS_ROMAN;
    faPtr->underline = ((style & underline) != 0);
    faPtr->overstrike = 0;
 
    fmPtr = &fontPtr->font.fm;
    fmPtr->ascent = fi.ascent;        
    fmPtr->descent = fi.descent;        
    fmPtr->maxWidth = fi.widMax;
    fmPtr->fixed = (CharWidth('i') == CharWidth('w'));

    ATSUFontID fontId;
    FMFontStyle fontStyle;
    FMGetFontFromFontFamilyInstance(faceNum, style, &fontId, &fontStyle);
    Fixed sizeFixed = Int2Fixed((int)pixels);
    ATSUCreateStyle(&fontPtr->atsuStyle);
    ATSUAttributeTag tags[2] = { kATSUSizeTag, kATSUFontTag };
    ByteCount sizes[2] = { sizeof(Fixed), sizeof(ATSUFontID) };
    ATSUAttributeValuePtr values[2] = { &sizeFixed, &fontId };
    ATSUSetAttributes(fontPtr->atsuStyle, 2, tags, sizes, values);

    /* Needed to set up MacOS X controls with this font */
    fontPtr->size = pixels;
    fontPtr->style = (short) style;
    fontPtr->faceNum = faceNum;

    SetGWorld(saveWorld, saveDevice);
}

/*
 *-------------------------------------------------------------------------
 *
 * GetFamilyNum --
 *
 *        Determines 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.
 *
 * Results:
 *        The return value is 0 if the specified font family does not exist,
 *        non-zero otherwise.  *faceNumPtr is filled with the unique face
 *        number that identifies the screen font, or 0 if the font family
 *        did not exist.
 *
 * Side effects:
 *        None.
 *
 *-------------------------------------------------------------------------
 */

static int
GetFamilyNum(
    CONST char *faceName,         /* UTF-8 name of font family to query. */
    short *faceNumPtr)            /* Filled with font number for above family. */
{
    FontNameMap *mapPtr;
    
    if (faceName != NULL) {
        for (mapPtr = gFontNameMap; mapPtr->utfName != NULL; mapPtr++) {
            if (strcasecmp(faceName, mapPtr->utfName) == 0) {
                *faceNumPtr = mapPtr->faceNum;
                return 1;
            }
        }
    }
    *faceNumPtr = 0;    
    return 0;
}

static int
GetFamilyOrAliasNum(
    CONST char *faceName,         /* UTF-8 name of font family to query. */
    short *faceNumPtr)            /* Filled with font number for above family. */
{
    char **aliases;
    int i;
    
    if (GetFamilyNum(faceName, faceNumPtr) != 0) {
        return 1;
    }
    aliases = TkFontGetAliasList(faceName);
    if (aliases != NULL) {
        for (i = 0; aliases[i] != NULL; i++) {
            if (GetFamilyNum(aliases[i], faceNumPtr) != 0) {
                return 1;
            }
        }
    }
    return 0;
}

/*
 *-------------------------------------------------------------------------
 *
 * GetUtfFaceName --
 *
 *        Given the native name for a Macintosh font (in which the name of
 *        the font is in the encoding of the font itself), return the UTF-8
 *        name that corresponds to that font.  The specified font name must
 *        refer to a font that actually exists on the machine.  
 *
 *        This function is used to obtain the UTF-8 name when querying the
 *        properties of a Macintosh font object.
 *
 * Results:
 *        The return value is a pointer to the UTF-8 of the specified font.
 *
 * Side effects:
 *        None.
 *
 *------------------------------------------------------------------------
 */
 
static Tk_Uid
GetUtfFaceName(
    StringPtr nativeName)        /* Pascal name for font in native encoding. */
{
    FontNameMap *mapPtr;
    
    for (mapPtr = gFontNameMap; mapPtr->utfName != NULL; mapPtr++) {
        if (pstrcmp(nativeName, mapPtr->nativeName) == 0) {
            return mapPtr->utfName;
        }
    }
    Tcl_Panic("GetUtfFaceName: unexpected nativeName");
    return NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * TkMacOSXInitControlFontStyle --
 *
 *	This procedure sets up the appropriate ControlFontStyleRec
 *	for a Mac control.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
TkMacOSXInitControlFontStyle(Tk_Font tkfont, ControlFontStylePtr fsPtr)
{
    MacFont    *fontPtr;
    fontPtr = (MacFont *) tkfont;
    fsPtr->flags =
        kControlUseFontMask|
        kControlUseSizeMask|
        kControlUseFaceMask|
        kControlUseJustMask;
    fsPtr->font = fontPtr->faceNum;
    fsPtr->size = fontPtr->size;
    fsPtr->style = fontPtr->style;
    fsPtr->just = teCenter;
}

/*
 *----------------------------------------------------------------------
 *
 * TkMacOSXUseAntialiasedText --
 *
 *      Enables or disables application-wide use of quickdraw 
 *      antialiased text (where available).
 *      Sets up a linked tcl global boolean variable with write trace
 *      to allow disabling of antialiased text from tcl.
 *
 * Results:
 *      TCL_OK if facility was sucessfully enabled/disabled.
 *      TCL_ERROR if an error occurred or if facility is not available.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

/* define constants from 10.2 Quickdraw.h to enable compilation in 10.1 */
#define kQDUseTrueTypeScalerGlyphs      (1 << 0)
#define kQDUseCGTextRendering           (1 << 1)
#define kQDUseCGTextMetrics             (1 << 2)

static int TkMacOSXAntialiasedTextEnabled = FALSE;

static char *
TkMacOSXAntialiasedTextVariableProc(clientData, interp, name1, name2, flags)
    ClientData clientData;
    Tcl_Interp *interp;
    CONST char *name1;
    CONST char *name2;
    int flags;
{
    TkMacOSXUseAntialiasedText(interp, TkMacOSXAntialiasedTextEnabled);
    return (char *) NULL;
}

int
TkMacOSXUseAntialiasedText(interp, enable)
        Tcl_Interp *interp;
        int enable;
{
    static Boolean initialized = FALSE;
    static UInt32 (*swaptextflags)(UInt32) = NULL;
    
    if(!initialized) {
        swaptextflags = TkMacOSXGetNamedSymbol("QD", "_QDSwapTextFlags");
        if (!swaptextflags) {
            swaptextflags = TkMacOSXGetNamedSymbol("QD", "_SwapQDTextFlags");
        }
        initialized = TRUE;

        TkMacOSXAntialiasedTextEnabled = (swaptextflags ? enable : FALSE);
        if (Tcl_CreateNamespace(interp, "::tk::mac", NULL, NULL) == NULL) {
            Tcl_ResetResult(interp);
        }
        if (Tcl_TraceVar(interp, "::tk::mac::antialiasedtext", 
                TCL_GLOBAL_ONLY | TCL_TRACE_WRITES,
                TkMacOSXAntialiasedTextVariableProc, NULL) != TCL_OK) {
            Tcl_ResetResult(interp);
        }
        if (Tcl_LinkVar(interp, "::tk::mac::antialiasedtext",
                (char *) &TkMacOSXAntialiasedTextEnabled, 
                TCL_LINK_BOOLEAN) != TCL_OK) {
            Tcl_ResetResult(interp);
        }
    }
    if (swaptextflags) {
        swaptextflags(enable ? kQDUseCGTextRendering | kQDUseCGTextMetrics 
                : kQDUseTrueTypeScalerGlyphs);
        TkMacOSXAntialiasedTextEnabled = enable;
        return TCL_OK;
    } else {
        TkMacOSXAntialiasedTextEnabled = FALSE;
        return TCL_ERROR;
    }
}
