/*
* tkMacFont.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.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* SCCS:@(#) tkMacFont.c 1.52 97/11/20 18:29:51
*/
#include <Windows.h>
#include <Strings.h>
#include <Fonts.h>
#include <Resources.h>
#include "tkMacInt.h"
#include "tkFont.h"
#include "tkPort.h"
/*
* The following structure represents the Macintosh's' implementation of a
* font.
*/
typedef struct MacFont {
TkFont font; /* Stuff used by generic font package. Must
* be first in structure. */
short family;
short size;
short style;
} MacFont;
static GWorldPtr gWorld = NULL;
static TkFont * AllocMacFont _ANSI_ARGS_((TkFont *tkfont,
Tk_Window tkwin, int family, int size, int style));
/*
*---------------------------------------------------------------------------
*
* 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. */
{
short family;
if (strcmp(name, "system") == 0) {
family = GetSysFont();
} else if (strcmp(name, "application") == 0) {
family = GetAppFont();
} else {
return NULL;
}
return AllocMacFont(NULL, tkwin, family, 0, 0);
}
/*
*---------------------------------------------------------------------------
*
* 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. */
{
char buf[257];
size_t len;
short family, size, style;
if (faPtr->family == NULL) {
family = 0;
} else {
CONST char *familyName;
familyName = faPtr->family;
if (strcasecmp(familyName, "Times New Roman") == 0) {
familyName = "Times";
} else if (strcasecmp(familyName, "Courier New") == 0) {
familyName = "Courier";
} else if (strcasecmp(familyName, "Arial") == 0) {
familyName = "Helvetica";
}
len = strlen(familyName);
if (len > 255) {
len = 255;
}
buf[0] = (char) len;
memcpy(buf + 1, familyName, len);
buf[len + 1] = '\0';
GetFNum((StringPtr) buf, &family);
}
size = faPtr->pointsize;
if (size <= 0) {
size = GetDefFontSize();
}
style = 0;
if (faPtr->weight != TK_FW_NORMAL) {
style |= bold;
}
if (faPtr->slant != TK_FS_ROMAN) {
style |= italic;
}
if (faPtr->underline) {
style |= underline;
}
return AllocMacFont(tkFontPtr, tkwin, family, size, style);
}
/*
*---------------------------------------------------------------------------
*
* 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. */
{
ckfree((char *) tkFontPtr);
}
/*
*---------------------------------------------------------------------------
*
* TkpGetFontFamilies --
*
* Return information about the font families that are available
* on the display of the given window.
*
* Results:
* interp->result is modified 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. */
{
MenuHandle fontMenu;
int i;
char itemText[257];
fontMenu = NewMenu(1, "\px");
AddResMenu(fontMenu, 'FONT');
for (i = 1; i < CountMItems(fontMenu); i++) {
/*
* Each item is a pascal string. Convert it to C and append.
*/
GetMenuItemText(fontMenu, i, (unsigned char *) itemText);
itemText[itemText[0] + 1] = '\0';
Tcl_AppendElement(interp, &itemText[1]);
}
DisposeMenu(fontMenu);
}
/*
*---------------------------------------------------------------------------
*
* TkMacIsCharacterMissing --
*
* 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
TkMacIsCharacterMissing(
Tk_Font tkfont, /* The font we are looking in. */
unsigned int searchChar) /* The character we are looking for. */
{
MacFont *fontPtr = (MacFont *) tkfont;
FMInput fm;
FontRec **fontRecHandle;
fm.family = fontPtr->family;
fm.size = fontPtr->size;
fm.face = fontPtr->style;
fm.needBits = 0;
fm.device = 0;
fm.numer.h = fm.numer.v = fm.denom.h = fm.denom.v = 1;
/*
* This element of the FMOutput structure was changed between the 2.0 & 3.0
* versions of the Universal Headers.
*/
#if !defined(UNIVERSAL_INTERFACES_VERSION) || (UNIVERSAL_INTERFACES_VERSION < 0x0300)
fontRecHandle = (FontRec **) FMSwapFont(&fm)->fontResult;
#else
fontRecHandle = (FontRec **) FMSwapFont(&fm)->fontHandle;
#endif
return *(short *) ((long) &(*fontRecHandle)->owTLoc
+ ((long)((*fontRecHandle)->owTLoc + searchChar
- (*fontRecHandle)->firstChar) * sizeof(short))) == -1;
}
/*
*---------------------------------------------------------------------------
*
* 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 characters 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
Tk_MeasureChars(
Tk_Font tkfont, /* Font in which characters will be drawn. */
CONST char *source, /* Characters to be displayed. Need not be
* '\0' terminated. */
int numChars, /* Maximum number of characters 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. */
{
short staticWidths[128];
short *widths;
CONST char *p, *term;
int curX, termX, curIdx, sawNonSpace;
MacFont *fontPtr;
CGrafPtr saveWorld;
GDHandle saveDevice;
if (numChars == 0) {
*lengthPtr = 0;
return 0;
}
if (gWorld == NULL) {
Rect rect = {0, 0, 1, 1};
if (NewGWorld(&gWorld, 0, &rect, NULL, NULL, 0) != noErr) {
panic("NewGWorld failed in Tk_MeasureChars");
}
}
GetGWorld(&saveWorld, &saveDevice);
SetGWorld(gWorld, NULL);
fontPtr = (MacFont *) tkfont;
TextFont(fontPtr->family);
TextSize(fontPtr->size);
TextFace(fontPtr->style);
if (maxLength <= 0) {
*lengthPtr = TextWidth(source, 0, numChars);
SetGWorld(saveWorld, saveDevice);
return numChars;
}
if (numChars > maxLength) {
/*
* Assume that all chars are at least 1 pixel wide, so there's no
* need to measure more characters than there are pixels. This
* assumption could be refined to an iterative approach that would
* use that as a starting point and try more chars if necessary (if
* there actually were some zero-width chars).
*/
numChars = maxLength;
}
if (numChars > SHRT_MAX) {
/*
* If they are trying to measure more than 32767 chars at one time,
* it would require several separate measurements.
*/
numChars = SHRT_MAX;
}
widths = staticWidths;
if (numChars >= sizeof(staticWidths) / sizeof(staticWidths[0])) {
widths = (short *) ckalloc((numChars + 1) * sizeof(short));
}
MeasureText((short) numChars, source, widths);
if (widths[numChars] <= maxLength) {
curX = widths[numChars];
curIdx = numChars;
} else {
p = term = source;
curX = termX = 0;
sawNonSpace = !isspace(UCHAR(*p));
for (curIdx = 1; ; curIdx++) {
if (isspace(UCHAR(*p))) {
if (sawNonSpace) {
term = p;
termX = widths[curIdx - 1];
sawNonSpace = 0;
}
} else {
sawNonSpace = 1;
}
if (widths[curIdx] > maxLength) {
curIdx--;
curX = widths[curIdx];
break;
}
p++;
}
if (flags & TK_PARTIAL_OK) {
curIdx++;
curX = widths[curIdx];
}
if ((curIdx == 0) && (flags & TK_AT_LEAST_ONE)) {
/*
* The space was too small to hold even one character. Since at
* least one character must always fit on a line, return the width
* of the first character.
*/
curX = TextWidth(source, 0, 1);
curIdx = 1;
} else if (flags & TK_WHOLE_WORDS) {
/*
* Break at last word that fits on the line.
*/
if ((flags & TK_AT_LEAST_ONE) && (term == source)) {
/*
* The space was too small to hold an entire word. This
* is the only word on the line, so just return the part of th
* word that fit.
*/
;
} else {
curIdx = term - source;
curX = termX;
}
}
}
if (widths != staticWidths) {
ckfree((char *) widths);
}
*lengthPtr = curX;
SetGWorld(saveWorld, saveDevice);
return curIdx;
}
/*
*---------------------------------------------------------------------------
*
* 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, /* Characters 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 numChars, /* Number of characters 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;
fontPtr = (MacFont *) tkfont;
macWin = (MacDrawable *) drawable;
destPort = TkMacGetDrawablePort(drawable);
GetGWorld(&saveWorld, &saveDevice);
SetGWorld(destPort, NULL);
TkMacSetUpClippingRgn(drawable);
TkMacSetUpGraphicsPort(gc);
txFont = tcl_macQdPtr->thePort->txFont;
txFace = tcl_macQdPtr->thePort->txFace;
txSize = tcl_macQdPtr->thePort->txSize;
GetForeColor(&origColor);
if ((gc->fill_style == FillStippled
|| gc->fill_style == FillOpaqueStippled)
&& gc->stipple != None) {
Pixmap pixmap;
GWorldPtr bufferPort;
stippleMap = TkMacMakeStippleMap(drawable, gc->stipple);
pixmap = Tk_GetPixmap(display, drawable,
stippleMap->bounds.right, stippleMap->bounds.bottom, 0);
bufferPort = TkMacGetDrawablePort(pixmap);
SetGWorld(bufferPort, NULL);
TextFont(fontPtr->family);
TextSize(fontPtr->size);
TextFace(fontPtr->style);
if (TkSetMacColor(gc->foreground, &macColor) == true) {
RGBForeColor(&macColor);
}
ShowPen();
MoveTo((short) 0, (short) 0);
FillRect(&stippleMap->bounds, &tcl_macQdPtr->white);
MoveTo((short) x, (short) y);
DrawText(source, 0, (short) numChars);
SetGWorld(destPort, NULL);
CopyDeepMask(&((GrafPtr) bufferPort)->portBits, stippleMap,
&((GrafPtr) destPort)->portBits, &stippleMap->bounds,
&stippleMap->bounds, &((GrafPtr) destPort)->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 {
TextFont(fontPtr->family);
TextSize(fontPtr->size);
TextFace(fontPtr->style);
if (TkSetMacColor(gc->foreground, &macColor) == true) {
RGBForeColor(&macColor);
}
ShowPen();
MoveTo((short) (macWin->xOff + x), (short) (macWin->yOff + y));
DrawText(source, 0, (short) numChars);
}
TextFont(txFont);
TextSize(txSize);
TextFace(txFace);
RGBForeColor(&origColor);
SetGWorld(saveWorld, saveDevice);
}
/*
*---------------------------------------------------------------------------
*
* AllocMacFont --
*
* Helper for TkpGetNativeFont() and TkpGetFontFromAttributes().
* Allocates and intializes the memory for a new TkFont that
* wraps the platform-specific data.
*
* Results:
* Returns pointer to newly constructed TkFont.
*
* 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().
*
* Side effects:
* Memory allocated.
*
*---------------------------------------------------------------------------
*/
static TkFont *
AllocMacFont(
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. */
int family, /* Macintosh font family. */
int size, /* Point size for Macintosh font. */
int style) /* Macintosh style bits. */
{
char buf[257];
FontInfo fi;
MacFont *fontPtr;
TkFontAttributes *faPtr;
TkFontMetrics *fmPtr;
CGrafPtr saveWorld;
GDHandle saveDevice;
if (gWorld == NULL) {
Rect rect = {0, 0, 1, 1};
if (NewGWorld(&gWorld, 0, &rect, NULL, NULL, 0) != noErr) {
panic("NewGWorld failed in AllocMacFont");
}
}
GetGWorld(&saveWorld, &saveDevice);
SetGWorld(gWorld, NULL);
if (tkFontPtr == NULL) {
fontPtr = (MacFont *) ckalloc(sizeof(MacFont));
} else {
fontPtr = (MacFont *) tkFontPtr;
}
fontPtr->font.fid = (Font) fontPtr;
faPtr = &fontPtr->font.fa;
GetFontName(family, (StringPtr) buf);
buf[UCHAR(buf[0]) + 1] = '\0';
faPtr->family = Tk_GetUid(buf + 1);
faPtr->pointsize = 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;
TextFont(family);
TextSize(size);
TextFace(style);
GetFontInfo(&fi);
fmPtr->ascent = fi.ascent;
fmPtr->descent = fi.descent;
fmPtr->maxWidth = fi.widMax;
fmPtr->fixed = (CharWidth('i') == CharWidth('w'));
fontPtr->family = (short) family;
fontPtr->size = (short) size;
fontPtr->style = (short) style;
SetGWorld(saveWorld, saveDevice);
return (TkFont *) fontPtr;
}