From 6b3ea13890d4e66f7983aaafcc40c5171dac8c66 Mon Sep 17 00:00:00 2001 From: das Date: Wed, 22 Mar 2006 00:21:15 +0000 Subject: * generic/tkFont.c: implementation of ATSUI text rendering * generic/tkInt.h: in TkAqua provided by Benjamin * generic/tkTextDisp.c: Riefenstahl. [Patch 638966] * library/demos/unicodeout.tcl: * macosx/tkMacOSXFont.h (new file): * macosx/tkMacOSXFont.c: * tests/font.test: * unix/tkUnixFont.c: * win/tkWinFont.c: * generic/tkFont.c: moved MODULE_SCOPE declarations of * generic/tkFont.h: font helper procs into header files. * macosx/tkMacOSXButton.c: * macosx/tkMacOSXFont.h: * macosx/tkMacOSXMenubutton.c: * macosx/Wish.xcode/project.pbxproj: add new tkMacOSXFont.h file, * macosx/Wish.xcodeproj/project.pbxproj: turn off dead code stripping as it interferes with -sectcreate (rdar://4486223). * macosx/Wish.xcode/default.pbxuser: add TCLLIBPATH=/Library/Tcl * macosx/Wish.xcodeproj/default.pbxuser: env var setting to tktest. * unix/configure.in: fix detection of symbols build when enabling TkAqua debug code; filter nm output of libtclstub better to avoid error on intel macs [Bug 1415789]. * unix/configure: autoconf-2.59 --- ChangeLog | 30 + generic/tkFont.c | 55 +- generic/tkFont.h | 4 +- generic/tkInt.h | 22 +- generic/tkTextDisp.c | 809 +++++++- library/demos/unicodeout.tcl | 30 +- macosx/Wish.xcode/default.pbxuser | 5 + macosx/Wish.xcode/project.pbxproj | 11 +- macosx/Wish.xcodeproj/default.pbxuser | 5 + macosx/Wish.xcodeproj/project.pbxproj | 6 +- macosx/tkMacOSXButton.c | 6 +- macosx/tkMacOSXFont.c | 3633 ++++++++++++++++++--------------- macosx/tkMacOSXFont.h | 113 + macosx/tkMacOSXMenubutton.c | 6 +- tests/font.test | 12 +- unix/configure | 6 +- unix/configure.in | 6 +- unix/tkUnixFont.c | 107 +- win/tkWinFont.c | 104 +- 19 files changed, 3279 insertions(+), 1691 deletions(-) create mode 100644 macosx/tkMacOSXFont.h diff --git a/ChangeLog b/ChangeLog index 7bb4991..b7c0763 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2006-03-21 Daniel Steffen + + * generic/tkFont.c: implementation of ATSUI text rendering + * generic/tkInt.h: in TkAqua provided by Benjamin + * generic/tkTextDisp.c: Riefenstahl. [Patch 638966] + * library/demos/unicodeout.tcl: + * macosx/tkMacOSXFont.h (new file): + * macosx/tkMacOSXFont.c: + * tests/font.test: + * unix/tkUnixFont.c: + * win/tkWinFont.c: + + * generic/tkFont.c: moved MODULE_SCOPE declarations of + * generic/tkFont.h: font helper procs into header files. + * macosx/tkMacOSXButton.c: + * macosx/tkMacOSXFont.h: + * macosx/tkMacOSXMenubutton.c: + + * macosx/Wish.xcode/project.pbxproj: add new tkMacOSXFont.h file, + * macosx/Wish.xcodeproj/project.pbxproj: turn off dead code stripping + as it interferes with -sectcreate (rdar://4486223). + + * macosx/Wish.xcode/default.pbxuser: add TCLLIBPATH=/Library/Tcl + * macosx/Wish.xcodeproj/default.pbxuser: env var setting to tktest. + + * unix/configure.in: fix detection of symbols build when enabling + TkAqua debug code; filter nm output of libtclstub better to avoid + error on intel macs [Bug 1415789]. + * unix/configure: autoconf-2.59 + 2006-03-20 Don Porter * generic/tkConsole.c: Added exit handler to clean up the interp diff --git a/generic/tkFont.c b/generic/tkFont.c index 5166e04..3d64409 100644 --- a/generic/tkFont.c +++ b/generic/tkFont.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkFont.c,v 1.27 2005/11/27 02:36:13 das Exp $ + * RCS: @(#) $Id: tkFont.c,v 1.28 2006/03/22 00:21:16 das Exp $ */ #include "tkPort.h" @@ -343,8 +343,6 @@ static int SetFontFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); static void TheWorldHasChanged(ClientData clientData); static void UpdateDependentFonts(TkFontInfo *fiPtr, Tk_Window tkwin, Tcl_HashEntry *namedHashPtr); -MODULE_SCOPE int TkFontGetFirstTextLayout(Tk_TextLayout layout, - Tk_Font *font, char *dst); /* * The following structure defines the implementation of the "font" Tcl @@ -1721,14 +1719,15 @@ Tk_TextWidth( /* *--------------------------------------------------------------------------- * - * Tk_UnderlineChars -- + * Tk_UnderlineChars, TkUnderlineCharsInContext -- * - * This function draws an underline for a given range of characters in a - * given string. It doesn't draw the characters (which are assumed to - * have been displayed previously); it just draws the underline. This - * function would mainly be used to quickly underline a few characters - * without having to construct an underlined font. To produce properly - * underlined text, the appropriate underlined font should be constructed + * These procedures draw an underline for a given range of + * characters in a given string. They don't draw the characters + * (which are assumed to have been displayed previously); they + * just draw the underline. These procedures would mainly be + * used to quickly underline a few characters without having to + * construct an underlined font. To produce properly underlined + * text, the appropriate underlined font should be constructed * and used. * * Results: @@ -1758,12 +1757,42 @@ Tk_UnderlineChars( * character. */ { TkFont *fontPtr; - int startX, endX; fontPtr = (TkFont *) tkfont; - Tk_MeasureChars(tkfont, string, firstByte, -1, 0, &startX); - Tk_MeasureChars(tkfont, string, lastByte, -1, 0, &endX); + TkUnderlineCharsInContext(display, drawable, gc, tkfont, string, + lastByte, x, y, firstByte, lastByte); +} + +void +TkUnderlineCharsInContext(display, drawable, gc, tkfont, string, numBytes, + x, y, firstByte, lastByte) + + Display *display; /* Display on which to draw. */ + Drawable drawable; /* Window or pixmap in which to draw. */ + GC gc; /* Graphics context for actually drawing + * line. */ + Tk_Font tkfont; /* Font used in GC; must have been allocated + * by Tk_GetFont(). Used for character + * dimensions, etc. */ + CONST char *string; /* String containing characters to be + * underlined or overstruck. */ + int numBytes; /* Number of bytes in string. */ + int x, y; /* Coordinates at which the first character + * of the whole string would be drawn. */ + int firstByte; /* Index of first byte of first character. */ + int lastByte; /* Index of first byte after the last + * character. */ +{ + TkFont *fontPtr; + int startX, endX; + + fontPtr = (TkFont *) tkfont; + + TkpMeasureCharsInContext(tkfont, string, numBytes, 0, firstByte, -1, 0, + &startX); + TkpMeasureCharsInContext(tkfont, string, numBytes, 0, lastByte, -1, 0, + &endX); XFillRectangle(display, drawable, gc, x + startX, y + fontPtr->underlinePos, (unsigned int) (endX - startX), diff --git a/generic/tkFont.h b/generic/tkFont.h index 3b6489a..cc362fb 100644 --- a/generic/tkFont.h +++ b/generic/tkFont.h @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkFont.h,v 1.7 2005/11/27 02:36:13 das Exp $ + * RCS: @(#) $Id: tkFont.h,v 1.8 2006/03/22 00:21:16 das Exp $ */ #ifndef _TKFONT @@ -203,6 +203,8 @@ MODULE_SCOPE int TkFontGetPixels(Tk_Window tkwin, int size); MODULE_SCOPE int TkFontGetPoints(Tk_Window tkwin, int size); MODULE_SCOPE char ** TkFontGetGlobalClass(void); MODULE_SCOPE char ** TkFontGetSymbolClass(void); +MODULE_SCOPE int TkFontGetFirstTextLayout(Tk_TextLayout layout, + Tk_Font *font, char *dst); /* * Low-level API exported by platform-specific code to generic code. diff --git a/generic/tkInt.h b/generic/tkInt.h index 0b4ca96..21808a0 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: $Id: tkInt.h,v 1.70 2006/03/16 17:32:28 dgp Exp $ + * RCS: $Id: tkInt.h,v 1.71 2006/03/22 00:21:16 das Exp $ */ #ifndef _TKINT @@ -881,6 +881,14 @@ extern TkDisplay *tkDisplayList; #define TK_GRAB_ANCESTOR 2 #define TK_GRAB_EXCLUDED 3 +/* + * An additional flag for TkpMeasureCharsInContext(). Coordinate with + * the other flags for this routine, but don't make public until + * TkpMeasureCharsInContext() is made public, too. + */ + +#define TK_ISOLATE_END 32 + /* * The macro below is used to modify a "char" value (e.g. by casting it to an * unsigned character) so that it can be used safely with macros such as @@ -1156,6 +1164,18 @@ MODULE_SCOPE void TkPrintPadAmount(Tcl_Interp *interp, MODULE_SCOPE int TkParsePadAmount(Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj *objPtr, int *pad1Ptr, int *pad2Ptr); +MODULE_SCOPE void TkpDrawCharsInContext(Display * display, + Drawable drawable, GC gc, Tk_Font tkfont, + CONST char * source, int numBytes, int rangeStart, + int rangeLength, int x, int y); +MODULE_SCOPE int TkpMeasureCharsInContext(Tk_Font tkfont, + CONST char * source, int numBytes, int rangeStart, + int rangeLength, int maxLength, int flags, + int * lengthPtr); +MODULE_SCOPE void TkUnderlineCharsInContext(Display *display, + Drawable drawable, GC gc, Tk_Font tkfont, + CONST char *string, int numBytes, int x, int y, + int firstByte, int lastByte); /* * Unsupported commands. diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c index 13c6b24..d14a000 100644 --- a/generic/tkTextDisp.c +++ b/generic/tkTextDisp.c @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkTextDisp.c,v 1.55 2005/11/27 02:36:14 das Exp $ + * RCS: @(#) $Id: tkTextDisp.c,v 1.56 2006/03/22 00:21:17 das Exp $ */ #include "tkPort.h" @@ -68,6 +68,64 @@ */ /* + * TK_LAYOUT_WITH_BASE_CHUNKS: + * + * With this macro set, collect all char chunks that have no holes between + * them, that are on the same line and use the same font and font size. + * Allocate the chars of all these chunks, the so-called "stretch", in a + * DString in the first chunk, the so-called "base chunk". Use the base + * chunk string for measuring and drawing, so that these actions are always + * performed with maximum context. + * + * This is necessary for text rendering engines that provide ligatures and + * sub-pixel layout, like ATSU on Mac. If we don't do this, the measuring + * will change all the time, leading to an ugly "tremble and shiver" + * effect. This is because of the continuous splitting and re-merging of + * chunks that goes on in a text widget, when the cursor or the selection + * move. + * + * Side effects: + * + * Memory management changes. Instead of attaching the character data to + * the clientData structures of the char chunks, an additional DString is + * used. The collection process will even lead to resizing this DString + * for large stretches (> TCL_DSTRING_STATIC_SIZE == 200). We could reduce + * the overall memory footprint by copying the result to a plain char array + * after the line breaking process, but that would complicate the code and + * make performance even worse speedwise. See also TODOs. + * + * TODOs: + * + * - Move the character collection process from the LayoutProc into + * LayoutDLine(), so that the collection can be done before actual + * layout. In this way measuring can look at the following text, too, + * right from the beginning. Memory handling can also be improved with + * this. Problem: We don't easily know which chunks are adjacent until + * all the other chunks have calculated their width. Apparently marks + * would return width==0. A separate char collection loop would have to + * know these things. + * + * - Use a new context parameter to pass the context from LayoutDLine() to + * the LayoutProc instead of using a global variable like now. Not + * pressing until the previous point gets implemented. + */ + +#ifdef MAC_OSX_TK +#define TK_LAYOUT_WITH_BASE_CHUNKS 1 +#define TK_DRAW_IN_CONTEXT 1 +#endif + +#if TK_LAYOUT_WITH_BASE_CHUNKS && !TK_DRAW_IN_CONTEXT + +#ifdef MAC_OSX_TK +#define TextStyle MacTextStyle +#include "tkMacOSXInt.h" /* TkSetMacColor() */ +#undef TextStyle +#endif + +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ + +/* * The following structure describes how to display a range of characters. * The information is generated by scanning all of the tags associated with * the characters and combining that with default information for the overall @@ -365,6 +423,8 @@ typedef struct TextDInfo { * points to one of the following structures: */ +#if !TK_LAYOUT_WITH_BASE_CHUNKS + typedef struct CharInfo { int numBytes; /* Number of bytes to display. */ char chars[4]; /* UTF characters to display. Actual size will @@ -372,6 +432,37 @@ typedef struct CharInfo { * FIELD IN THE STRUCTURE. */ } CharInfo; +#else /* TK_LAYOUT_WITH_BASE_CHUNKS */ + +typedef struct CharInfo { + TkTextDispChunk *baseChunkPtr; + int baseOffset; /* Starting offset in base chunk + * baseChars. */ + int numBytes; /* Number of bytes that belong to this + * chunk. */ + const char *chars; /* UTF characters to display. Actually + * points into the baseChars of the base + * chunk. Only valid after + * FinalizeBaseChunk(). */ +} CharInfo; + +/* + * The BaseCharInfo is a CharInfo with some additional data added. + */ + +typedef struct BaseCharInfo { + CharInfo ci; + Tcl_DString baseChars; /* Actual characters for the stretch of text + * represented by this base chunk. */ + int width; /* Width in pixels of the whole string, if + * known, else -1. Valid during + * LayoutDLine(). */ +} BaseCharInfo; + +static TkTextDispChunk *baseCharChunkPtr = NULL; + +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ + /* * Flag values for TextDInfo structures: * @@ -432,6 +523,11 @@ static void CharBboxProc(TkText *textPtr, int index, int y, int lineHeight, int baseline, int *xPtr, int *yPtr, int *widthPtr, int *heightPtr); +static int CharChunkMeasureChars( + TkTextDispChunk *chunkPtr, + const char *chars, int charsLen, + int start, int end, int startX, int maxX, + int flags, int *nextX); static void CharDisplayProc(TkText *textPtr, TkTextDispChunk *chunkPtr, int x, int y, int height, int baseline, @@ -440,6 +536,20 @@ static int CharMeasureProc(TkTextDispChunk *chunkPtr, int x); static void CharUndisplayProc(TkText *textPtr, TkTextDispChunk *chunkPtr); + +#if TK_LAYOUT_WITH_BASE_CHUNKS + +static void FinalizeBaseChunk( + TkTextDispChunk *additionalChunkPtr); +static void FreeBaseChunk( + TkTextDispChunk *baseChunkPtr); +static int IsSameFGStyle( + TextStyle *style1, TextStyle *style2); +static void RemoveFromBaseChunk( + TkTextDispChunk *chunkPtr); + +#endif + /* * Definitions of elided procs. Compiler can't inline these since we use * pointers to these functions. ElideDisplayProc, ElideUndisplayProc @@ -471,7 +581,8 @@ static int GetYPixelCount(TkText *textPtr, DLine *dlPtr); static DLine * LayoutDLine(TkText *textPtr, CONST TkTextIndex *indexPtr); static int MeasureChars(Tk_Font tkfont, CONST char *source, - int maxBytes, int startX, int maxX, int *nextXPtr); + int maxBytes, int rangeStart, int rangeLength, + int startX, int maxX, int flags, int *nextXPtr); static void MeasureUp(TkText *textPtr, CONST TkTextIndex *srcPtr, int distance, TkTextIndex *dstPtr, int *overlap); @@ -1264,6 +1375,7 @@ LayoutDLine( if (chunkPtr == NULL) { chunkPtr = (TkTextDispChunk *) ckalloc(sizeof(TkTextDispChunk)); chunkPtr->nextPtr = NULL; + chunkPtr->clientData = NULL; } chunkPtr->stylePtr = GetStyle(textPtr, &curIndex); elide = chunkPtr->stylePtr->sValuePtr->elide; @@ -1312,15 +1424,16 @@ LayoutDLine( } } - /* - * See if there is a tab in the current chunk; if so, only layout - * characters up to (and including) the tab. - */ - gotTab = 0; maxBytes = segPtr->size - byteOffset; - if (!elide && justify == TK_JUSTIFY_LEFT) { - if (segPtr->typePtr == &tkTextCharType) { + if (segPtr->typePtr == &tkTextCharType) { + + /* + * See if there is a tab in the current chunk; if so, only layout + * characters up to (and including) the tab. + */ + + if (!elide && justify == TK_JUSTIFY_LEFT) { char *p; for (p = segPtr->body.chars + byteOffset; *p != 0; p++) { @@ -1331,6 +1444,19 @@ LayoutDLine( } } } + +#if TK_LAYOUT_WITH_BASE_CHUNKS + if (baseCharChunkPtr != NULL) { + int expectedX = + ((BaseCharInfo*) baseCharChunkPtr->clientData)->width + + baseCharChunkPtr->x; + + if ((expectedX != x) || !IsSameFGStyle( + baseCharChunkPtr->stylePtr, chunkPtr->stylePtr)) { + FinalizeBaseChunk(NULL); + } + } +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ } chunkPtr->x = x; if (elide /*&& maxBytes*/) { @@ -1460,6 +1586,9 @@ LayoutDLine( chunkPtr = NULL; } +#if TK_LAYOUT_WITH_BASE_CHUNKS + FinalizeBaseChunk(NULL); +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ if (noCharsYet) { dlPtr->spaceAbove = 0; dlPtr->spaceBelow = 0; @@ -1512,6 +1641,9 @@ LayoutDLine( (*segPtr->typePtr->layoutProc)(textPtr, &breakIndex, segPtr, byteOffset, maxX, breakByteOffset, 0, wrapMode, breakChunkPtr); +#if TK_LAYOUT_WITH_BASE_CHUNKS + FinalizeBaseChunk(NULL); +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ } lastChunkPtr = breakChunkPtr; wholeLine = 0; @@ -6900,6 +7032,12 @@ TkTextCharLayoutProc( char *p; TkTextSegment *nextPtr; Tk_FontMetrics fm; +#if TK_LAYOUT_WITH_BASE_CHUNKS + const char *line; + int lineOffset; + BaseCharInfo *bciPtr; + Tcl_DString *baseString; +#endif /* * Figure out how many characters will fit in the space we've got. Include @@ -6915,14 +7053,58 @@ TkTextCharLayoutProc( p = segPtr->body.chars + byteOffset; tkfont = chunkPtr->stylePtr->sValuePtr->tkfont; - bytesThatFit = - MeasureChars(tkfont, p, maxBytes, chunkPtr->x, maxX, &nextX); + +#if TK_LAYOUT_WITH_BASE_CHUNKS + if (baseCharChunkPtr == NULL) { + + baseCharChunkPtr = chunkPtr; + bciPtr = (BaseCharInfo*) ckalloc(sizeof(BaseCharInfo)); + baseString = &bciPtr->baseChars; + Tcl_DStringInit(baseString); + bciPtr->width = 0; + + ciPtr = &bciPtr->ci; + + } else { + + bciPtr = (BaseCharInfo*) baseCharChunkPtr->clientData; + ciPtr = (CharInfo*) ckalloc(sizeof(CharInfo)); + baseString = &bciPtr->baseChars; + + } + + lineOffset = Tcl_DStringLength(baseString); + line = Tcl_DStringAppend(baseString,p,maxBytes); + + chunkPtr->clientData = (ClientData) ciPtr; + ciPtr->baseChunkPtr = baseCharChunkPtr; + ciPtr->baseOffset = lineOffset; + ciPtr->chars = NULL; + ciPtr->numBytes = 0; + + bytesThatFit = CharChunkMeasureChars( + chunkPtr, line, lineOffset + maxBytes, lineOffset, -1, + chunkPtr->x, maxX, TK_ISOLATE_END, &nextX); +#else /* !TK_LAYOUT_WITH_BASE_CHUNKS */ + bytesThatFit = CharChunkMeasureChars( + chunkPtr, p, maxBytes, 0, -1, + chunkPtr->x, maxX, TK_ISOLATE_END, &nextX); +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ + if (bytesThatFit < maxBytes) { if ((bytesThatFit == 0) && noCharsYet) { Tcl_UniChar ch; - - bytesThatFit = MeasureChars(tkfont, p, Tcl_UtfToUniChar(p, &ch), - chunkPtr->x, -1, &nextX); + int chLen = Tcl_UtfToUniChar(p, &ch); + +#if TK_LAYOUT_WITH_BASE_CHUNKS + bytesThatFit = CharChunkMeasureChars( + chunkPtr, line, lineOffset + chLen, lineOffset, -1, + chunkPtr->x, -1, 0, &nextX); +#else /* !TK_LAYOUT_WITH_BASE_CHUNKS */ + bytesThatFit = CharChunkMeasureChars( + chunkPtr, p, chLen, 0, -1, + chunkPtr->x, -1, 0, &nextX); +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ } if ((nextX < maxX) && ((p[bytesThatFit] == ' ') || (p[bytesThatFit] == '\t'))) { @@ -6944,6 +7126,16 @@ TkTextCharLayoutProc( bytesThatFit++; } if (bytesThatFit == 0) { +#if TK_LAYOUT_WITH_BASE_CHUNKS + chunkPtr->clientData = NULL; + if (chunkPtr == baseCharChunkPtr) { + baseCharChunkPtr = NULL; + Tcl_DStringFree(baseString); + } else { + Tcl_DStringSetLength(baseString,lineOffset); + } + ckfree((char*)ciPtr); +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ return 0; } } @@ -6966,15 +7158,39 @@ TkTextCharLayoutProc( chunkPtr->minHeight = 0; chunkPtr->width = nextX - chunkPtr->x; chunkPtr->breakIndex = -1; - ciPtr = (CharInfo *) ckalloc((unsigned) - (sizeof(CharInfo) - 3 + bytesThatFit)); + +#if !TK_LAYOUT_WITH_BASE_CHUNKS + ciPtr = (CharInfo *) ckalloc( + bytesThatFit + Tk_Offset(CharInfo,chars) +1); chunkPtr->clientData = (ClientData) ciPtr; + memcpy(ciPtr->chars, p, bytesThatFit); +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ + ciPtr->numBytes = bytesThatFit; - strncpy(ciPtr->chars, p, (size_t) bytesThatFit); if (p[bytesThatFit - 1] == '\n') { ciPtr->numBytes--; } +#if TK_LAYOUT_WITH_BASE_CHUNKS + + /* + * Final update for the current base chunk data. + */ + + Tcl_DStringSetLength(baseString,lineOffset+ciPtr->numBytes); + bciPtr->width = nextX - baseCharChunkPtr->x; + + /* + * Finalize the base chunk if this chunk ends in a tab, which definitly + * breaks the context and needs to be handled on a higher level. + */ + + if (ciPtr->numBytes > 0 && p[ciPtr->numBytes - 1] == '\t') { + FinalizeBaseChunk(chunkPtr); + } + +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ + /* * Compute a break location. If we're in word wrap mode, a break can occur * after any space character, or at the end of the chunk if the next @@ -7007,6 +7223,106 @@ TkTextCharLayoutProc( } /* + *--------------------------------------------------------------------------- + * + * CharChunkMeasureChars -- + * + * Determine the number of characters from a char chunk that will fit in + * the given horizontal span. + * + * This is the same as MeasureChars (which see), but in the context of a + * char chunk, i.e. on a higher level of abstraction. Use this function + * whereever possible instead of plain MeasureChars, so that the right + * context is used automatically. + * + * Results: + * The return value is the number of bytes from the range of start to + * end in source that fit in the span given by startX and maxX. + * *nextXPtr is filled in with the x-coordinate at which the first + * character that didn't fit would be drawn, if it were to be drawn. + * + * Side effects: + * None. + *-------------------------------------------------------------- + */ + +static int +CharChunkMeasureChars( + TkTextDispChunk *chunkPtr, /* Chunk from which to measure. */ + const char *chars, /* Chars to use, instead of the chunk's own. + * Used by the layoutproc during chunk setup. + * All other callers use NULL. Not + * NUL-terminated. */ + int charsLen, /* Length of the "chars" parameter. */ + int start, int end, /* The range of chars to measure inside the + * chunk (or inside the additional chars). */ + int startX, /* Starting x coordinate where the measured + * span will begin. */ + int maxX, /* Maximum pixel width of the span. May be + * -1 for unlimited. */ + int flags, /* Flags to pass to MeasureChars. */ + int *nextXPtr) /* The function puts the newly calculated + * right border x-position of the span + * here. */ +{ + Tk_Font tkfont = chunkPtr->stylePtr->sValuePtr->tkfont; + CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData; + +#if !TK_LAYOUT_WITH_BASE_CHUNKS + if (chars == NULL) { + chars = ciPtr->chars; + charsLen = ciPtr->numBytes; + } + if (end == -1) { + end = charsLen; + } + + return MeasureChars(tkfont, chars, charsLen, start, end-start, + startX, maxX, flags, nextXPtr); +#else + { + int xDisplacement; + int fit, bstart = start, bend = end; + + if (chars == NULL) { + Tcl_DString *baseChars = + &((BaseCharInfo *)ciPtr->baseChunkPtr->clientData)->baseChars; + chars = Tcl_DStringValue(baseChars); + charsLen = Tcl_DStringLength(baseChars); + bstart += ciPtr->baseOffset; + if (bend == -1) { + bend = ciPtr->baseOffset + ciPtr->numBytes; + } else { + bend += ciPtr->baseOffset; + } + } else { + if (bend == -1) { + bend = charsLen; + } + } + + if (bstart == ciPtr->baseOffset) { + xDisplacement = startX - chunkPtr->x; + } else { + int widthUntilStart = 0; + MeasureChars(tkfont, chars, charsLen, 0, bstart, + 0, -1, 0, &widthUntilStart); + xDisplacement = startX - widthUntilStart - chunkPtr->x; + } + + fit = MeasureChars(tkfont, chars, charsLen, 0, bend, + ciPtr->baseChunkPtr->x + xDisplacement, maxX, flags, nextXPtr); + + if (fit < bstart) { + return 0; + } else { + return fit - bstart; + } + } +#endif +} + +/* *-------------------------------------------------------------- * * CharDisplayProc -- @@ -7040,9 +7356,13 @@ CharDisplayProc( * corresponds to y. */ { CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData; + const char *string; TextStyle *stylePtr; StyleValues *sValuePtr; - int offsetBytes, offsetX; + int numBytes, offsetBytes, offsetX; +#if TK_DRAW_IN_CONTEXT + BaseCharInfo *bciPtr; +#endif if ((x + chunkPtr->width) <= 0) { /* @@ -7052,6 +7372,32 @@ CharDisplayProc( return; } +#if TK_DRAW_IN_CONTEXT + + bciPtr = (BaseCharInfo*) ciPtr->baseChunkPtr->clientData; + numBytes = Tcl_DStringLength(&bciPtr->baseChars); + string = Tcl_DStringValue(&bciPtr->baseChars); + +#elif TK_LAYOUT_WITH_BASE_CHUNKS + + if (ciPtr->baseChunkPtr != chunkPtr) { + /* + * Without context drawing only base chunks display their foreground. + */ + + return; + } + + numBytes = Tcl_DStringLength(&((BaseCharInfo*) ciPtr)->baseChars); + string = ciPtr->chars; + +#else /* !TK_LAYOUT_WITH_BASE_CHUNKS */ + + numBytes = ciPtr->numBytes; + string = ciPtr->chars; + +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ + stylePtr = chunkPtr->stylePtr; sValuePtr = stylePtr->sValuePtr; @@ -7066,18 +7412,54 @@ CharDisplayProc( offsetX = x; offsetBytes = 0; if (x < 0) { - offsetBytes = MeasureChars(sValuePtr->tkfont, ciPtr->chars, - ciPtr->numBytes, x, 0, &offsetX); + offsetBytes = CharChunkMeasureChars(chunkPtr, NULL, 0, 0, -1, + x, 0, 0, &offsetX); } /* * Draw the text, underline, and overstrike for this chunk. */ - if (!sValuePtr->elide && (ciPtr->numBytes > offsetBytes) - && (stylePtr->fgGC != None)) { - int numBytes = ciPtr->numBytes - offsetBytes; - char *string = ciPtr->chars + offsetBytes; + if (!sValuePtr->elide && (numBytes > offsetBytes) + && (stylePtr->fgGC != None)) { +#if TK_DRAW_IN_CONTEXT + int start = ciPtr->baseOffset + offsetBytes; + int len = ciPtr->numBytes - offsetBytes; + int xDisplacement = x - chunkPtr->x; + + if ((len > 0) && (string[start + len - 1] == '\t')) { + len--; + } + if (len <= 0) { + return; + } + + TkpDrawCharsInContext(display, dst, stylePtr->fgGC, sValuePtr->tkfont, + string, numBytes, start, len, + ciPtr->baseChunkPtr->x + xDisplacement, + y + baseline - sValuePtr->offset); + + if (sValuePtr->underline) { + TkUnderlineCharsInContext(display, dst, stylePtr->fgGC, + sValuePtr->tkfont, string, numBytes, + ciPtr->baseChunkPtr->x + xDisplacement, + y + baseline - sValuePtr->offset, + start, start+len); + } + if (sValuePtr->overstrike) { + Tk_FontMetrics fm; + + Tk_GetFontMetrics(sValuePtr->tkfont, &fm); + TkUnderlineCharsInContext(display, dst, stylePtr->fgGC, + sValuePtr->tkfont, string, numBytes, + ciPtr->baseChunkPtr->x + xDisplacement, + y + baseline - sValuePtr->offset + - fm.descent - (fm.ascent * 3) / 10, + start, start+len); + } +#else + string += offsetBytes; + numBytes -= offsetBytes; if ((numBytes > 0) && (string[numBytes - 1] == '\t')) { numBytes--; @@ -7086,8 +7468,9 @@ CharDisplayProc( numBytes, offsetX, y + baseline - sValuePtr->offset); if (sValuePtr->underline) { Tk_UnderlineChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont, - ciPtr->chars + offsetBytes, offsetX, - y + baseline - sValuePtr->offset, 0, numBytes); + string, offsetX, + y + baseline - sValuePtr->offset, + 0, numBytes); } if (sValuePtr->overstrike) { @@ -7095,11 +7478,12 @@ CharDisplayProc( Tk_GetFontMetrics(sValuePtr->tkfont, &fm); Tk_UnderlineChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont, - ciPtr->chars + offsetBytes, offsetX, + string, offsetX, y + baseline - sValuePtr->offset - fm.descent - (fm.ascent * 3) / 10, 0, numBytes); } +#endif } } @@ -7128,7 +7512,35 @@ CharUndisplayProc( { CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData; +#if TK_LAYOUT_WITH_BASE_CHUNKS + if (chunkPtr == ciPtr->baseChunkPtr) { + + /* + * Basechunks are undisplayed first, when DLines are freed or + * partially freed, so this makes sure we don't access their data any + * more. + */ + + FreeBaseChunk(chunkPtr); + + } else if (ciPtr->baseChunkPtr != NULL) { + + /* + * When other char chunks are undisplayed, drop their characters from + * the base chunk. This usually happens, when they are last in a + * line and need to be re-layed out. + */ + + RemoveFromBaseChunk(chunkPtr); + } + + ciPtr->baseChunkPtr = NULL; + ciPtr->chars = NULL; + ciPtr->numBytes = 0; +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ + ckfree((char *) ciPtr); + chunkPtr->clientData = NULL; } /* @@ -7155,11 +7567,11 @@ CharMeasureProc( int x) /* X-coordinate, in same coordinate system as * chunkPtr->x. */ { - CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData; int endX; - return MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, ciPtr->chars, - chunkPtr->numBytes - 1, chunkPtr->x, x, &endX); /* CHAR OFFSET */ + return CharChunkMeasureChars( + chunkPtr, NULL, 0, 0, chunkPtr->numBytes - 1, chunkPtr->x, x, 0, + &endX); /* CHAR OFFSET */ } /* @@ -7207,8 +7619,8 @@ CharBboxProc( int maxX; maxX = chunkPtr->width + chunkPtr->x; - MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, ciPtr->chars, - byteIndex, chunkPtr->x, -1, xPtr); + CharChunkMeasureChars(chunkPtr, NULL, 0, 0, byteIndex, + chunkPtr->x, -1, 0, xPtr); if (byteIndex == ciPtr->numBytes) { /* @@ -7227,8 +7639,8 @@ CharBboxProc( *widthPtr = maxX - *xPtr; } else { - MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, - ciPtr->chars + byteIndex, 1, *xPtr, -1, widthPtr); + CharChunkMeasureChars(chunkPtr, NULL, 0, byteIndex, byteIndex+1, + *xPtr, -1, 0, widthPtr); if (*widthPtr > maxX) { *widthPtr = maxX - *xPtr; } else { @@ -7275,7 +7687,7 @@ AdjustForTab( TkTextDispChunk *chunkPtr2, *decimalChunkPtr; CharInfo *ciPtr; int tabX, spaceWidth; - char *p; + const char *p; TkTextTabAlign alignment; if (chunkPtr->nextPtr == NULL) { @@ -7388,8 +7800,8 @@ AdjustForTab( int curX; ciPtr = (CharInfo *) decimalChunkPtr->clientData; - MeasureChars(decimalChunkPtr->stylePtr->sValuePtr->tkfont, - ciPtr->chars, decimal, decimalChunkPtr->x, -1, &curX); + CharChunkMeasureChars(decimalChunkPtr, NULL, 0, 0, decimal, + decimalChunkPtr->x, -1, 0, &curX); desired = tabX - (curX - x); goto update; } else { @@ -7413,7 +7825,7 @@ AdjustForTab( update: delta = desired - x; - MeasureChars(textPtr->tkfont, " ", 1, 0, -1, &spaceWidth); + MeasureChars(textPtr->tkfont, " ", 1, 0, 1, 0, -1, 0, &spaceWidth); if (delta < spaceWidth) { delta = spaceWidth; } @@ -7558,7 +7970,7 @@ SizeOfTab( } done: - MeasureChars(textPtr->tkfont, " ", 1, 0, -1, &spaceWidth); + MeasureChars(textPtr->tkfont, " ", 1, 0, 1, 0, -1, 0, &spaceWidth); if (result < spaceWidth) { result = spaceWidth; } @@ -7620,17 +8032,17 @@ NextTabStop( * assumption that Tk_DrawChars will be used to actually display the * characters. * - * If tabs are encountered in the string, they will be expanded to the - * next tab stop. + * If tabs are encountered in the string, they will be ignored (they + * should only occur as last character of the string anyway). * * If a newline is encountered in the string, the line will be broken at * that point. * * Results: - * The return value is the number of bytes from source that fit in the - * span given by startX and maxX. *nextXPtr is filled in with the - * x-coordinate at which the first character that didn't fit would be - * drawn, if it were to be drawn. + * The return value is the number of bytes from the range of start to + * end in source that fit in the span given by startX and maxX. + * *nextXPtr is filled in with the x-coordinate at which the first + * character that didn't fit would be drawn, if it were to be drawn. * * Side effects: * None. @@ -7645,10 +8057,13 @@ MeasureChars( * NULL-terminated. */ int maxBytes, /* Maximum # of bytes to consider from * source. */ + int rangeStart, int rangeLength, + /* Range of bytes to consider in source.*/ int startX, /* X-position at which first character will be * drawn. */ int maxX, /* Don't consider any character that would * cross this x-position. */ + int flags, /* Flags to pass to Tk_MeasureChars. */ int *nextXPtr) /* Return x-position of terminating character * here. */ { @@ -7657,8 +8072,8 @@ MeasureChars( ch = 0; /* lint. */ curX = startX; - special = source; - end = source + maxBytes; + special = source + rangeStart; + end = source + rangeLength; for (start = source; start < end; ) { if (start >= special) { /* @@ -7681,8 +8096,15 @@ MeasureChars( if ((maxX >= 0) && (curX >= maxX)) { break; } - start += Tk_MeasureChars(tkfont, start, special - start, maxX - curX, - 0, &width); +#if TK_DRAW_IN_CONTEXT + start += TkpMeasureCharsInContext(tkfont, source, maxBytes, + start - source, special - start, + maxX >= 0 ? maxX - curX : -1, flags, &width); +#else + (void) maxBytes; + start += Tk_MeasureChars(tkfont, start, special - start, + maxX >= 0 ? maxX - curX : -1, flags, &width); +#endif curX += width; if (start < special) { /* @@ -7701,7 +8123,7 @@ MeasureChars( } *nextXPtr = curX; - return start - source; + return start - (source+rangeStart); } /* @@ -7804,6 +8226,293 @@ TextGetScrollInfoObj( return TKTEXT_SCROLL_ERROR; } +#if TK_LAYOUT_WITH_BASE_CHUNKS +/* + *---------------------------------------------------------------------- + * + * FinalizeBaseChunk -- + * + * This procedure makes sure that all the chunks of the stretch are + * up-to-date. It is invoked when the LayoutProc has been called for + * all chunks and the base chunk is stable. + * + * Results: + * None. + * + * Side effects: + * The CharInfo.chars of all dependent chunks point into + * BaseCharInfo.baseChars for easy access (and compatibility). + * + *---------------------------------------------------------------------- + */ + +static void +FinalizeBaseChunk( + TkTextDispChunk *addChunkPtr) /* An additional chunk to add to the + * stretch, even though it may not be + * in the linked list yet. Used by + * the LayoutProc, otherwise NULL. */ +{ + const char *baseChars; + TkTextDispChunk *chunkPtr; + CharInfo * ciPtr; +#if TK_DRAW_IN_CONTEXT + int widthAdjust = 0; + int newwidth; +#endif + + if (baseCharChunkPtr == NULL) { + return; + } + + baseChars = Tcl_DStringValue( + &((BaseCharInfo*) baseCharChunkPtr->clientData)->baseChars); + + for (chunkPtr = baseCharChunkPtr; + chunkPtr != NULL; + chunkPtr = chunkPtr->nextPtr) { + +#if TK_DRAW_IN_CONTEXT + chunkPtr->x += widthAdjust; +#endif + + if (chunkPtr->displayProc != CharDisplayProc) { + continue; + } + ciPtr = (CharInfo *)chunkPtr->clientData; + if (ciPtr->baseChunkPtr != baseCharChunkPtr) { + break; + } + ciPtr->chars = baseChars + ciPtr->baseOffset; + +#if TK_DRAW_IN_CONTEXT + newwidth = 0; + CharChunkMeasureChars( + chunkPtr, NULL, 0, 0, -1, 0, -1, 0, &newwidth); + if (newwidth != chunkPtr->width) { + widthAdjust += newwidth - chunkPtr->width; + chunkPtr->width = newwidth; + } +#endif + + } + + if (addChunkPtr != NULL) { + + ciPtr = (CharInfo *)addChunkPtr->clientData; + ciPtr->chars = baseChars + ciPtr->baseOffset; + +#if TK_DRAW_IN_CONTEXT + addChunkPtr->x += widthAdjust; + CharChunkMeasureChars( + addChunkPtr, NULL, 0, 0, -1, 0, -1, 0, &addChunkPtr->width); +#endif + + } + + baseCharChunkPtr = NULL; +} + +/* + *---------------------------------------------------------------------- + * + * FreeBaseChunk -- + * + * This procedure makes sure that all the chunks of the stretch are + * disconnected from the base chunk and the base chunk specific data is + * freed. It is invoked from the UndisplayProc. The procedure doesn't + * ckfree the base chunk clientData itself, that's up to the main + * UndisplayProc. + * + * Results: + * None. + * + * Side effects: + * The CharInfo.chars of all dependent chunks are set to NULL. Memory + * that belongs specifically to the base chunk is freed. + * + *---------------------------------------------------------------------- + */ + +static void +FreeBaseChunk( + TkTextDispChunk * baseChunkPtr) /* The base chunk of the stretch and + * head of the linked list. */ +{ + TkTextDispChunk *chunkPtr; + CharInfo *ciPtr; + + if (baseCharChunkPtr == baseChunkPtr) { + baseCharChunkPtr = NULL; + } + + for (chunkPtr = baseChunkPtr; + chunkPtr != NULL; + chunkPtr = chunkPtr->nextPtr) { + + if (chunkPtr->undisplayProc != CharUndisplayProc) { + continue; + } + ciPtr = (CharInfo *) chunkPtr->clientData; + if (ciPtr->baseChunkPtr != baseChunkPtr) { + break; + } + + ciPtr->baseChunkPtr = NULL; + ciPtr->chars = NULL; + } + + Tcl_DStringFree(&((BaseCharInfo*)baseChunkPtr->clientData)->baseChars); +} + +/* + *---------------------------------------------------------------------- + * + * IsSameFGStyle -- + * + * Compare the foreground attributes of two styles. Specifically + * consider: Foreground color, font, font style and font decorations, + * elide, "offset" and foreground stipple. *Don't* consider: Background + * color, border, relief or background stipple. + * + * If we use TkpDrawCharsInContext(), we also don't need to check + * foreground color, font decorations, elide, offset and foreground + * stipple, so all that is left is font (including font size and font + * style) and "offset". + * + * Results: + * 1 if the two styles match, 0 otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +IsSameFGStyle( + TextStyle *style1, + TextStyle *style2) +{ + StyleValues *sv1; + StyleValues *sv2; + + if (style1 == style2) { + return 1; + } + +#if !TK_DRAW_IN_CONTEXT +#ifdef MAC_OSX_TK + + /* + * On Mac, color codes may specify symbolic values like "highlight + * foreground", but we really need the actual values here to compare. + * Maybe see also: "TIP #154: Add Named Colors to Tk". + * + * FIXME: We should have and use a generic function for this. + */ + + { + RGBColor col1, col2; + TkSetMacColor(style1->fgGC->foreground,&col1); + TkSetMacColor(style2->fgGC->foreground,&col2); + if (memcmp(&col1,&col2,sizeof(col1)) != 0) { + return 0; + } + } + +#else + + if (style1->fgGC->foreground != style2->fgGC->foreground) { + return 0; + } + +#endif +#endif + + sv1 = style1->sValuePtr; + sv2 = style2->sValuePtr; + +#if TK_DRAW_IN_CONTEXT + return sv1->tkfont == sv2->tkfont + && sv1->offset == sv2->offset + ; +#else + return sv1->tkfont == sv2->tkfont + && sv1->underline == sv2->underline + && sv1->overstrike == sv2->overstrike + && sv1->elide == sv2->elide + && sv1->offset == sv2->offset + && sv1->fgStipple == sv1->fgStipple + ; +#endif +} + +/* + *---------------------------------------------------------------------- + * + * RemoveFromBaseChunk -- + * + * This procedure removes a chunk from the stretch as a result of + * UndisplayProc. The chunk in question should be the last in a + * stretch. This happens during re-layouting of the break position. + * + * Results: + * None. + * + * Side effects: + * The characters that belong to this chunk are removed from the base + * chunk. It is assumed that LayoutProc and FinalizeBaseChunk are + * called next to repair any damage that this causes to the integrity of + * the stretch and the other chunks. For that reason the base chunk is + * also put into baseCharChunkPtr automatically, so that LayoutProc can + * resume correctly. + * + *---------------------------------------------------------------------- + */ + +static void +RemoveFromBaseChunk( + TkTextDispChunk *chunkPtr) /* The chunk to remove from the end + * of the stretch. */ +{ + CharInfo *ciPtr; + BaseCharInfo *bciPtr; + + if (chunkPtr->displayProc != CharDisplayProc) { + fprintf(stderr,"RemoveFromBaseChunk called with wrong chunk type\n"); + return; + } + + /* + * Reinstitute this base chunk for re-layout. + */ + + ciPtr = (CharInfo*) chunkPtr->clientData; + baseCharChunkPtr = ciPtr->baseChunkPtr; + + /* + * Remove the chunk data from the base chunk data. + */ + + bciPtr = (BaseCharInfo*) baseCharChunkPtr->clientData; + + if ((ciPtr->baseOffset + ciPtr->numBytes) + != Tcl_DStringLength(&bciPtr->baseChars)) { + fprintf(stderr,"RemoveFromBaseChunk called with wrong chunk " + "(not last)\n"); + } + + Tcl_DStringSetLength(&bciPtr->baseChars,ciPtr->baseOffset); + + /* + * Invalidate the stored pixel width of the base chunk. + */ + + bciPtr->width = -1; +} +#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */ + /* * Local Variables: * mode: c diff --git a/library/demos/unicodeout.tcl b/library/demos/unicodeout.tcl index d9f0fef..bd9738c 100644 --- a/library/demos/unicodeout.tcl +++ b/library/demos/unicodeout.tcl @@ -3,7 +3,7 @@ # This demonstration script shows how you can produce output (in label # widgets) using many different alphabets. # -# RCS: @(#) $Id: unicodeout.tcl,v 1.4 2004/12/21 11:56:35 dkf Exp $ +# RCS: @(#) $Id: unicodeout.tcl,v 1.5 2006/03/22 00:21:17 das Exp $ if {![info exists widgetDemo]} { error "This script should be run from the \"widget\" demo." @@ -55,17 +55,33 @@ set oldCursor [$w cget -cursor] $w conf -cursor watch update -addSample $w Arabic \ - "\uFE94\uFEF4\uFE91\uFEAE\uFECC\uFEDF\uFE8D\uFE94" \ - "\uFEE4\uFEE0\uFEDC\uFEDF\uFE8D" +if {[tk windowingsystem] eq "x11"} { + # Using presentation forms (pre-layouted) + addSample $w Arabic \ + "\uFE94\uFEF4\uFE91\uFEAE\uFECC\uFEDF\uFE8D " \ + "\uFE94\uFEE4\uFEE0\uFEDC\uFEDF\uFE8D" +} else { + # Using standard text characters + addSample $w Arabic \ + "\u0627\u0644\u0643\u0644\u0645\u0629 " \ + "\u0627\u0644\u0639\u0631\u0628\u064A\u0629" +} addSample $w "Trad. Chinese" "\u4E2D\u570B\u7684\u6F22\u5B57" addSample $w "Simpl. Chinese" "\u6C49\u8BED" addSample $w Greek \ "\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AE " \ "\u03B3\u03BB\u03CE\u03C3\u03C3\u03B1" -addSample $w Hebrew \ - "\u05DD\u05D9\u05DC\u05E9\u05D5\u05E8\u05D9 " \ - "\u05DC\u05D9\u05D0\u05E8\u05E9\u05D9" +if {[tk windowingsystem] eq "x11"} { + # Visual order (pre-layouted) + addSample $w Hebrew \ + "\u05DD\u05D9\u05DC\u05E9\u05D5\u05E8\u05D9 " \ + "\u05DC\u05D9\u05D0\u05E8\u05E9\u05D9" +} else { + # Standard logical order + addSample $w Hebrew \ + "\u05D9\u05E9\u05E8\u05D0\u05D9\u05DC " \ + "\u05D9\u05E8\u05D5\u05E9\u05DC\u05D9\u05DD" +} addSample $w Japanese \ "\u65E5\u672C\u8A9E\u306E\u3072\u3089\u304C\u306A, " \ "\u6F22\u5B57\u3068\u30AB\u30BF\u30AB\u30CA" diff --git a/macosx/Wish.xcode/default.pbxuser b/macosx/Wish.xcode/default.pbxuser index 164d686..9121060 100644 --- a/macosx/Wish.xcode/default.pbxuser +++ b/macosx/Wish.xcode/default.pbxuser @@ -80,6 +80,11 @@ value = "${SRCROOT}/../../tk/library"; }, { + active = YES; + name = TCLLIBPATH; + value = /Library/Tcl; + }, + { active = NO; name = DYLD_PRINT_LIBRARIES; }, diff --git a/macosx/Wish.xcode/project.pbxproj b/macosx/Wish.xcode/project.pbxproj index 6049eb4..608dea1 100644 --- a/macosx/Wish.xcode/project.pbxproj +++ b/macosx/Wish.xcode/project.pbxproj @@ -27,7 +27,7 @@ buildSettings = { BUILD_STYLE = Deployment; CONFIGURE_ARGS = "$(value) --disable-symbols"; - DEAD_CODE_STRIPPING = YES; + DEAD_CODE_STRIPPING = NO; DEPLOYMENT_POSTPROCESSING = YES; GCC_DEBUGGING_SYMBOLS = full; GCC_ENABLE_FIX_AND_CONTINUE = NO; @@ -466,6 +466,14 @@ //F92 //F93 //F94 + F93E5EFD09CF8711008FA367 = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + path = tkMacOSXFont.h; + refType = 4; + sourceTree = ""; + }; F966BA0308F27A37005CB29B = { children = ( F966BA0408F27A37005CB29B, @@ -3699,6 +3707,7 @@ F966BBD408F27A3B005CB29B, F966BBD508F27A3B005CB29B, F966BBD608F27A3B005CB29B, + F93E5EFD09CF8711008FA367, F966BBD708F27A3B005CB29B, F966BBD808F27A3B005CB29B, F966BBDA08F27A3B005CB29B, diff --git a/macosx/Wish.xcodeproj/default.pbxuser b/macosx/Wish.xcodeproj/default.pbxuser index 1695fa5..c30bf3f 100644 --- a/macosx/Wish.xcodeproj/default.pbxuser +++ b/macosx/Wish.xcodeproj/default.pbxuser @@ -81,6 +81,11 @@ value = "${SRCROOT}/../../tk/library"; }, { + active = YES; + name = TCLLIBPATH; + value = /Library/Tcl; + }, + { active = NO; name = DYLD_PRINT_LIBRARIES; }, diff --git a/macosx/Wish.xcodeproj/project.pbxproj b/macosx/Wish.xcodeproj/project.pbxproj index 29331fc..d832275 100644 --- a/macosx/Wish.xcodeproj/project.pbxproj +++ b/macosx/Wish.xcodeproj/project.pbxproj @@ -300,6 +300,7 @@ /* Begin PBXFileReference section */ 8DD76FB20486AB0100D96B5E /* tktest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tktest; sourceTree = BUILT_PRODUCTS_DIR; }; + F93E5EFD09CF8711008FA367 /* tkMacOSXFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tkMacOSXFont.h; sourceTree = ""; }; F966BA0408F27A37005CB29B /* error.xbm */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; fileEncoding = 4; path = error.xbm; sourceTree = ""; }; F966BA0508F27A37005CB29B /* gray12.xbm */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; fileEncoding = 4; path = gray12.xbm; sourceTree = ""; }; F966BA0608F27A37005CB29B /* gray25.xbm */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; fileEncoding = 4; path = gray25.xbm; sourceTree = ""; }; @@ -2087,6 +2088,7 @@ F966BBD408F27A3B005CB29B /* tkMacOSXEvent.c */, F966BBD508F27A3B005CB29B /* tkMacOSXEvent.h */, F966BBD608F27A3B005CB29B /* tkMacOSXFont.c */, + F93E5EFD09CF8711008FA367 /* tkMacOSXFont.h */, F966BBD708F27A3B005CB29B /* tkMacOSXHLEvents.c */, F966BBD808F27A3B005CB29B /* tkMacOSXInit.c */, F966BBDA08F27A3B005CB29B /* tkMacOSXInt.h */, @@ -3704,7 +3706,7 @@ ); BINDIR = "${PREFIX}/bin"; CONFIGURE_ARGS = "--enable-threads ${CONFIGURE_ARGS}"; - DEAD_CODE_STRIPPING = YES; + DEAD_CODE_STRIPPING = NO; DEPLOYMENT_POSTPROCESSING = YES; GCC_DEBUGGING_SYMBOLS = full; GCC_DYNAMIC_NO_PIC = YES; @@ -3906,7 +3908,7 @@ buildSettings = { BINDIR = "${PREFIX}/bin"; CONFIGURE_ARGS = "--enable-threads ${CONFIGURE_ARGS}"; - DEAD_CODE_STRIPPING = YES; + DEAD_CODE_STRIPPING = NO; DEPLOYMENT_POSTPROCESSING = YES; GCC_DEBUGGING_SYMBOLS = full; GCC_DYNAMIC_NO_PIC = YES; diff --git a/macosx/tkMacOSXButton.c b/macosx/tkMacOSXButton.c index 8effde3..ed75502 100644 --- a/macosx/tkMacOSXButton.c +++ b/macosx/tkMacOSXButton.c @@ -10,11 +10,12 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkMacOSXButton.c,v 1.16 2005/11/27 02:36:14 das Exp $ + * RCS: @(#) $Id: tkMacOSXButton.c,v 1.17 2006/03/22 00:21:17 das Exp $ */ #include "tkButton.h" #include "tkMacOSXInt.h" +#include "tkMacOSXFont.h" #include "tkMacOSXDebug.h" #define DEFAULT_USE_TK_TEXT 0 @@ -109,9 +110,6 @@ static void SetupBevelButton _ANSI_ARGS_((MacButton *butPtr, ControlRef controlHandle, GWorldPtr destPort, GC gc, Pixmap pixmap)); -MODULE_SCOPE int TkFontGetFirstTextLayout(Tk_TextLayout layout, Tk_Font * font, char * dst); -MODULE_SCOPE void TkMacOSXInitControlFontStyle(Tk_Font tkfont,ControlFontStylePtr fsPtr); - /* * The class procedure table for the button widgets. */ diff --git a/macosx/tkMacOSXFont.c b/macosx/tkMacOSXFont.c index 2059662..f55542d 100644 --- a/macosx/tkMacOSXFont.c +++ b/macosx/tkMacOSXFont.c @@ -1,8 +1,13 @@ -/* +/* * tkMacOSXFont.c -- * - * Contains the Macintosh implementation of the platform-independant - * font package interface. + * 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 + * + * 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. @@ -11,370 +16,267 @@ * 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.11 2005/12/08 07:50:14 das Exp $ + * + * 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). + * + * RCS: @(#) $Id: tkMacOSXFont.c,v 1.12 2006/03/22 00:21:17 das Exp $ */ -#include #include "tkMacOSXInt.h" -#include "tkFont.h" +#include "tkMacOSXFont.h" /* - * For doing things with Mac strings and Fixed numbers. This probably should move - * the mac header file. +#ifdef TK_MAC_DEBUG +#define TK_MAC_DEBUG_FONTS +#endif +*/ + +typedef TkMacOSXFont MacFont; +typedef TkMacOSXFontDrawingContext DrawingContext; + +/* + * Features that we still may want to disable again. See first occurrance of + * these macros in #if statements for respective general discussions. */ -#ifndef StrLength -#define StrLength(s) (*((unsigned char *) (s))) -#endif -#ifndef StrBody -#define StrBody(s) ((char *) (s) + 1) -#endif -#define pstrcmp(s1, s2) RelString((s1), (s2), 1, 1) -#define pstrcasecmp(s1, s2) RelString((s1), (s2), 0, 1) +/* #define TK_MAC_COALESCE_LINE 1 */ +/* #define TK_MAC_USE_MEASURETEXTIMAGE 1 */ +/* #define TK_MAC_USE_GETGLYPHBOUNDS 1 */ -#ifndef Fixed2Int -#define Fixed2Int(f) ((f) >> 16) -#define Int2Fixed(i) ((i) << 16) -#endif /* - * The preferred font encodings. + * 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. */ -static CONST char *encodingList[] = { - "macRoman", "macJapan", NULL -}; +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. */ /* - * The following structures are used to map the script/language codes of a - * font to the name that should be passed to Tcl_GetTextEncoding() to obtain - * the encoding for that font. The set of numeric constants is fixed and - * defined by Apple. + * 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. */ - -static TkStateMap scriptMap[] = { - {smRoman, "macRoman"}, - {smJapanese, "macJapan"}, - {smTradChinese, "macChinese"}, - {smKorean, "macKorean"}, - {smArabic, "macArabic"}, - {smHebrew, "macHebrew"}, - {smGreek, "macGreek"}, - {smCyrillic, "macCyrillic"}, - {smRSymbol, "macRSymbol"}, - {smDevanagari, "macDevanagari"}, - {smGurmukhi, "macGurmukhi"}, - {smGujarati, "macGujarati"}, - {smOriya, "macOriya"}, - {smBengali, "macBengali"}, - {smTamil, "macTamil"}, - {smTelugu, "macTelugu"}, - {smKannada, "macKannada"}, - {smMalayalam, "macMalayalam"}, - {smSinhalese, "macSinhalese"}, - {smBurmese, "macBurmese"}, - {smKhmer, "macKhmer"}, - {smThai, "macThailand"}, - {smLaotian, "macLaos"}, - {smGeorgian, "macGeorgia"}, - {smArmenian, "macArmenia"}, - {smSimpChinese, "macSimpChinese"}, - {smTibetan, "macTIbet"}, - {smMongolian, "macMongolia"}, - {smGeez, "macEthiopia"}, - {smEastEurRoman, "macCentEuro"}, - {smVietnamese, "macVietnam"}, - {smExtArabic, "macSindhi"}, - {0, NULL} -}; - -static TkStateMap romanMap[] = { - {langCroatian, "macCroatian"}, - {langSlovenian, "macCroatian"}, - {langIcelandic, "macIceland"}, - {langRomanian, "macRomania"}, - {langTurkish, "macTurkish"}, - {langGreek, "macGreek"}, - {0, NULL} -}; - -static TkStateMap cyrillicMap[] = { - {langUkrainian, "macUkraine"}, - {langBulgarian, "macBulgaria"}, - {0, NULL} -}; + +#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 /* - * 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. + * 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 avoid lines that + * tremble and shiver while the cursor passes through them, we undercut the + * system and behind the scenes paste 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. */ +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. */ -#define FONTMAP_SHIFT 10 +#endif /* TK_MAC_COALESCE_LINE */ -#define FONTMAP_PAGES (1 << (sizeof(Tcl_UniChar) * 8 - FONTMAP_SHIFT)) -#define FONTMAP_BITSPERPAGE (1 << FONTMAP_SHIFT) +static int TkMacOSXAntialiasedTextEnabled = -1; -typedef struct FontFamily { - struct FontFamily *nextPtr; /* Next in list of all known font families. */ - int refCount; /* How many SubFonts are referring to this - * FontFamily. When the refCount drops to - * zero, this FontFamily may be freed. */ - /* - * Key. - */ +/* + * The names for our two "native" fonts. + */ - FMFontFamily faceNum; /* Unique face number key for this FontFamily. */ - - /* - * Derived properties. - */ - - Tcl_Encoding encoding; /* Encoding for this font family. */ - int isSymbolFont; /* Non-zero if this is a symbol family. */ - int isMultiByteFont; /* Non-zero if this is a multi-byte family. */ - char typeTable[256]; /* Table that identfies all lead bytes for a - * multi-byte family, used when measuring chars. - * If a byte is a lead byte, the value at the - * corresponding position in the typeTable is 1, - * otherwise 0. If this is a single-byte font, - * all entries are 0. */ - char *fontMap[FONTMAP_PAGES]; - /* Two-level sparse table used to determine - * quickly if the specified character exists. - * As characters are encountered, more pages - * in this table are dynamically added. The - * contents of each page is a bitmask - * consisting of FONTMAP_BITSPERPAGE bits, - * representing whether this font can be used - * to display the given character at the - * corresponding bit position. The high bits - * of the character are used to pick which - * page of the table is used. */ -} FontFamily; +#define SYSTEMFONT_NAME "system" +#define APPLFONT_NAME "application" /* - * The following structure encapsulates an individual screen font. A font - * object is made up of however many SubFonts are necessary to display a - * stream of multilingual characters. + * Procedures used only in this file. + */ + +/* + * The actual workers. */ -typedef struct SubFont { - char **fontMap; /* Pointer to font map from the FontFamily, - * cached here to save a dereference. */ - FontFamily *familyPtr; /* The FontFamily for this SubFont. */ -} SubFont; +static void MacFontDrawText( + const MacFont * fontPtr, + const char * source, int numBytes, + int rangeStart, int rangeLength, + int x, int y); +static int MeasureStringWidth( + const MacFont * fontPtr, + int start, int end); + +#if TK_MAC_COALESCE_LINE +static const Tcl_UniChar * UpdateLineBuffer( + const MacFont * fontPtr, + const DrawingContext * drawingContextPtr, + const char * source, int numBytes, + int x, int y, + int * offset); +#endif /* TK_MAC_COALESCE_LINE */ /* - * The following structure represents Macintosh's implementation of a font - * object. + * Initialization and setup of a font data structure. */ -#define SUBFONT_SPACE 3 - -typedef struct MacFont { - TkFont font; /* Stuff used by generic font package. Must - * be first in structure. */ - SubFont staticSubFonts[SUBFONT_SPACE]; - /* Builtin space for a limited number of - * SubFonts. */ - int numSubFonts; /* Length of following array. */ - SubFont *subFontArray; /* Array of SubFonts that have been loaded - * in order to draw/measure all the characters - * encountered by this font so far. All fonts - * start off with one SubFont initialized by - * AllocFont() from the original set of font - * attributes. Usually points to - * staticSubFonts, but may point to malloced - * space if there are lots of SubFonts. */ - - short size; /* Font size in pixels, constructed from - * font attributes. */ - short style; /* Style bits, constructed from font - * attributes. */ -} MacFont; +static void InitFont( + Tk_Window tkwin, + 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, + ATSUStyle style); +static void AdjustFontHeight( + MacFont * fontPtr); +static void InitATSULayout( + const DrawingContext * drawingContextPtr, + ATSUTextLayout layout, int fixed); +static void ReleaseFont( + MacFont * fontPtr); /* - * 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. + * Finding fonts by name. */ - -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; + +static const MacFontFamily * FindFontFamilyOrAlias( + const char * name); +static const MacFontFamily * FindFontFamilyOrAliasOrFallback( + const char * name); /* - * The list of font families that are currently loaded. As screen fonts - * are loaded, this list grows to hold information about what characters - * exist in each font family. + * Doing interesting things with font families and fonts. */ -static FontFamily *fontFamilyList = NULL; +static void InitFontFamilies(void); +static OSStatus GetFontFamilyName( + FMFontFamily fontFamily, char * name, int numBytes); /* - * Information cached about the system at startup time. + * Accessor functions and internal utilities for the font family list. */ - -static FontNameMap *gFontNameMap = NULL; -static GWorldPtr gWorld = NULL; + +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); /* - * Procedures used only in this file. + * Trace interface for configuring anti-aliasing through a global variable. */ -static FontFamily * AllocFontFamily(CONST MacFont *fontPtr, int family); -static SubFont * CanUseFallback(MacFont *fontPtr, CONST char *fallbackName, int ch, SubFont **fixSubFontPtrPtr); -static SubFont * CanUseFallbackWithAliases(MacFont *fontPtr, CONST char *faceName, int ch, Tcl_DString *nameTriedPtr, SubFont **fixSubFontPtrPtr); -static SubFont * FindSubFontForChar(MacFont *fontPtr, int ch, SubFont **fixSubFontPtrPtr); -static void FontMapInsert(SubFont *subFontPtr, int ch); -static void FontMapLoadPage(SubFont *subFontPtr, int row); -static int FontMapLookup(SubFont *subFontPtr, int ch); -static void FreeFontFamily(FontFamily *familyPtr); -static void InitFont(Tk_Window tkwin, int family, int size, int style, MacFont *fontPtr); -static void InitSubFont(CONST MacFont *fontPtr, int family, SubFont *subFontPtr); -static void MultiFontDrawText(MacFont *fontPtr, CONST char *source, int numBytes, int x, int y); -static void ReleaseFont(MacFont *fontPtr); -static void ReleaseSubFont(SubFont *subFontPtr); -static int SeenName(CONST char *name, Tcl_DString *dsPtr); - -static CONST char * BreakLine(FontFamily *familyPtr, 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 Tcl_Encoding GetFontEncoding(int faceNum, int allowSymbol, int *isSymbolPtr); -static Tk_Uid GetUtfFaceName(StringPtr faceNameStr); - -MODULE_SCOPE void TkMacOSXInitControlFontStyle(Tk_Font tkfont, - ControlFontStylePtr fsPtr); +static char * TkMacOSXAntialiasedTextVariableProc( + ClientData clientData, Tcl_Interp * interp, + const char * name1, const char * name2, + int flag); + +/* + * For doing things with Fixed numbers. FIXME: This probably should move to + * tkMacOSXInt.h. + */ + +#ifndef Fixed2Int +#define Fixed2Int(f) ((f+0x8000) >> 16) +#define Int2Fixed(i) ((i) << 16) +#endif + /* *------------------------------------------------------------------------- - * + * * TkpFontPkgInit -- * * This procedure is called when an application is created. It - * initializes all the structures that are used by the + * initializes all the structures that are used by the * platform-dependant code on a per application basis. * * Results: - * None. + * None. * * Side effects: - * See comments below. + * Initialization of variables local to this file. * *------------------------------------------------------------------------- */ void -TkpFontPkgInit(mainPtr) - TkMainInfo *mainPtr; /* The application being created. */ +TkpFontPkgInit( + TkMainInfo * mainPtr) /* The application being created. */ { - FMFontFamilyIterator fontFamilyIterator; - FMFontFamily fontFamily; - FontNameMap *tmpFontNameMap, *newFontNameMap, *mapPtr; - int i, j, numFonts, fontMapOffset, isSymbol; - Str255 nativeName; - Tcl_DString ds; - Tcl_Encoding encoding; - Tcl_Encoding *encodings; - - 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); - encodings = (Tcl_Encoding *) ckalloc(sizeof(Tcl_Encoding) * numFonts); - mapPtr = tmpFontNameMap; - FMResetFontFamilyIterator(NULL, NULL, kFMDefaultOptions, &fontFamilyIterator); - i = 0; - while (FMGetNextFontFamily(&fontFamilyIterator, &fontFamily) != kFMIterationCompleted) { - mapPtr->faceNum = fontFamily; - encodings[i] = GetFontEncoding(mapPtr->faceNum, 0, &isSymbol); - FMGetFontFamilyName(fontFamily, nativeName ); - Tcl_ExternalToUtfDString(encodings[i], StrBody(nativeName), StrLength(nativeName), &ds); - mapPtr->utfName = Tk_GetUid(Tcl_DStringValue(&ds)); - mapPtr->nativeName = (StringPtr) ckalloc(StrLength(nativeName) + 1); - memcpy(mapPtr->nativeName, nativeName, StrLength(nativeName) + 1); - Tcl_DStringFree(&ds); - mapPtr++; - i++; - } - FMDisposeFontFamilyIterator (&fontFamilyIterator); - - /* - * Reorder FontNameMap so fonts with the preferred encodings are at - * the front of the list. The relative order of fonts that all have - * the same encoding is preserved. Fonts with unknown encodings get - * stuck at the end. - */ - newFontNameMap = (FontNameMap *) ckalloc(sizeof(FontNameMap) * (numFonts + 1)); - fontMapOffset = 0; - for (i = 0; encodingList[i] != NULL; i++) { - encoding = Tcl_GetEncoding(NULL, encodingList[i]); - if (encoding == NULL) { - continue; - } - for (j = 0; j < numFonts; j++) { - if (encodings[j] == encoding) { - newFontNameMap[fontMapOffset] = tmpFontNameMap[j]; - fontMapOffset++; - Tcl_FreeEncoding(encodings[j]); - tmpFontNameMap[j].utfName = NULL; - } - } - Tcl_FreeEncoding(encoding); - } - for (i = 0; i < numFonts; i++) { - if (tmpFontNameMap[i].utfName != NULL) { - newFontNameMap[fontMapOffset] = tmpFontNameMap[i]; - fontMapOffset++; - Tcl_FreeEncoding(encodings[i]); - } - } - if (fontMapOffset != numFonts) { - Tcl_Panic("TkpFontPkgInit: unexpected number of fonts"); - } + InitFontFamilies(); - mapPtr = &newFontNameMap[numFonts]; - mapPtr->utfName = NULL; - mapPtr->nativeName = NULL; - mapPtr->faceNum = 0; +#if TK_MAC_COALESCE_LINE + Tcl_DStringInit(¤tLine); +#endif - ckfree((char *) tmpFontNameMap); - ckfree((char *) encodings); - - gFontNameMap = newFontNameMap; - } +#ifdef TK_MAC_DEBUG_FONTS + fprintf(stderr, "tkMacOSXFont.c (ATSU version) intialized " + "(" __TIME__ ")\n");*/ +#endif } + /* *--------------------------------------------------------------------------- @@ -384,13 +286,13 @@ TkpFontPkgInit(mainPtr) * Map a platform-specific native font name to a TkFont. * * Results: - * The return value is a pointer to a TkFont that represents the + * 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. + * 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. + * 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 @@ -405,43 +307,43 @@ TkpFontPkgInit(mainPtr) TkFont * TkpGetNativeFont( Tk_Window tkwin, /* For display where font will be used. */ - CONST char *name) /* Platform-specific font name. */ + 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(); + FMFontFamily familyId; + MacFont * fontPtr; + + if (strcmp(name, SYSTEMFONT_NAME) == 0) { + familyId = GetSysFont(); + } else if (strcmp(name, APPLFONT_NAME) == 0) { + familyId = GetAppFont(); } else { return NULL; } - + fontPtr = (MacFont *) ckalloc(sizeof(MacFont)); - InitFont(tkwin, family, 0, 0, fontPtr); - + InitFont(tkwin, familyId, NULL, 0, 0, fontPtr); + return (TkFont *) fontPtr; } /* *--------------------------------------------------------------------------- * - * TkpGetFontFromAttributes -- + * TkpGetFontFromAttributes -- * - * Given a desired set of attributes for a font, find a font with - * the closest matching attributes. + * 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. + * 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. + * 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 @@ -452,65 +354,45 @@ TkpGetNativeFont( * *--------------------------------------------------------------------------- */ + 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. */ + 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++) { - for (j = 0; (fallback = fallbacks[i][j]) != NULL; j++) { - if (strcasecmp(faceName, fallback) == 0) { - for (j = 0; (fallback = fallbacks[i][j]) != NULL; j++) { - if (GetFamilyOrAliasNum(fallback, &faceNum)) { - goto found; - } - } - } - break; - } + 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; } } - - found: - style = 0; + if (faPtr->weight != TK_FW_NORMAL) { - style |= bold; + qdStyle |= bold; } if (faPtr->slant != TK_FS_ROMAN) { - style |= italic; + qdStyle |= italic; } if (faPtr->underline) { - style |= underline; + qdStyle |= underline; } if (tkFontPtr == NULL) { fontPtr = (MacFont *) ckalloc(sizeof(MacFont)); @@ -518,8 +400,8 @@ TkpGetFontFromAttributes( fontPtr = (MacFont *) tkFontPtr; ReleaseFont(fontPtr); } - InitFont(tkwin, faceNum, faPtr->size, style, fontPtr); - + InitFont(tkwin, familyId, name, faPtr->size, qdStyle, fontPtr); + return (TkFont *) fontPtr; } @@ -530,26 +412,23 @@ TkpGetFontFromAttributes( * * 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. + * released the fields of the TkFont that are used exclusively by the + * generic TkFont code. * * Results: - * None. + * TkFont is deallocated. * * Side effects: - * TkFont is deallocated. + * None. * *--------------------------------------------------------------------------- */ void TkpDeleteFont( - TkFont *tkFontPtr) /* Token of font to be deleted. */ + TkFont * tkFontPtr) /* Token of font to be deleted. */ { - MacFont *fontPtr; - - fontPtr = (MacFont *) tkFontPtr; - ReleaseFont(fontPtr); + ReleaseFont((MacFont *) tkFontPtr); } /* @@ -557,8 +436,8 @@ TkpDeleteFont( * * TkpGetFontFamilies -- * - * Return information about the font families that are available - * on the display of the given window. + * 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 @@ -569,20 +448,13 @@ TkpDeleteFont( * *--------------------------------------------------------------------------- */ - + void TkpGetFontFamilies( - Tcl_Interp *interp, /* Interp to hold result. */ + 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); - } +{ + Tcl_SetObjResult(interp,EnumFontFamilies()); } /* @@ -590,38 +462,28 @@ TkpGetFontFamilies( * * TkpGetSubFonts -- * - * A function used by the testing package for querying the actual + * 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. + * 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. */ +TkpGetSubFonts( + Tcl_Interp * interp, /* Interp to hold result. */ + Tk_Font tkfont) /* Font object to query. */ { - int i; - Tcl_Obj *resultPtr, *strPtr; - MacFont *fontPtr; - FontFamily *familyPtr; - Str255 nativeName; - - resultPtr = Tcl_GetObjResult(interp); - fontPtr = (MacFont *) tkfont; - for (i = 0; i < fontPtr->numSubFonts; i++) { - familyPtr = fontPtr->subFontArray[i].familyPtr; - GetFontName(familyPtr->faceNum, nativeName); - strPtr = Tcl_NewStringObj(GetUtfFaceName(nativeName), -1); - Tcl_ListObjAppendElement(NULL, resultPtr, strPtr); - } + /* 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. */ } /* @@ -629,166 +491,417 @@ TkpGetSubFonts(interp, tkfont) * * 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. + * 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. + * + * 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. + * + * 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. */ + 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; - SubFont *thisSubFontPtr, *lastSubFontPtr; - CGrafPtr saveWorld; - GDHandle saveDevice; - int curX, curByte; + 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 (or at least the first partial word + * in case TK_WHOLE_WORDS is also set) even if no + * characters (words) fit. + * 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; + int curByte = 0; + UniChar * uchars; + int ulen, urstart, urlen, urend; + Tcl_DString ucharBuffer; + DrawingContext drawingContext; + /* - * According to "Inside Macintosh: Text", the Macintosh may - * automatically substitute - * ligatures or context-sensitive presentation forms when - * measuring/displaying text within a font run. We cannot safely - * measure individual characters and add up the widths w/o errors. - * However, if we convert a range of text from UTF-8 to, say, - * Shift-JIS, and get the offset into the Shift-JIS string as to - * where a word or line break would occur, then can we map that - * number back to UTF-8? + * Sanity checks. */ - - fontPtr = (MacFont *) tkfont; - GetGWorld(&saveWorld, &saveDevice); - SetGWorld(gWorld, NULL); - - TextSize(fontPtr->size); - TextFace(fontPtr->style); + if ((rangeStart < 0) || ((rangeStart+rangeLength) > numBytes)) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "MeasureChars: bad parameters\n"); +#endif + *lengthPtr = 0; + return 0; + } + + /* + * Get simple no-brainers out of the way. + */ + + if (rangeLength == 0 || (maxLength == 0 && !(flags & TK_AT_LEAST_ONE))) { +#ifdef TK_MAC_DEBUG_FONTS + fflush(stdout); + fprintf(stderr, "measure: '%.*s', empty\n", + rangeLength, source+rangeStart); + fflush(stderr); +#endif + *lengthPtr = 0; + return 0; + } + +#if TK_MAC_USE_QUARZ + TkMacOSXQuarzStartDraw(&drawingContext); +#endif + + Tcl_DStringInit(&ucharBuffer); + uchars = Tcl_UtfToUniCharDString(source, numBytes, &ucharBuffer); + ulen = Tcl_DStringLength(&ucharBuffer) / sizeof(uchars[0]); + TkMacOSXLayoutSetString(fontPtr, &drawingContext, 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; - lastSubFontPtr = &fontPtr->subFontArray[0]; - - if (numBytes == 0) { - curX = 0; - curByte = 0; - } else if (maxLength < 0) { - CONST char *p, *end, *next; - Tcl_UniChar ch; - FontFamily *familyPtr; - Tcl_DString runString; - - /* - * A three step process: - * 1. Find a contiguous range of characters that can all be - * represented by a single screen font. - * 2. Convert those chars to the encoding of that font. - * 3. Measure converted chars. - */ - - curX = 0; - end = source + numBytes; - for (p = source; p < end; ) { - next = p + Tcl_UtfToUniChar(p, &ch); - thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); - if (thisSubFontPtr != lastSubFontPtr) { - familyPtr = lastSubFontPtr->familyPtr; - TextFont(familyPtr->faceNum); - Tcl_UtfToExternalDString(familyPtr->encoding, source, - p - source, &runString); - curX += TextWidth(Tcl_DStringValue(&runString), 0, - Tcl_DStringLength(&runString)); - Tcl_DStringFree(&runString); - lastSubFontPtr = thisSubFontPtr; - source = p; - } - p = next; - } - familyPtr = lastSubFontPtr->familyPtr; - TextFont(familyPtr->faceNum); - Tcl_UtfToExternalDString(familyPtr->encoding, source, p - source, - &runString); - curX += TextWidth(Tcl_DStringValue(&runString), 0, - Tcl_DStringLength(&runString)); - Tcl_DStringFree(&runString); - curByte = numBytes; } else { - CONST char *p, *end, *next, *sourceOrig; - int widthLeft; - Tcl_UniChar ch; - CONST char *rest = NULL; - + + UniCharArrayOffset offset = 0; + OSStatus err; + /* - * How many chars will fit in the space allotted? + * Have some upper limit on the size actually used. */ - + if (maxLength > 32767) { maxLength = 32767; } - - widthLeft = maxLength; - sourceOrig = source; - end = source + numBytes; - for (p = source; p < end; p = next) { - next = p + Tcl_UtfToUniChar(p, &ch); - thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); - if (thisSubFontPtr != lastSubFontPtr) { - if (p > source) { - rest = BreakLine(lastSubFontPtr->familyPtr, flags, source, - p - source, &widthLeft); - flags &= ~TK_AT_LEAST_ONE; - if (rest != NULL) { - p = source; - break; + + 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 Int2Fixed(2), so we + * need at least Int2Fixed(3) (at least that's the current state + * of my knowledge). + */ + + err = ATSUBreakLine( + fontPtr->atsuLayout, + urstart, + Int2Fixed(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)) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUBreakLine(): Error %d for '%.*s'\n", + (int) err, rangeLength, source+rangeStart); +#endif + } + +#ifdef TK_MAC_DEBUG_FONTS + fprintf(stderr, "measure: '%.*s', break offset=%d, errcode=%d\n", + rangeLength, source+rangeStart, (int) offset, (int) 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) { + + /* + * If we are the the start of the range, we need to look + * forward. If we are not at the end of a word, we must + * be in the middle of the first word, so we also look + * forward. + */ + + if ((offset == urstart) || (uchars[offset] != ' ')) { + while ((offset < urend) + && (uchars[offset] != ' ')) { + offset++; + } + } + } else { + + /* + * If we are not at the end of a word, we need to look + * backward. + */ + + if ((offset != urend) && (uchars[offset] != ' ')) { + while ((offset > urstart) + && (uchars[offset-1] != ' ')) { + offset--; + } + while ((offset > urstart) + && (uchars[offset-1] == ' ')) { + offset--; + } } } - lastSubFontPtr = thisSubFontPtr; - source = p; } } - - if (p > source) { - rest = BreakLine(lastSubFontPtr->familyPtr, flags, source, p - source, - &widthLeft); + + 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. + */ + + if ((err != kATSULineBreakInWord) + && !(flags & TK_WHOLE_WORDS) + && (offset <= urend)) { + + UniCharArrayOffset lastOffset = offset; + UniCharArrayOffset nextoffset; + int lastX = -1; + int wantonemorechar = -1; /* undecided */ + + while (offset <= urend) { + + if (flags & TK_ISOLATE_END) { + TkMacOSXLayoutSetString(fontPtr, &drawingContext, + uchars, offset); + } + curX = MeasureStringWidth(fontPtr, urstart, offset); + +#ifdef TK_MAC_DEBUG_FONTS + fprintf(stderr, "measure: '%.*s', try until=%d, width=%d\n", + rangeLength, source+rangeStart, (int) 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) { + TkMacOSXLayoutSetString(fontPtr, &drawingContext, + uchars, ulen); + } + err = ATSUNextCursorPosition( + fontPtr->atsuLayout, + offset, + kATSUByCluster, + &nextoffset); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUNextCursorPosition(): " + "Error %d\n", (int) err); +#endif + break; + } + if (nextoffset <= offset) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUNextCursorPosition(): " + "Can't move further " + "(shouldn't happen, bad data?)\n"); +#endif + break; + } + + offset = nextoffset; + } + + /* + * We have overshot one character, so backup one position. + */ + + curX = lastX; + offset = lastOffset; } - - if (rest == NULL) { - curByte = numBytes; - } else { - curByte = rest - sourceOrig; + + if (curX < 0) { + if (flags & TK_ISOLATE_END) { + TkMacOSXLayoutSetString(fontPtr, &drawingContext, + uchars, offset); + } + curX = MeasureStringWidth(fontPtr, urstart, offset); } - curX = maxLength - widthLeft; + + curByte = Tcl_UtfAtIndex(source, offset) - source; + curByte -= rangeStart; } - SetGWorld(saveWorld, saveDevice); + Tcl_DStringFree(&ucharBuffer); + +#if TK_MAC_USE_QUARZ + TkMacOSXQuarzEndDraw(&drawingContext); +#endif + +#ifdef TK_MAC_DEBUG_FONTS + fflush(stdout); + fprintf(stderr, "measure: '%.*s', maxpix=%d, -> width=%d, bytes=%d, " + "flags=%s%s%s%s\n", + rangeLength, source+rangeStart, maxLength, curX, curByte, + flags & TK_PARTIAL_OK ? "partialOk " : "", + flags & TK_WHOLE_WORDS ? "wholeWords " : "", + flags & TK_AT_LEAST_ONE ? "atLeastOne " : "", + flags & TK_ISOLATE_END ? "isolateEnd " : ""); + fflush(stderr); +#endif *lengthPtr = curX; return curByte; @@ -797,392 +910,606 @@ Tk_MeasureChars( /* *--------------------------------------------------------------------------- * - * BreakLine -- + * Tk_DrawChars -- + * + * Draw a string of characters on the screen. * - * 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. + * With ATSUI we need the line context to do this right, so we have the + * actual implementation in TkpDrawCharsInContext(). * * Results: - * The return value is NULL if the specified range is larger that the - * space the text needs, and *widthLeftPtr is filled with how much - * space is left in the range after measuring the whole text buffer. - * Otherwise, the return value is a pointer into the text buffer that - * indicates where the line should be broken (up to, but not including - * that character), and *widthLeftPtr is filled with how much space is - * left in the range after measuring up to that character. + * + * None. * * Side effects: - * None. + * + * Information gets drawn on the screen. * *--------------------------------------------------------------------------- */ - -static CONST char * -BreakLine( - FontFamily *familyPtr, /* FontFamily that describes the font values - * that are already selected into the graphics - * port. */ - 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 numBytes, /* Maximum number of bytes to consider - * from source string. */ - int *widthLeftPtr) /* On input, specifies size of range into - * which characters from source buffer should - * be fit. On output, filled with how much - * space is left after fitting as many - * characters as possible into the range. - * Result may be negative if TK_AT_LEAST_ONE - * was specified in the flags argument. */ -{ - Fixed pixelWidth, widthLeft; - StyledLineBreakCode breakCode; - Tcl_DString runString; - long textOffset; - Boolean leadingEdge; - Point point; - int charOffset, thisCharWasDoubleByte; - char *p, *end, *typeTable; - - TextFont(familyPtr->faceNum); - Tcl_UtfToExternalDString(familyPtr->encoding, source, numBytes, - &runString); - pixelWidth = Int2Fixed(*widthLeftPtr) + 1; - if (flags & TK_WHOLE_WORDS) { - textOffset = (flags & TK_AT_LEAST_ONE); - widthLeft = pixelWidth; - breakCode = StyledLineBreak(Tcl_DStringValue(&runString), - Tcl_DStringLength(&runString), 0, Tcl_DStringLength(&runString), - 0, &widthLeft, &textOffset); - if (breakCode != smBreakOverflow) { - /* - * StyledLineBreak includes all the space characters at the end of - * line that we want to suppress. - */ - - textOffset = VisibleLength(Tcl_DStringValue(&runString), textOffset); - goto getoffset; - } - } else { - point.v = 1; - point.h = 1; - textOffset = PixelToChar(Tcl_DStringValue(&runString), - Tcl_DStringLength(&runString), 0, pixelWidth, &leadingEdge, - &widthLeft, smOnlyStyleRun, point, point); - if (Fixed2Int(widthLeft) < 0) { - goto getoffset; - } - } - *widthLeftPtr = Fixed2Int(widthLeft); - Tcl_DStringFree(&runString); - return NULL; - /* - * The conversion routine that converts UTF-8 to the target encoding - * must map one UTF-8 character to exactly one encoding-specific - * character, so that the following algorithm works: - * - * 1. Get byte offset of where line should be broken. - * 2. Get char offset corresponding to that byte offset. - * 3. Map that char offset to byte offset in UTF-8 string. - */ - - getoffset: - thisCharWasDoubleByte = 0; - if (familyPtr->isMultiByteFont == 0) { - charOffset = textOffset; - } else { - charOffset = 0; - typeTable = familyPtr->typeTable; - - p = Tcl_DStringValue(&runString); - end = p + textOffset; - thisCharWasDoubleByte = typeTable[*((unsigned char *) p)]; - for ( ; p < end; p++) { - thisCharWasDoubleByte = typeTable[*((unsigned char *) p)]; - p += thisCharWasDoubleByte; - charOffset++; - } - } - - if ((flags & TK_WHOLE_WORDS) == 0) { - if ((flags & TK_PARTIAL_OK) && (leadingEdge != 0)) { - textOffset += thisCharWasDoubleByte; - textOffset++; - charOffset++; - } else if (((flags & TK_PARTIAL_OK) == 0) && (leadingEdge == 0)) { - textOffset -= thisCharWasDoubleByte; - textOffset--; - charOffset--; - } - } - if ((textOffset == 0) && (Tcl_DStringLength(&runString) > 0) - && (flags & TK_AT_LEAST_ONE)) { - p = Tcl_DStringValue(&runString); - textOffset += familyPtr->typeTable[*((unsigned char *) p)]; - textOffset++; - charOffset++; - } - *widthLeftPtr = Fixed2Int(pixelWidth) - - TextWidth(Tcl_DStringValue(&runString), 0, textOffset); - Tcl_DStringFree(&runString); - return Tcl_UtfAtIndex(source, charOffset); +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. */ +{ + TkpDrawCharsInContext(display, drawable, gc, tkfont, source, numBytes, + 0, numBytes, x, y); } + /* *--------------------------------------------------------------------------- * - * Tk_DrawChars -- + * TkpDrawCharsInContext -- * - * Draw a string of characters on the screen. + * Draw a string of characters on the screen like Tk_DrawChars(), with + * access to all the characters on the line for context. * * Results: - * None. + * None. * * Side effects: - * Information gets drawn on the screen. + * Information gets drawn on the screen. + * + * Todo: + * + * We could try to implement a correct stipple algorithm. + * + * The fiddling with QD GraphPorts can be replaced with just working + * with the Quarz CGContext (see TkMacOSXQuarzStartDraw()), once we + * decide to stick to Quarz and really forget about QD drawing in here. * *--------------------------------------------------------------------------- */ 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. */ +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. */ { - MacFont *fontPtr; - MacDrawable *macWin; + const MacFont * fontPtr; + MacDrawable * macWin; RGBColor macColor, origColor; GWorldPtr destPort; CGrafPtr saveWorld; GDHandle saveDevice; - short txFont, txFace, txSize; BitMapPtr stippleMap; - Rect portRect; + Rect portRect; - fontPtr = (MacFont *) tkfont; + fontPtr = (const 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, + pixmap = Tk_GetPixmap(display, drawable, stippleMap->bounds.right, stippleMap->bounds.bottom, 0); - + bufferPort = TkMacOSXGetDrawablePort(pixmap); SetGWorld(bufferPort, NULL); - - if (TkSetMacColor(gc->foreground, &macColor) == true) { + + if (TkSetMacColor(gc->foreground, &macColor)) { RGBForeColor(&macColor); } GetQDGlobalsWhite(&white); ShowPen(); FillRect(&stippleMap->bounds, &white); - MultiFontDrawText(fontPtr, source, numBytes, 0, 0); + MacFontDrawText(fontPtr, source, numBytes, rangeStart, rangeLength, + 0, 0); HidePen(); SetGWorld(destPort, NULL); - CopyDeepMask(GetPortBitMapForCopyBits(bufferPort), stippleMap, + + 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 + + /* + * 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) { + } else { + if (TkSetMacColor(gc->foreground, &macColor)) { RGBForeColor(&macColor); } ShowPen(); - MultiFontDrawText(fontPtr, source, numBytes, macWin->xOff + x, - macWin->yOff + y); + MacFontDrawText(fontPtr, source, numBytes, rangeStart, rangeLength, + macWin->xOff + x, macWin->yOff + y); HidePen(); } - TextFont(txFont); - TextSize(txSize); - TextFace(txFace); RGBForeColor(&origColor); + SetGWorld(saveWorld, saveDevice); } +#if TK_MAC_USE_QUARZ +/* + *------------------------------------------------------------------------- + * + * TkMacOSXQuarzStartDraw -- + * + * Setup a Quarz CGContext from the current QD GraphPort for use in + * drawing or measuring. + * + * Results: + * + * A CGContext is allocated, configured and returned in + * drawingContextPtr. Also drawingContextPtr->portRect is filled in. + * + * Side effects: + * + * None. + * + * Assumptions: + * + * The current QD GraphPort contains all the data necessary. This is + * clearly the case for the actual drawing, but not so clear for + * measuring. OTOH for measuring the specific parameters are not really + * interesting and the GraphPort is not changed either. The + * availability of a CGContext may be important for the measuring + * process though. + * + *------------------------------------------------------------------------- + */ + +void +TkMacOSXQuarzStartDraw( + DrawingContext * drawingContextPtr) /* Quarz context data filled in + * by this function. */ +{ + GDHandle currentDevice; + RgnHandle clipRgn; + RGBColor qdColor; + double red, green, blue; + + /* + * Create the CGContext. + */ + + GetGWorld(&drawingContextPtr->graphPort, ¤tDevice); + QDBeginCGContext( + drawingContextPtr->graphPort, + &drawingContextPtr->cgContext); + CGContextSaveGState( + drawingContextPtr->cgContext); + + /* + * Sync some parameters, most notably the clipping region. I'm not sure + * if that part of the code is right, as the coordinate systems of the + * graphPort and the cgContext are different. + */ + + SyncCGContextOriginWithPort( + drawingContextPtr->cgContext, drawingContextPtr->graphPort); + GetPortBounds(drawingContextPtr->graphPort, &drawingContextPtr->portRect); + + clipRgn = NewRgn(); + GetPortClipRegion(drawingContextPtr->graphPort, clipRgn); + ClipCGContextToRegion( + drawingContextPtr->cgContext, + &drawingContextPtr->portRect, clipRgn); + DisposeRgn(clipRgn); + + /* + * Scale the color values, as QD uses UInt16 with the range [0..2^16-1] + * while Quarz uses float with [0..1]. NB: Only + * CGContextSetRGBFillColor() seems to be actually used by ATSU. + */ + + GetForeColor(&qdColor); + red = (double) qdColor.red / (double) 0xFFFF; + green = (double) qdColor.green / (double) 0xFFFF; + blue = (double) qdColor.blue / (double) 0xFFFF; + CGContextSetRGBFillColor( + drawingContextPtr->cgContext, red, green, blue, 1.0); +/* CGContextSetRGBStrokeColor( */ +/* drawingContextPtr->cgContext, red, green, blue, 1.0); */ +} +#endif /* TK_MAC_USE_QUARZ */ + +#if TK_MAC_USE_QUARZ /* *------------------------------------------------------------------------- * - * MultiFontDrawText -- + * TkMacOSXQuarzEndDraw -- * - * Helper function for Tk_DrawChars. Draws characters, using the - * various screen fonts in fontPtr to draw multilingual characters. - * Note: No bidirectional support. + * Free the Quarz CGContext in drawingContextPtr. + * + * Results: + * + * The CGContext is de-allocated. drawingContextPtr->cgContext will be + * invalid after this. + * + * Side effects: + * + * None. + * + *------------------------------------------------------------------------- + */ + +void +TkMacOSXQuarzEndDraw( + DrawingContext * drawingContextPtr) +{ + CGContextRestoreGState( + drawingContextPtr->cgContext); + QDEndCGContext( + drawingContextPtr->graphPort, + &drawingContextPtr->cgContext); +} +#endif /* TK_MAC_USE_QUARZ */ + +/* + *------------------------------------------------------------------------- + * + * MacFontDrawText -- + * + * Helper function for Tk_DrawChars. Draws characters, using the + * screen font in fontPtr to draw multilingual characters. * * 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. + * Information gets drawn on the screen. * *------------------------------------------------------------------------- */ static void -MultiFontDrawText( - MacFont *fontPtr, /* Contains set of fonts to use when drawing +MacFontDrawText( + const MacFont * fontPtr, /* Contains font to use when drawing * following string. */ - CONST char *source, /* Potentially multilingual UTF-8 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. */ + 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 + * string when drawing. */ { - SubFont *thisSubFontPtr, *lastSubFontPtr; - FontFamily *familyPtr; + Fixed fx, fy; + int ulen, urstart, urlen; + const UniChar * uchars; + int lineOffset; + DrawingContext drawingContext; + OSStatus err; + +#if !TK_MAC_COALESCE_LINE Tcl_DString runString; - CONST char *p, *end, *next; - Tcl_UniChar ch; - - TextSize(fontPtr->size); - TextFace(fontPtr->style); - - lastSubFontPtr = &fontPtr->subFontArray[0]; - - end = source + numBytes; - for (p = source; p < end; ) { - next = p + Tcl_UtfToUniChar(p, &ch); - thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); - if (thisSubFontPtr != lastSubFontPtr) { - if (p > source) { - familyPtr = lastSubFontPtr->familyPtr; - TextFont(familyPtr->faceNum); - Tcl_UtfToExternalDString(familyPtr->encoding, source, - p - source, &runString); - MoveTo((short) x, (short) y); - DrawText(Tcl_DStringValue(&runString), 0, - Tcl_DStringLength(&runString)); - x += TextWidth(Tcl_DStringValue(&runString), 0, - Tcl_DStringLength(&runString)); - Tcl_DStringFree(&runString); - source = p; - } - lastSubFontPtr = thisSubFontPtr; - } - p = next; - } - if (p > source) { - familyPtr = lastSubFontPtr->familyPtr; - TextFont(familyPtr->faceNum); - Tcl_UtfToExternalDString(familyPtr->encoding, source, - p - source, &runString); - MoveTo((short) x, (short) y); - DrawText(Tcl_DStringValue(&runString), 0, - Tcl_DStringLength(&runString)); - Tcl_DStringFree(&runString); +#endif + + +#if TK_MAC_USE_QUARZ + TkMacOSXQuarzStartDraw(&drawingContext); + + /* + * Turn the y coordinate upside-down for Quarz drawing. We would have + * liked to just use a CTM transform in the CGContext, but than we get + * upside-down text, so doing it that way gets more painfull than to just + * hack around the problem right here. + */ + + y = drawingContext.portRect.bottom - drawingContext.portRect.top - y; + fy = Int2Fixed(y); +#else + fy = Int2Fixed(y); +#endif + + +#if TK_MAC_COALESCE_LINE + UpdateLineBuffer( + fontPtr, &drawingContext, source, numBytes, x, y, &lineOffset); + + fx = Int2Fixed(currentLeft); + + uchars = (const Tcl_UniChar*) Tcl_DStringValue(¤tLine); + ulen = Tcl_DStringLength(¤tLine) / sizeof(uchars[0]); +#else + lineOffset = 0; + fx = Int2Fixed(x); + + Tcl_DStringInit(&runString); + uchars = Tcl_UtfToUniCharDString(source, numBytes, &runString); + ulen = Tcl_DStringLength(&runString) / sizeof(uchars[0]); + + TkMacOSXLayoutSetString(fontPtr, &drawingContext, uchars, ulen); +#endif + + urstart = Tcl_NumUtfChars(source, rangeStart); + urlen = Tcl_NumUtfChars(source+rangeStart,rangeLength); + + err = ATSUDrawText( + fontPtr->atsuLayout, + lineOffset+urstart, urlen, + fx, fy); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUDrawText(): Error %d\n", (int) err); +#endif } + +#if !TK_MAC_COALESCE_LINE + Tcl_DStringFree(&runString); +#endif + +#if TK_MAC_USE_QUARZ + TkMacOSXQuarzEndDraw(&drawingContext); +#endif } + /* *--------------------------------------------------------------------------- * - * TkMacOSXIsCharacterMissing -- + * MeasureStringWidth -- * - * 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. + * Low-level measuring of strings. * * Results: - * Returns a 1 if the character is missing, a 0 if it is not. + * + * The width of the string in pixels. * * Side effects: - * None. + * + * None. + * + * Assumptions: + * + * fontPtr->atsuLayout is setup with the actual string data to measure. * *--------------------------------------------------------------------------- */ - -int -TkMacOSXIsCharacterMissing( - Tk_Font tkfont, /* The font we are looking in. */ - unsigned int searchChar) /* The character we are looking for. */ +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. */ { -/* - * For some reason, FMSwapFont always returns a NULL font handle under OS X - * Until we figure this one out, return 0; - */ -#ifdef MAC_OSX_TK - return 0; -#else - MacFont *fontPtr = (MacFont *) tkfont; - FMInput fm; - FontRec **fontRecHandle; - FMOutPtr fmOutPtr; - - - fm.family = fontPtr->subFontArray[0].familyPtr->faceNum; - 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; - - fmOutPtr = FMSwapFont(&fm); - fprintf(stderr,"fmOut %08x, handle %08x\n", (int)fmOutPtr, fmOutPtr->fontHandle); - -#if !defined(UNIVERSAL_INTERFACES_VERSION) || (UNIVERSAL_INTERFACES_VERSION < 0x0300) - fontRecHandle = (FontRec **) FMSwapFont(&fm)->fontResult; -#else - fontRecHandle = (FontRec **) FMSwapFont(&fm)->fontHandle; +#if TK_MAC_USE_MEASURETEXTIMAGE + + /* + * This implementation of measuring via ATSUMeasureTextImage() 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 ATSUMeasureTextImage() *does* include these "extra pixels." + * Also ATSUMeasureTextImage() has the important property that it will + * measure 'a' and 'a-umlaut' the same, even when expressed as decomposed + * characters, while the other implementations use some strange + * interpolation instead, possibly to generate some cute intermediate + * cursor positions automatically. + */ + + OSStatus err; + Rect size; + + size.left = size.right = 0; + err = ATSUMeasureTextImage( + fontPtr->atsuLayout, + start, end-start, + 0, 0, + &size); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUMeasureTextImage(): Error %d\n", (int) err); #endif - return *(short *) ((long) &(*fontRecHandle)->owTLoc - + ((long)((*fontRecHandle)->owTLoc + searchChar - - (*fontRecHandle)->firstChar) * sizeof(short))) == -1; + } + + return size.right - size.left; + +#elif TK_MAC_USE_GETGLYPHBOUNDS + + /* + * 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 I would assume that ATSUGetGlyphBounds() *does* include these + * "extra pixels." Still this implementation works slightly better with + * the main page of demos/widgets than the one via + * ATSUOffsetToPosition(), so I prefer this one if we use + * TK_MAC_COALESCE_LINE locally. + */ + + ATSTrapezoid bounds; + ItemCount numBounds; + OSStatus err; + + if (end <= start) { + return 0; + } + + bounds.upperRight.x = bounds.upperLeft.x = 0; + err = ATSUGetGlyphBounds( + fontPtr->atsuLayout, + 0, 0, + start, end-start, + kATSUseDeviceOrigins, + 1, &bounds, &numBounds); +#ifdef TK_MAC_DEBUG + if (err != noErr) { + fprintf(stderr, "ATSUGetGlyphBounds(): Error %d\n", (int) err); + } else if (numBounds < 1) { + fprintf(stderr, "ATSUGetGlyphBounds(): No output\n"); + } else if (numBounds > 1) { + fprintf(stderr, "ATSUGetGlyphBounds(): More output\n"); + } #endif + + return Fixed2Int(bounds.upperRight.x - bounds.upperLeft.x); + +#else /* ! TK_MAC_USE_GETGLYPHBOUNDS */ + + /* + * This implementation via ATSUOffsetToPosition() is in principle the + * right thing to do. But with TK_MAC_COALESCE_LINE and this, the + * "tremble and shiver" (see discussion of TK_MAC_COALESCE_LINE at the + * top) on the main page of demos/widgets is quite a bit more noticable + * than with the ATSUGetGlyphBounds() implementation. + */ + + ATSUCaret mainCaretStart, secCaretStart, mainCaretEnd, secCaretEnd; + Boolean isSplit; + OSStatus err; + + if (end <= start) { + return 0; + } + + mainCaretStart.fX = mainCaretEnd.fX = 0; + + if (start != 0) { + err = ATSUOffsetToPosition( + fontPtr->atsuLayout, + start, false, + &mainCaretStart, &secCaretStart, &isSplit); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUOffsetToPosition(): Error %d\n", (int) err); +#endif + } + } + + if (end != 0) { + err = ATSUOffsetToPosition( + fontPtr->atsuLayout, + end, false, + &mainCaretEnd, &secCaretEnd, &isSplit); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUOffsetToPosition(): Error %d\n", (int) err); +#endif + } + } + + return Fixed2Int(mainCaretEnd.fX - mainCaretStart.fX); + +#endif /* ? TK_MAC_USE_GETGLYPHBOUNDS */ +} + +#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 TkMacOSXLayoutSetString()). + * + *------------------------------------------------------------------------- + */ + +static const Tcl_UniChar * +UpdateLineBuffer( + const MacFont * fontPtr,/* The font to be used for the new piece of + * text. */ + const DrawingContext * 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(¤tLine); + Tcl_DStringInit(¤tLine); + currentY = y; + currentLeft = x; + currentFontPtr = fontPtr; + *offset = 0; + } else { + *offset = Tcl_DStringLength(¤tLine) / 2; + } + + Tcl_UtfToUniCharDString(source, numBytes, ¤tLine); + uchars = (const Tcl_UniChar*) Tcl_DStringValue(¤tLine); + ulen = Tcl_DStringLength(¤tLine) / sizeof(*uchars); + TkMacOSXLayoutSetString(fontPtr, drawingContextPtr, uchars, ulen); + currentRight = x + MeasureStringWidth(fontPtr, *offset, ulen); + + return uchars; } +#endif /* TK_MAC_COALESCE_LINE */ /* *--------------------------------------------------------------------------- @@ -1190,12 +1517,12 @@ TkMacOSXIsCharacterMissing( * InitFont -- * * Helper for TkpGetNativeFont() and TkpGetFontFromAttributes(). - * Initializes the memory for a MacFont that wraps the platform-specific - * data. + * 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(). + * 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. @@ -1204,984 +1531,1105 @@ TkMacOSXIsCharacterMissing( * 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. */ + Tk_Window tkwin, /* For display where font will be used. */ + 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. */ { - Str255 nativeName; + OSStatus err; FontInfo fi; - TkFontAttributes *faPtr; - TkFontMetrics *fmPtr; - CGrafPtr saveWorld; - GDHandle saveDevice; - short pixels; + TkFontAttributes * faPtr; + TkFontMetrics * fmPtr; + short points; + int iWidth, wWidth; if (size == 0) { - size = -GetDefFontSize(); + size = GetDefFontSize(); } - pixels = (short) TkFontGetPixels(tkwin, size); - - GetGWorld(&saveWorld, &saveDevice); - SetGWorld(gWorld, NULL); - TextFont(faceNum); - - - TextSize(pixels); - TextFace(style); + points = (short) TkFontGetPoints(tkwin, size); + + err = FetchFontInfo(familyId, points, qdStyle, &fi); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "FetchFontInfo(): Error %d\n", (int) err); +#endif + } + + if (familyName == NULL) { + char name[256] = ""; + const MacFontFamily * familyPtr; + + err = GetFontFamilyName(familyId, name, sizeof(name)); + if (err == noErr) { + + /* + * We find the canonical font name, so we can avoid unnecessary + * memory management. + */ + + familyPtr = FindFontFamily(name); + if (familyPtr != NULL) { + familyName = familyPtr->name; + } else { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "Font family '%s': Not found\n", name); +#endif + } + } + } + + fontPtr->font.fid = (Font) fontPtr; - GetFontInfo(&fi); - GetFontName(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->family = familyName; + faPtr->size = points; + 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; - fmPtr->ascent = fi.ascent; - fmPtr->descent = fi.descent; + + /* + * 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; - fmPtr->fixed = (CharWidth('i') == CharWidth('w')); - - fontPtr->size = pixels; - fontPtr->style = (short) style; - - fontPtr->numSubFonts = 1; - fontPtr->subFontArray = fontPtr->staticSubFonts; - InitSubFont(fontPtr, faceNum, &fontPtr->subFontArray[0]); - SetGWorld(saveWorld, saveDevice); + fontPtr->qdFont = familyId; + fontPtr->qdSize = points; + fontPtr->qdStyle = (short) qdStyle; + + InitATSUObjects( + familyId, points, qdStyle, + &fontPtr->atsuFontId, &fontPtr->atsuLayout, &fontPtr->atsuStyle); + + Tk_MeasureChars((Tk_Font)fontPtr, "i", 1, -1, 0, &iWidth); + Tk_MeasureChars((Tk_Font)fontPtr, "w", 1, -1, 0, &wWidth); + fmPtr->fixed = iWidth == wWidth; + + SetFontFeatures(fontPtr->atsuFontId, fmPtr->fixed, fontPtr->atsuStyle); + + AdjustFontHeight(fontPtr); } /* - *------------------------------------------------------------------------- + *--------------------------------------------------------------------------- * - * ReleaseFont -- - * - * Called to release the Macintosh-specific contents of a TkFont. - * The caller is responsible for freeing the memory used by the - * font itself. + * InitATSUObjects -- + * + * Helper for InitFont(). Initializes the ATSU data handles for a + * MacFont. * * Results: - * None. + * + * Sets up all we know and can do at this point in time in fontIdPtr, + * layoutPtr and stylePtr. * * Side effects: - * Memory is freed. + * + * Allocates data structures inside of ATSU. * *--------------------------------------------------------------------------- */ - + static void -ReleaseFont( - MacFont *fontPtr) /* The font to delete. */ +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. */ { - int i; + OSStatus err; + 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. + */ + + err = FMGetFontFromFontFamilyInstance( + familyId, qdStyles, fontIdPtr, &stylesDone); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "FMGetFontFromFontFamilyInstance(): Error %d\n", + (int) err); +#endif + } + + /* + * We see what style bits are left and tell ATSU to synthesize what's + * left like QD does it. + */ + + stylesLeft = qdStyles & ~(unsigned)stylesDone; - for (i = 0; i < fontPtr->numSubFonts; i++) { - ReleaseSubFont(&fontPtr->subFontArray[i]); + /* + * Create the style and set its attributes. + */ + + err = ATSUCreateStyle(stylePtr); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUCreateStyle(): Error %d\n", (int) err); +#endif } - if (fontPtr->subFontArray != fontPtr->staticSubFonts) { - ckfree((char *) fontPtr->subFontArray); + 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. + */ + + err = ATSUCreateTextLayout(layoutPtr); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUCreateTextLayout(): Error %d\n", (int) err); +#endif } + /*InitATSULayout(*layoutPtr);*/ } /* - *------------------------------------------------------------------------- + *--------------------------------------------------------------------------- * - * InitSubFont -- + * InitATSUStyle -- * - * Wrap a screen font and load the FontFamily that represents - * it. Used to prepare a SubFont so that characters can be mapped - * from UTF-8 to the charset of the font. + * Helper for InitATSUObjects(). Initializes the ATSU style for a + * MacFont. * * Results: - * The subFontPtr is filled with information about the font. + * + * Sets up all parameters needed for an ATSU style. * * Side effects: - * None. * - *------------------------------------------------------------------------- + * Allocates data structures for the style inside of ATSU. + * + *--------------------------------------------------------------------------- */ static void -InitSubFont( - CONST MacFont *fontPtr, /* Font object in which the SubFont will be - * used. */ - int faceNum, /* The font number. */ - SubFont *subFontPtr) /* Filled with SubFont constructed from - * above attributes. */ +InitATSUStyle( + ATSUFontID fontId, /* The font id to use. */ + short ptSize, short qdStyles, /* Additional font parameters. */ + ATSUStyle style) /* The style handle to configure. */ { - subFontPtr->familyPtr = AllocFontFamily(fontPtr, faceNum); - subFontPtr->fontMap = subFontPtr->familyPtr->fontMap; + /* + * Attributes for the style. + */ + + Fixed fsize = Int2Fixed(ptSize); + Boolean + isBold = (qdStyles&bold) != 0, + isUnderline = (qdStyles&underline) != 0, + isItalic = (qdStyles&italic) != 0; + + ATSStyleRenderingOptions options = + TkMacOSXAntialiasedTextEnabled == -1 ? kATSStyleNoOptions : + TkMacOSXAntialiasedTextEnabled == 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, + }; + + OSStatus err; + + err = ATSUSetAttributes( + style, + sizeof(styleTags)/sizeof(styleTags[0]), + styleTags, styleSizes, styleValues); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUSetAttributes(): Error %d\n", (int) err); +#endif + } } + /* - *------------------------------------------------------------------------- + *--------------------------------------------------------------------------- * - * ReleaseSubFont -- + * SetFontFeatures -- * - * Called to release the contents of a SubFont. The caller is - * responsible for freeing the memory used by the SubFont itself. + * Helper for InitFont(). Request specific font features of the ATSU + * style object for a MacFont. * * Results: - * None. + * + * None. * * Side effects: - * Memory and resources are freed. + * + * Specific font features are enabled on the ATSU style object. * *--------------------------------------------------------------------------- */ static void -ReleaseSubFont( - SubFont *subFontPtr) /* The SubFont to delete. */ +SetFontFeatures( + ATSUFontID fontId, /* The font id to use. */ + int fixed, /* Is this a fixed font? */ + ATSUStyle style) /* The style handle to configure. */ { - FreeFontFamily(subFontPtr->familyPtr); + /* + * 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 + }; + + OSStatus err; + + if (fixed) { + err = ATSUSetFontFeatures( + style, + sizeof(fixed_featureTypes)/sizeof(fixed_featureTypes[0]), + fixed_featureTypes, fixed_featureSelectors); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUSetFontFeatures(): Error %d\n", (int) err); +#endif + } + } } /* - *------------------------------------------------------------------------- + *--------------------------------------------------------------------------- * - * AllocFontFamily -- + * AdjustFontHeight -- * - * Find the FontFamily structure associated with the given font - * family. The information should be stored by the caller in a - * SubFont and used when determining if that SubFont supports a - * character. + * Helper for InitFont(). Check font height against some real world + * examples. * * Results: - * A pointer to a FontFamily. The reference count in the FontFamily - * is automatically incremented. When the SubFont is released, the - * reference count is decremented. When no SubFont is using this - * FontFamily, it may be deleted. + * + * None. * * Side effects: - * A new FontFamily structure will be allocated if this font family - * has not been seen. * - *------------------------------------------------------------------------- + * The metrics in fontPtr->font.fm are adjusted so that typical combined + * characters fit into ascent+descent. + * + *--------------------------------------------------------------------------- */ -static FontFamily * -AllocFontFamily( - CONST MacFont *fontPtr, /* Font object in which the FontFamily will - * be used. */ - int faceNum) /* The font number. */ +static void +AdjustFontHeight( + MacFont * fontPtr) { - FontFamily *familyPtr; - int i; - - familyPtr = fontFamilyList; - for (; familyPtr != NULL; familyPtr = familyPtr->nextPtr) { - if (familyPtr->faceNum == faceNum) { - familyPtr->refCount++; - return familyPtr; - } - } + /* + * 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]. + */ - familyPtr = (FontFamily *) ckalloc(sizeof(FontFamily)); - memset(familyPtr, 0, sizeof(FontFamily)); - familyPtr->nextPtr = fontFamilyList; - fontFamilyList = familyPtr; + static const UniChar chars[] + /* Auml, Aacute, Acirc, Atilde, Ccedilla */ + = {0x00C4, 0x00C1, 0x00C2, 0x00C3, 0x00C7}; + static const int charslen = sizeof(chars) / sizeof(chars[0]); - /* - * Set key for this FontFamily. - */ - - familyPtr->faceNum = faceNum; + DrawingContext drawingContext; + Rect size; + OSStatus err; - /* - * An initial refCount of 2 means that FontFamily information will - * persist even when the SubFont that loaded the FontFamily is released. - * Change it to 1 to cause FontFamilies to be unloaded when not in use. - */ - - familyPtr->refCount = 2; - familyPtr->encoding = GetFontEncoding(faceNum, 1, &familyPtr->isSymbolFont); - familyPtr->isMultiByteFont = 0; - FillParseTable(familyPtr->typeTable, FontToScript(faceNum)); - for (i = 0; i < 256; i++) { - if (familyPtr->typeTable[i] != 0) { - familyPtr->isMultiByteFont = 1; - break; + +#if TK_MAC_USE_QUARZ + TkMacOSXQuarzStartDraw(&drawingContext); +#endif + + TkMacOSXLayoutSetString(fontPtr, &drawingContext, chars, charslen); + + size.top = size.bottom = 0; + err = ATSUMeasureTextImage( + fontPtr->atsuLayout, + 0, charslen, + 0, 0, + &size); + +#if TK_MAC_USE_QUARZ + TkMacOSXQuarzEndDraw(&drawingContext); +#endif + + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUMeasureTextImage(): Error %d\n", (int) err); +#endif + } else { + 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; } } - return familyPtr; } + /* - *------------------------------------------------------------------------- + *--------------------------------------------------------------------------- * - * FreeFontFamily -- + * InitATSULayout -- * - * Called to free a FontFamily when the SubFont is finished using it. - * Frees the contents of the FontFamily and the memory used by the - * FontFamily itself. + * Helper for TkMacOSXLayoutSetString(). Initializes the ATSU layout + * object for a MacFont and a specific string. * * Results: - * None. + * + * Sets up all parameters needed for an ATSU layout object. * * Side effects: - * None. * - *------------------------------------------------------------------------- + * 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 + * TkMacOSXLayoutSetString()). + * + *--------------------------------------------------------------------------- */ - + static void -FreeFontFamily( - FontFamily *familyPtr) /* The FontFamily to delete. */ +InitATSULayout( + const DrawingContext * drawingContextPtr, + /* Specifies the CGContext to use. */ + ATSUTextLayout layout, /* The layout object to configure. */ + int fixed) /* Is this a fixed font? */ { - FontFamily **familyPtrPtr; - int i; + /* + * Attributes for the layout. + */ + + ATSLineLayoutOptions layoutOptions = + 0 +#if TK_MAC_COALESCE_LINE + /* + * Options to use unconditionally, when we try to do coalescing in here. + */ + | kATSLineDisableAllLayoutOperations + | kATSLineFractDisable + | kATSLineUseDeviceMetrics +#endif + ; + + static const ATSUAttributeTag layoutTags[] = { +#if TK_MAC_USE_QUARZ + kATSUCGContextTag, +#endif + kATSULineLayoutOptionsTag, + }; + static const ByteCount layoutSizes[] = { +#if TK_MAC_USE_QUARZ + sizeof(CGContextRef), +#endif + sizeof(ATSLineLayoutOptions), + }; + const ATSUAttributeValuePtr layoutValues[] = { +#if TK_MAC_USE_QUARZ + (void*)&drawingContextPtr->cgContext, +#endif + &layoutOptions, + }; + + OSStatus err; + + /* + * Ensure W(abcdefg) == W(a)*7 for fixed fonts (Latin scripts only). + */ - if (familyPtr == NULL) { - return; + if (fixed) { + layoutOptions |= + kATSLineFractDisable + | kATSLineUseDeviceMetrics + ; } - familyPtr->refCount--; - if (familyPtr->refCount > 0) { - return; + + err = ATSUSetLayoutControls( + layout, + sizeof(layoutTags)/sizeof(layoutTags[0]), + layoutTags, layoutSizes, layoutValues); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUSetLayoutControls(): Error %d\n", (int) err); +#endif } - Tcl_FreeEncoding(familyPtr->encoding); - for (i = 0; i < FONTMAP_PAGES; i++) { - if (familyPtr->fontMap[i] != NULL) { - ckfree((char *) familyPtr->fontMap[i]); - } + + err = ATSUSetTransientFontMatching(layout, true); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUSetTransientFontMatching(): Error %d\n", + (int) err); +#endif } - - /* - * Delete from list. +} + +/* + *--------------------------------------------------------------------------- + * + * TkMacOSXLayoutSetString -- + * + * 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 +TkMacOSXLayoutSetString( + const MacFont * fontPtr, /* The fontPtr to configure. */ + const DrawingContext * drawingContextPtr, + /* For the CGContext to be used.*/ + const UniChar * uchars, int ulen) /* The UniChar string to set into + * fontPtr->atsuLayout. */ +{ + OSStatus err; + err = ATSUSetTextPointerLocation( + fontPtr->atsuLayout, + uchars, kATSUFromTextBeginning, ulen, + ulen); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUSetTextPointerLocation(): Error %d\n", (int) err); +#endif + } + + /* + * Styles can only be set after the text is set. */ - - for (familyPtrPtr = &fontFamilyList; ; ) { - if (*familyPtrPtr == familyPtr) { - *familyPtrPtr = familyPtr->nextPtr; - break; - } - familyPtrPtr = &(*familyPtrPtr)->nextPtr; + + err = ATSUSetRunStyle( + fontPtr->atsuLayout, fontPtr->atsuStyle, + kATSUFromTextBeginning, kATSUToTextEnd); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSUSetRunStyle(): Error %d\n", (int) err); +#endif } - - ckfree((char *) familyPtr); + + /* + * 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); } /* *------------------------------------------------------------------------- * - * FindSubFontForChar -- + * 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(). * - * Determine which physical screen font is necessary to use to - * display the given character. If the font object does not have - * a screen font that can display the character, another screen font - * may be loaded into the font object, following a set of preferred - * fallback rules. + * 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 the SubFont to use to display the given - * character. + * + * The return value is NULL if the specified font family does not exist, + * a valid MacFontFamily* otherwise. * * Side effects: - * The contents of fontPtr are modified to cache the results - * of the lookup and remember any SubFonts that were dynamically - * loaded. The table of SubFonts might be extended, and if a non-NULL - * reference to a subfont pointer is available, it is updated if it - * previously pointed into the old subfont table. + * + * None. * *------------------------------------------------------------------------- */ -static SubFont * -FindSubFontForChar( - MacFont *fontPtr, /* The font object with which the character - * will be displayed. */ - int ch, /* The Unicode character to be displayed. */ - SubFont **fixSubFontPtrPtr) /* Subfont reference to fix up if we - * reallocate our subfont table. */ +static const MacFontFamily * +FindFontFamilyOrAlias( + const char * name) /* Name or alias name of the font to find. */ { - int i, j, k; - CONST char *fallbackName; - char **aliases; - SubFont *subFontPtr; - FontNameMap *mapPtr; - Tcl_DString faceNames; - char ***fontFallbacks; - char **anyFallbacks; - - if (FontMapLookup(&fontPtr->subFontArray[0], ch)) { - return &fontPtr->subFontArray[0]; + const MacFontFamily * familyPtr; + char ** aliases; + int i; + + familyPtr = FindFontFamily(name); + if (familyPtr != NULL) { + return familyPtr; } - for (i = 1; i < fontPtr->numSubFonts; i++) { - if (FontMapLookup(&fontPtr->subFontArray[i], ch)) { - return &fontPtr->subFontArray[i]; + aliases = TkFontGetAliasList(name); + if (aliases != NULL) { + for (i = 0; aliases[i] != NULL; i++) { + familyPtr = FindFontFamily(aliases[i]); + if (familyPtr != NULL) { + return familyPtr; + } } } + return NULL; +} - /* - * Keep track of all face names that we check, so we don't check some - * name multiple times if it can be reached by multiple paths. - */ - - Tcl_DStringInit(&faceNames); - - aliases = TkFontGetAliasList(fontPtr->font.fa.family); - - subFontPtr = NULL; - fontFallbacks = TkFontGetFallbacks(); - for (i = 0; fontFallbacks[i] != NULL; i++) { - for (j = 0; fontFallbacks[i][j] != NULL; j++) { - fallbackName = fontFallbacks[i][j]; - if (strcasecmp(fallbackName, fontPtr->font.fa.family) == 0) { - /* - * If the base font has a fallback... - */ - - goto tryfallbacks; - } else if (aliases != NULL) { - /* - * Or if an alias for the base font has a fallback... - */ +static const MacFontFamily * +FindFontFamilyOrAliasOrFallback( + const char * name) /* Name or alias name of the font to find. */ +{ + const MacFontFamily * familyPtr; + const char * fallback; + char *** fallbacks; + int i, j; - for (k = 0; aliases[k] != NULL; k++) { - if (strcasecmp(aliases[k], fallbackName) == 0) { - goto tryfallbacks; + 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; } } } - } - continue; - - /* - * ...then see if we can use one of the fallbacks, or an - * alias for one of the fallbacks. - */ - - tryfallbacks: - for (j = 0; fontFallbacks[i][j] != NULL; j++) { - fallbackName = fontFallbacks[i][j]; - subFontPtr = CanUseFallbackWithAliases(fontPtr, fallbackName, - ch, &faceNames, fixSubFontPtrPtr); - if (subFontPtr != NULL) { - goto end; - } + break; /* benny: This "break" is a carry-over from + * tkMacOSXFont.c, but what is actually its purpose + * ???? */ } } - - /* - * See if we can use something from the global fallback list. - */ - anyFallbacks = TkFontGetGlobalClass(); - for (i = 0; anyFallbacks[i] != NULL; i++) { - fallbackName = anyFallbacks[i]; - subFontPtr = CanUseFallbackWithAliases(fontPtr, fallbackName, ch, - &faceNames, fixSubFontPtrPtr); - if (subFontPtr != NULL) { - goto end; - } - } /* - * Try all face names available in the whole system until we - * find one that can be used. + * 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. */ - for (mapPtr = gFontNameMap; mapPtr->utfName != NULL; mapPtr++) { - fallbackName = mapPtr->utfName; - if (SeenName(fallbackName, &faceNames) == 0) { - subFontPtr = CanUseFallback(fontPtr, fallbackName, ch, - fixSubFontPtrPtr); - if (subFontPtr != NULL) { - goto end; - } - } - } - - end: - Tcl_DStringFree(&faceNames); - - if (subFontPtr == NULL) { - /* - * No font can display this character. We will use the base font - * and have it display the "unknown" character. - */ +#ifdef TK_MAC_DEBUG + fprintf(stderr, "Font family '%s': Not found\n", name); +#endif - subFontPtr = &fontPtr->subFontArray[0]; - FontMapInsert(subFontPtr, ch); - } - return subFontPtr; + return NULL; } /* *------------------------------------------------------------------------- * - * FontMapLookup -- + * InitFontFamilies -- * - * See if the screen font can display the given character. + * Helper to TkpFontPkgInit. Use the Font Manager to fill in the + * familyList global array. * * Results: - * The return value is 0 if the screen font cannot display the - * character, non-zero otherwise. + * + * None. * * Side effects: - * New pages are added to the font mapping cache whenever the - * character belongs to a page that hasn't been seen before. - * When a page is loaded, information about all the characters on - * that page is stored, not just for the single character in - * question. + * + * Allocates memory. * *------------------------------------------------------------------------- */ -static int -FontMapLookup( - SubFont *subFontPtr, /* Contains font mapping cache to be queried - * and possibly updated. */ - int ch) /* Character to be tested. */ +static void +InitFontFamilies(void) +{ + OSStatus err; + + err = ATSFontFamilyApplyFunction(FontFamilyEnumCallback,NULL); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "ATSFontFamilyApplyFunction(): Error %d\n", (int) err); +#endif + } + + AddFontFamily(APPLFONT_NAME, GetAppFont()); + AddFontFamily(SYSTEMFONT_NAME, GetSysFont()); + + SortFontFamilies(); +} + +static OSStatus +FontFamilyEnumCallback( + ATSFontFamilyRef family, + void *refCon) { - int row, bitOffset; + OSStatus err; + char name[260] = ""; + + (void) refCon; - row = ch >> FONTMAP_SHIFT; - if (subFontPtr->fontMap[row] == NULL) { - FontMapLoadPage(subFontPtr, row); + err = GetFontFamilyName(family, name, sizeof(name)); + if (err == noErr) { + AddFontFamily(name, family); } - bitOffset = ch & (FONTMAP_BITSPERPAGE - 1); - return (subFontPtr->fontMap[row][bitOffset >> 3] >> (bitOffset & 7)) & 1; + + return noErr; } + /* *------------------------------------------------------------------------- * - * FontMapInsert -- + * GetFontFamilyName -- * - * Tell the font mapping cache that the given screen font should be - * used to display the specified character. This is called when no - * font on the system can be be found that can display that - * character; we lie to the font and tell it that it can display - * the character, otherwise we would end up re-searching the entire - * fallback hierarchy every time that character was seen. + * 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. Hint: The MacOSX font selector seems to use the localized + * family name given by ATSUFindFontName(kFontFamilyName, + * GetCurrentLanguage()). * * Results: - * None. + * + * An OS error code, noErr on success. name is filled with the + * resulting name. * * Side effects: - * New pages are added to the font mapping cache whenever the - * character belongs to a page that hasn't been seen before. - * When a page is loaded, information about all the characters on - * that page is stored, not just for the single character in - * question. + * + * None. * *------------------------------------------------------------------------- */ -static void -FontMapInsert( - SubFont *subFontPtr, /* Contains font mapping cache to be - * updated. */ - int ch) /* Character to be added to cache. */ +static OSStatus +GetFontFamilyName( + FMFontFamily fontFamily, /* The font family for which to find the + * name. */ + char * name, int numBytes) /* Filled with the result. */ { - int row, bitOffset; + OSStatus err; + Str255 nativeName; + CFStringRef cfString; + TextEncoding encoding; + ScriptCode nameencoding; + + nativeName[0] = 0; + name[0] = 0; + + err = FMGetFontFamilyName(fontFamily, nativeName); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "FMGetFontFamilyName(): Error %d\n", (int) err); +#endif + 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. + */ + + encoding = kTextEncodingMacRoman; + err = FMGetFontFamilyTextEncoding(fontFamily, &encoding); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "FMGetFontFamilyTextEncoding(): Error %d\n", (int) err); +#endif + } - row = ch >> FONTMAP_SHIFT; - if (subFontPtr->fontMap[row] == NULL) { - FontMapLoadPage(subFontPtr, row); + nameencoding = encoding; + err = RevertTextEncodingToScriptInfo(encoding, &nameencoding, NULL, NULL); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "RevertTextEncodingToScriptInfo(): Error %d\n", + (int) err); +#endif } - bitOffset = ch & (FONTMAP_BITSPERPAGE - 1); - subFontPtr->fontMap[row][bitOffset >> 3] |= 1 << (bitOffset & 7); + + /* + * 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(). + */ + + cfString = CFStringCreateWithPascalStringNoCopy( + NULL, nativeName, nameencoding, kCFAllocatorNull); + CFStringGetCString( + cfString, name, numBytes, kCFStringEncodingUTF8); + CFRelease(cfString); + + return noErr; } /* *------------------------------------------------------------------------- * - * FontMapLoadPage -- + * FindFontFamily -- * - * Load information about all the characters on a given page. - * This information consists of one bit per character that indicates - * whether the associated HFONT can (1) or cannot (0) display the - * characters on the page. + * 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: - * None. + * + * MacFontFamily: A pair of family id and the actual name registered for + * the font. * * Side effects: - * Mempry allocated. + * + * None. + * + * Assumption: + * + * Requires the familyList array to be sorted. * *------------------------------------------------------------------------- */ -static void -FontMapLoadPage( - SubFont *subFontPtr, /* Contains font mapping cache to be - * updated. */ - int row) /* Index of the page to be loaded into - * the cache. */ -{ - FMInput fm; - FMOutPtr fmOut; - int i, end, bitOffset, isMultiByteFont; - char src[TCL_UTF_MAX]; - unsigned char buf[16]; - int srcRead, dstWrote; - Tcl_Encoding encoding; - Handle fHandle = NULL; - - subFontPtr->fontMap[row] = (char *) ckalloc(FONTMAP_BITSPERPAGE / 8); - memset(subFontPtr->fontMap[row], 0, FONTMAP_BITSPERPAGE / 8); - - encoding = subFontPtr->familyPtr->encoding; - - fm.family = subFontPtr->familyPtr->faceNum; - fm.size = 12; - fm.face = 0; - fm.needBits = 0; - fm.device = 0; - fm.numer.h = 1; - fm.numer.v = 1; - fm.denom.h = 1; - fm.denom.v = 1; - /* -#if !defined(UNIVERSAL_INTERFACES_VERSION) || (UNIVERSAL_INTERFACES_VERSION < 0x0300) - fHandle = FMSwapFont(&fm)->fontHandle; -#else - fHandle = FMSwapFont(&fm)->fontHandle; -#endif -*/ -/* - * For some reason, FMSwapFont alywas returns a structure where the returned font handle - * is NULL. Until we figure this one out, assume all characters are allowed - */ - fmOut = FMSwapFont(&fm); - fHandle = fmOut->fontHandle; - isMultiByteFont = subFontPtr->familyPtr->isMultiByteFont; -#ifndef MAC_OSX_TK - GetResInfo(fHandle, &theID, &theType, theName); - fprintf ( stderr, "ResError() %d, %x\n", ResError (), fHandle ); - if (theType == 'sfnt') { -#endif - /* - * Found an outline font which has very complex font record. - * Let's just assume *ALL* the characters are allowed. - */ - end = (row + 1) << FONTMAP_SHIFT; - for (i = row << FONTMAP_SHIFT; i < end; i++) { - if (Tcl_UtfToExternal(NULL, encoding, src, Tcl_UniCharToUtf(i, - src), - TCL_ENCODING_STOPONERROR, NULL, (char *) buf, - sizeof(buf), - &srcRead, &dstWrote, NULL) == TCL_OK) { - bitOffset = i & (FONTMAP_BITSPERPAGE - 1); - subFontPtr->fontMap[row][bitOffset >> 3] |= 1 - << (bitOffset & 7); - } - } -#ifndef MAC_OSX_TK - } else { - /* - * Found an old bitmap font which has a well-defined record. - * We can check the width table to see which characters exist. - */ +static const MacFontFamily * +FindFontFamily( + const char * name) /* The family name. Note: Names are compared + * non-case-sensitive. */ +{ + const MacFontFamily key = {name,-1}; - fontRecPtr = *((FontRec **) fHandle ); - widths = (short *) ((long) &fontRecPtr->owTLoc - + ((long) (fontRecPtr->owTLoc - fontRecPtr->firstChar) - * sizeof(short))); - - end = (row + 1) << FONTMAP_SHIFT; - for (i = row << FONTMAP_SHIFT; i < end; i++) { - if (Tcl_UtfToExternal(NULL, encoding, src, - Tcl_UniCharToUtf(i, src), - TCL_ENCODING_STOPONERROR, NULL, (char *) buf, sizeof(buf), - &srcRead, &dstWrote, NULL) == TCL_OK) { - - if (((isMultiByteFont != 0) && (buf[0] > 31)) - || (widths[buf[0]] != -1)) { - if ((buf[0] == 0x11) && (widths[0x12] == -1)) { - continue; - } - - /* - * Mac's char existence metrics are only for one-byte - * characters. If we have a double-byte char, just - * assume that the font supports that char if the font's - * encoding supports that char. - */ - - bitOffset = i & (FONTMAP_BITSPERPAGE - 1); - subFontPtr->fontMap[row][bitOffset >> 3] |= 1 - << (bitOffset & 7); - } - } - } + if(familyListMaxValid <= 0) { + return NULL; } -#endif + + return bsearch( + &key, + familyList, familyListMaxValid, sizeof(*familyList), + CompareFontFamilies); } /* - *--------------------------------------------------------------------------- + *------------------------------------------------------------------------- * - * CanUseFallbackWithAliases -- + * EnumFontFamilies -- * - * Helper function for FindSubFontForChar. Determine if the - * specified face name (or an alias of the specified face name) - * can be used to construct a screen font that can display the - * given character. + * Create a Tcl list with the registered names in the global familyList. * * Results: - * See CanUseFallback(). + * + * A Tcl list of names. * * Side effects: - * If the name and/or one of its aliases was rejected, the - * rejected string is recorded in nameTriedPtr so that it won't - * be tried again. The table of SubFonts might be extended, and if - * a non-NULL reference to a subfont pointer is available, it is - * updated if it previously pointed into the old subfont table. * - *--------------------------------------------------------------------------- + * None. + * + *------------------------------------------------------------------------- */ -static SubFont * -CanUseFallbackWithAliases( - MacFont *fontPtr, /* The font object that will own the new - * screen font. */ - CONST char *faceName, /* Desired face name for new screen font. */ - int ch, /* The Unicode character that the new - * screen font must be able to display. */ - Tcl_DString *nameTriedPtr, /* Records face names that have already - * been tried. It is possible for the same - * face name to be queried multiple times when - * trying to find a suitable screen font. */ - SubFont **fixSubFontPtrPtr) /* Subfont reference to fix up if we - * reallocate our subfont table. */ +static Tcl_Obj * +EnumFontFamilies(void) { - SubFont *subFontPtr; - char **aliases; int i; - - if (SeenName(faceName, nameTriedPtr) == 0) { - subFontPtr = CanUseFallback(fontPtr, faceName, ch, fixSubFontPtrPtr); - if (subFontPtr != NULL) { - return subFontPtr; - } - } - aliases = TkFontGetAliasList(faceName); - if (aliases != NULL) { - for (i = 0; aliases[i] != NULL; i++) { - if (SeenName(aliases[i], nameTriedPtr) == 0) { - subFontPtr = CanUseFallback(fontPtr, aliases[i], ch, - fixSubFontPtrPtr); - if (subFontPtr != NULL) { - return subFontPtr; - } - } - } + Tcl_Obj * tclList; + + tclList = Tcl_NewListObj(0, NULL); + for (i=0; i= familyListSize) { + familyListSize += 100; + familyList = (MacFontFamily *) ckrealloc( + (void*) familyList, + familyListSize * sizeof(*familyList)); } - Tcl_DStringAppend(dsPtr, (char *) name, (int) (strlen(name) + 1)); - return 0; + + familyPtr = familyList + familyListNextFree; + ++familyListNextFree; + + familyPtr->name = AddString(name); + familyPtr->familyId = familyId; + + return familyPtr; } /* *------------------------------------------------------------------------- * - * CanUseFallback -- + * SortFontFamilies -- * - * If the specified physical screen font has not already been loaded - * into the font object, determine if the specified physical screen - * font can display the given character. + * 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: - * The return value is a pointer to a newly allocated SubFont, owned - * by the font object. This SubFont can be used to display the given - * character. The SubFont represents the screen font with the base set - * of font attributes from the font object, but using the specified - * font name. NULL is returned if the font object already holds - * a reference to the specified physical font or if the specified - * physical font cannot display the given character. - * - * Side effects: - * The font object's subFontArray is updated to contain a reference - * to the newly allocated SubFont. The table of SubFonts might be - * extended, and if a non-NULL reference to a subfont pointer is - * available, it is updated if it previously pointed into the old - * subfont table. + * + * None. + * + * Side effects: + * + * familyList is sorted and familyListMaxValid is updated. * *------------------------------------------------------------------------- */ -static SubFont * -CanUseFallback( - MacFont *fontPtr, /* The font object that will own the new - * screen font. */ - CONST char *faceName, /* Desired face name for new screen font. */ - int ch, /* The Unicode character that the new - * screen font must be able to display. */ - SubFont **fixSubFontPtrPtr) /* Subfont reference to fix up if we - * reallocate our subfont table. */ +static void +SortFontFamilies(void) { - int i; - SubFont subFont; - short faceNum; - - if (GetFamilyNum(faceName, &faceNum) == 0) { - return NULL; + if (familyListNextFree > 0) { + qsort( familyList, familyListNextFree, sizeof(*familyList), + CompareFontFamilies); } - - /* - * Skip all fonts we've already used. - */ - - for (i = 0; i < fontPtr->numSubFonts; i++) { - if (faceNum == fontPtr->subFontArray[i].familyPtr->faceNum) { - return NULL; - } - } - - /* - * Load this font and see if it has the desired character. - */ - - InitSubFont(fontPtr, faceNum, &subFont); - if (((ch < 256) && (subFont.familyPtr->isSymbolFont)) - || (FontMapLookup(&subFont, ch) == 0)) { - ReleaseSubFont(&subFont); - return NULL; - } - - if (fontPtr->numSubFonts >= SUBFONT_SPACE) { - SubFont *newPtr; - newPtr = (SubFont *) ckalloc(sizeof(SubFont) - * (fontPtr->numSubFonts + 1)); - memcpy((char *) newPtr, fontPtr->subFontArray, - fontPtr->numSubFonts * sizeof(SubFont)); - if (fixSubFontPtrPtr != NULL) { - /* - * Fix up the variable pointed to by fixSubFontPtrPtr so it - * still points into the live array. [Bug 618872] - */ - *fixSubFontPtrPtr = - newPtr + (*fixSubFontPtrPtr - fontPtr->subFontArray); - } - if (fontPtr->subFontArray != fontPtr->staticSubFonts) { - ckfree((char *) fontPtr->subFontArray); - } - fontPtr->subFontArray = newPtr; - } - fontPtr->subFontArray[fontPtr->numSubFonts] = subFont; - fontPtr->numSubFonts++; - return &fontPtr->subFontArray[fontPtr->numSubFonts - 1]; + familyListMaxValid = familyListNextFree; } /* *------------------------------------------------------------------------- * - * GetFamilyNum -- + * CompareFontFamilies -- * - * 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. + * Comparison function used by SortFontFamilies() and FindFontFamily(). * * 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. + * + * 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. + * + * None. * *------------------------------------------------------------------------- */ static int -GetFamilyNum( - CONST char *faceName, /* UTF-8 name of font family to query. */ - short *faceNumPtr) /* Filled with font number for above family. */ +CompareFontFamilies( + const void * vp1, + const void * vp2) { - 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; -} + const char * name1; + const char * name2; + int len1; + int len2; + int diff; -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; + 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, len1utfName != NULL; mapPtr++) { - if (pstrcmp(nativeName, mapPtr->nativeName) == 0) { - return mapPtr->utfName; - } + len = strlen(in) +1; + + if (stringMemory == NULL + || (stringMemory->nextFree+len) > STRING_BLOCK_MAX ) { + StringBlock * newblock = + (StringBlock *) ckalloc(sizeof(StringBlock)); + newblock->next = stringMemory; + newblock->nextFree = 0; + stringMemory = newblock; } - Tcl_Panic("GetUtfFaceName: unexpected nativeName"); - return NULL; + + result = stringMemory->strings + stringMemory->nextFree; + stringMemory->nextFree += len; + + memcpy(result, in, len); + + return result; } /* - *------------------------------------------------------------------------ - * - * GetFontEncoding -- + *--------------------------------------------------------------------------- * - * Return a string that can be passed to Tcl_GetTextEncoding() and - * used to convert bytes from UTF-8 into the encoding of the - * specified font. + * TkMacOSXIsCharacterMissing -- * - * The desired encoding to use to convert the name of a symbolic - * font into UTF-8 is macRoman, while the desired encoding to use - * to convert bytes in a symbolic font to UTF-8 is the corresponding - * symbolic encoding. Due to this dual interpretatation of symbolic - * fonts, the caller can specify what type of encoding to return - * should the specified font be symbolic. + * Given a tkFont and a character determine whether the character has + * a glyph defined in the font or not. * * Results: - * The return value is a string that specifies the font's encoding. - * If the font's encoding could not be identified, NULL is returned. + * Returns a 1 if the character is missing, a 0 if it is not. * * Side effects: * None. * - *------------------------------------------------------------------------ + *--------------------------------------------------------------------------- */ - -static Tcl_Encoding -GetFontEncoding( - int faceNum, /* Macintosh font number. */ - int allowSymbol, /* If non-zero, then the encoding string - * for symbol fonts will be the corresponding - * symbol encoding. Otherwise, the encoding - * string for symbol fonts will be - * "macRoman". */ - int *isSymbolPtr) /* Filled with non-zero if this font is a - * symbol font, 0 otherwise. */ + +int +TkMacOSXIsCharacterMissing( + Tk_Font tkfont, /* The font we are looking in. */ + unsigned int searchChar) /* The character we are looking for. */ { - Str255 faceName; - int script, lang; - char *name; - - if (allowSymbol != 0) { - GetFontName(faceNum, faceName); - if (pstrcasecmp(faceName, "\psymbol") == 0) { - *isSymbolPtr = 1; - return Tcl_GetEncoding(NULL, "symbol"); - } - if (pstrcasecmp(faceName, "\pzapf dingbats") == 0) { - *isSymbolPtr = 1; - return Tcl_GetEncoding(NULL, "macDingbats"); - } - } - *isSymbolPtr = 0; - script = FontToScript(faceNum); - lang = GetScriptVariable(script, smScriptLang); - name = NULL; - if (script == smRoman) { - name = TkFindStateString(romanMap, lang); - } else if (script == smCyrillic) { - name = TkFindStateString(cyrillicMap, lang); - } - if (name == NULL) { - name = TkFindStateString(scriptMap, script); - } - return Tcl_GetEncoding(NULL, name); + /* 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; } /* @@ -2202,20 +2650,20 @@ GetFontEncoding( */ void -TkMacOSXInitControlFontStyle(Tk_Font tkfont, ControlFontStylePtr fsPtr) +TkMacOSXInitControlFontStyle( + Tk_Font tkfont, /* Tk font object to use for the control. */ + ControlFontStylePtr fsPtr) /* The style object to configure. */ { - MacFont *fontPtr; - FontFamily *lastFamilyPtr; + const MacFont * fontPtr; fontPtr = (MacFont *) tkfont; - lastFamilyPtr = fontPtr->subFontArray[0].familyPtr; fsPtr->flags = kControlUseFontMask| kControlUseSizeMask| kControlUseFaceMask| kControlUseJustMask; - fsPtr->font = lastFamilyPtr->faceNum; - fsPtr->size = fontPtr->size; - fsPtr->style = fontPtr->style; + fsPtr->font = fontPtr->qdFont; + fsPtr->size = fontPtr->qdSize; + fsPtr->style = fontPtr->qdStyle; fsPtr->just = teCenter; } @@ -2224,77 +2672,74 @@ TkMacOSXInitControlFontStyle(Tk_Font tkfont, ControlFontStylePtr fsPtr) * * 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. + * Enables or disables application-wide use of antialiased text (where + * available). Sets up a linked Tcl global boolean variable with write + * trace to allow disabling of antialiased text from tcl. The possible + * values for this variable are: + * + * -1 - Use system default as configurable in "System Preferences" -> + * "General". + * 0 - Unconditionally disable antialiasing. + * 1 - Unconditionally enable antialiasing. + * + * This function is also called once from tkMacOSXInit.c:TkpInit() to + * install the trace in an interpreter. The value given in TkpInit() + * determines the actual default value. + * + * FIXME: Initialization should be a separate function, so that the + * default given here (see variable declaration above) remains the real + * default. * * Results: - * TCL_OK if facility was sucessfully enabled/disabled. - * TCL_ERROR if an error occurred or if facility is not available. + * + * TCL_OK. * * 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; -} - MODULE_SCOPE int -TkMacOSXUseAntialiasedText(interp, enable) - Tcl_Interp *interp; - int enable; +TkMacOSXUseAntialiasedText( + Tcl_Interp * interp, /* The Tcl interpreter to receive the + * variable .*/ + int enable) /* Initial value. */ { 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", + 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) { + (char *) &TkMacOSXAntialiasedTextEnabled, + TCL_LINK_INT) != TCL_OK) { Tcl_ResetResult(interp); } } - if (swaptextflags) { - swaptextflags(enable ? kQDUseCGTextRendering | kQDUseCGTextMetrics - : kQDUseTrueTypeScalerGlyphs); - TkMacOSXAntialiasedTextEnabled = enable; - return TCL_OK; - } else { - TkMacOSXAntialiasedTextEnabled = FALSE; - return TCL_ERROR; - } + + TkMacOSXAntialiasedTextEnabled = enable; + + return TCL_OK; +} + +static char * +TkMacOSXAntialiasedTextVariableProc( + ClientData clientData, + Tcl_Interp * interp, + const char * name1, + const char * name2, + int flags) +{ + TkMacOSXUseAntialiasedText(interp, TkMacOSXAntialiasedTextEnabled); + return NULL; } diff --git a/macosx/tkMacOSXFont.h b/macosx/tkMacOSXFont.h new file mode 100644 index 0000000..ad16185 --- /dev/null +++ b/macosx/tkMacOSXFont.h @@ -0,0 +1,113 @@ +/* + * tkMacOSXFont.h -- + * + * Private functions and structs exported from tkMacOSXFont.c + * for use in ATSU specific extensions. + * + * Copyright 2002-2004 Benjamin Riefenstahl, Benjamin.Riefenstahl@epost.de + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tkMacOSXFont.h,v 1.1 2006/03/22 00:21:18 das Exp $ + */ + +#ifndef TKMACOSXFONT_H +#define TKMACOSXFONT_H 1 + +#include "tkFont.h" + +/* + * Switches + */ + +#define TK_MAC_USE_QUARZ 1 + +/* + * Types + */ + +/* + * 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. */ +} TkMacOSXFont; + + +#if TK_MAC_USE_QUARZ + +/* + * To use Quarz drawing we need some additional context. FIXME: We would + * have liked to use the similar functions from tkMacOSXDraw.c to do this + * (TkMacOSXSetUpCGContext(), etc), but a) those don't quite work for us + * (e.g. we can't use a simple upside-down coordinate system transformation, + * as we don't want upside-down characters ;-), and b) we don't have the + * necessary context information (MacDrawable), that we need as parameter for + * those functions. So I just cobbled together a limited edition, getting + * the necessary parameters from the current QD GraphPort. + */ + +typedef struct { + CGContextRef cgContext; /* Quarz context. */ + CGrafPtr graphPort; /* QD graph port to which this belongs. + * Needed for releasing cgContext. */ + Rect portRect; /* Cached size of port. */ +} TkMacOSXFontDrawingContext; + +#else /* ! TK_MAC_USE_QUARZ */ + +/* + * Just a dummy, so we don't have to #ifdef the parameter lists of functions + * that use this. + */ + +typedef struct {} DrawingContext; + +#endif /* ? TK_MAC_USE_QUARZ */ + + +/* + * Function prototypes + */ + +MODULE_SCOPE void TkMacOSXLayoutSetString(const TkMacOSXFont * fontPtr, + const TkMacOSXFontDrawingContext *drawingContextPtr, + const UniChar * uchars, int ulen); +MODULE_SCOPE void TkMacOSXInitControlFontStyle(Tk_Font tkfont, + ControlFontStylePtr fsPtr); + +#if TK_MAC_USE_QUARZ +MODULE_SCOPE void TkMacOSXQuarzStartDraw( + TkMacOSXFontDrawingContext * contextPtr); +MODULE_SCOPE void TkMacOSXQuarzEndDraw( + TkMacOSXFontDrawingContext * contextPtr); +#endif /* TK_MAC_USE_QUARZ */ + + +#endif /*TKMACOSXFONT_H*/ + diff --git a/macosx/tkMacOSXMenubutton.c b/macosx/tkMacOSXMenubutton.c index f6ed8b9..ef1ec4e 100644 --- a/macosx/tkMacOSXMenubutton.c +++ b/macosx/tkMacOSXMenubutton.c @@ -10,13 +10,14 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkMacOSXMenubutton.c,v 1.8 2005/11/27 06:47:25 das Exp $ + * RCS: @(#) $Id: tkMacOSXMenubutton.c,v 1.9 2006/03/22 00:21:18 das Exp $ */ #include #include "tkMenu.h" #include "tkMenubutton.h" #include "tkMacOSXInt.h" +#include "tkMacOSXFont.h" #include "tkMacOSXDebug.h" #if !defined(MAC_OS_X_VERSION_10_3) || \ @@ -90,9 +91,6 @@ static void CompareControlTitleParams( int * styleChanged ); -MODULE_SCOPE int TkFontGetFirstTextLayout(Tk_TextLayout layout, Tk_Font * font, char * dst); -MODULE_SCOPE void TkMacOSXInitControlFontStyle(Tk_Font tkfont,ControlFontStylePtr fsPtr); - /* * The structure below defines menubutton class behavior by means of * procedures that can be invoked from generic window code. diff --git a/tests/font.test b/tests/font.test index c0d5fcf..e573bc9 100644 --- a/tests/font.test +++ b/tests/font.test @@ -6,7 +6,7 @@ # Copyright (c) 1998-1999 by Scriptics Corporation. # All rights reserved. # -# RCS: @(#) $Id: font.test,v 1.12 2004/06/24 12:45:42 dkf Exp $ +# RCS: @(#) $Id: font.test,v 1.13 2006/03/22 00:21:18 das Exp $ package require tcltest 2.1 eval tcltest::configure $argv @@ -48,10 +48,14 @@ proc csetup {{str ""}} { setup -case $tcl_platform(platform) { - unix {set fixed "fixed"} - windows {set fixed "courier 12"} +case [tk windowingsystem] { + x11 {set fixed "fixed"} + win32 {set fixed "courier 12"} + classic - + aqua {set fixed "monaco 9"} } + + set times [font actual {times 0} -family] test font-1.1 {TkFontPkgInit} { diff --git a/unix/configure b/unix/configure index 3f644d0..9a3a53f 100755 --- a/unix/configure +++ b/unix/configure @@ -1339,7 +1339,7 @@ TK_MAJOR_VERSION=8 TK_MINOR_VERSION=5 TK_PATCH_LEVEL="a4" VERSION=${TK_VERSION} -LOCALES="cs de el en en_gb es fr it nl ru" +LOCALES="cs de el en en_gb eo es es_ES fr it nl pl pt ru" #-------------------------------------------------------------------- # Find and load the tclConfig.sh file @@ -8744,7 +8744,7 @@ _ACEOF LIBS="$LIBS -framework Carbon -framework IOKit" CFLAGS="$CFLAGS -fpascal-strings" TK_WINDOWINGSYSTEM=AQUA - if test "${enable_symbols+set}" = set; then + if test "${enable_symbols}" = yes; then cat >>confdefs.h <<\_ACEOF #define TK_MAC_DEBUG 1 @@ -9900,7 +9900,7 @@ echo "${ECHO_T}standard shared library" >&6 fi TK_SHLIB_LD_EXTRAS="-compatibility_version ${TK_VERSION} -current_version ${TK_VERSION}`echo ${TK_PATCH_LEVEL} | awk '{match($0, "\\\.[0-9]+"); print substr($0,RSTART,RLENGTH)}'`" - TK_SHLIB_LD_EXTRAS="${TK_SHLIB_LD_EXTRAS}"' -install_name ${DYLIB_INSTALL_DIR}/${TK_LIB_FILE} -seg1addr 0xb000000 -unexported_symbols_list $$(f=$(TCL_STUB_LIB_FILE).E && nm -gjp $(TCL_BIN_DIR)/$(TCL_STUB_LIB_FILE) | grep ^_ > $$f && echo $$f)' + TK_SHLIB_LD_EXTRAS="${TK_SHLIB_LD_EXTRAS}"' -install_name ${DYLIB_INSTALL_DIR}/${TK_LIB_FILE} -seg1addr 0xb000000 -unexported_symbols_list $$(f=$(TCL_STUB_LIB_FILE).E && nm -gjp $(TCL_BIN_DIR)/$(TCL_STUB_LIB_FILE) | grep ^_[^_] > $$f && echo $$f)' fi if test "$FRAMEWORK_BUILD" = "1" ; then diff --git a/unix/configure.in b/unix/configure.in index e073821..e7b77a0 100644 --- a/unix/configure.in +++ b/unix/configure.in @@ -3,7 +3,7 @@ dnl This file is an input file used by the GNU "autoconf" program to dnl generate the file "configure", which is run during Tk installation dnl to configure the system for the local environment. # -# RCS: @(#) $Id: configure.in,v 1.117 2006/03/16 13:59:12 dkf Exp $ +# RCS: @(#) $Id: configure.in,v 1.118 2006/03/22 00:21:19 das Exp $ AC_INIT([tk],[8.5]) AC_PREREQ(2.59) @@ -264,7 +264,7 @@ if test $tk_aqua = yes; then LIBS="$LIBS -framework Carbon -framework IOKit" CFLAGS="$CFLAGS -fpascal-strings" TK_WINDOWINGSYSTEM=AQUA - if test "${enable_symbols+set}" = set; then + if test "${enable_symbols}" = yes; then AC_DEFINE(TK_MAC_DEBUG, 1, [Are TkAqua debug messages enabled?]) fi else @@ -476,7 +476,7 @@ WISH_RSRC_FILE='wish$(VERSION).rsrc' if test "`uname -s`" = "Darwin" ; then SC_ENABLE_FRAMEWORK TK_SHLIB_LD_EXTRAS="-compatibility_version ${TK_VERSION} -current_version ${TK_VERSION}`echo ${TK_PATCH_LEVEL} | awk ['{match($0, "\\\.[0-9]+"); print substr($0,RSTART,RLENGTH)}']`" - TK_SHLIB_LD_EXTRAS="${TK_SHLIB_LD_EXTRAS}"' -install_name ${DYLIB_INSTALL_DIR}/${TK_LIB_FILE} -seg1addr 0xb000000 -unexported_symbols_list $$(f=$(TCL_STUB_LIB_FILE).E && nm -gjp $(TCL_BIN_DIR)/$(TCL_STUB_LIB_FILE) | grep ^_ > $$f && echo $$f)' + TK_SHLIB_LD_EXTRAS="${TK_SHLIB_LD_EXTRAS}"' -install_name ${DYLIB_INSTALL_DIR}/${TK_LIB_FILE} -seg1addr 0xb000000 -unexported_symbols_list $$(f=$(TCL_STUB_LIB_FILE).E && nm -gjp $(TCL_BIN_DIR)/$(TCL_STUB_LIB_FILE) | grep ^_[[^_]] > $$f && echo $$f)' fi if test "$FRAMEWORK_BUILD" = "1" ; then diff --git a/unix/tkUnixFont.c b/unix/tkUnixFont.c index 4caa7ea..e4718d8 100644 --- a/unix/tkUnixFont.c +++ b/unix/tkUnixFont.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkUnixFont.c,v 1.26 2006/02/07 11:20:01 dkf Exp $ + * RCS: @(#) $Id: tkUnixFont.c,v 1.27 2006/03/22 00:21:19 das Exp $ */ #include "tkUnixInt.h" @@ -1115,6 +1115,65 @@ Tk_MeasureChars( /* *--------------------------------------------------------------------------- * + * 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. On X11 this context + * isn't consulted, so we just call Tk_MeasureChars(). + * + * 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(tkfont, source, numBytes, rangeStart, rangeLength, + maxLength, flags, lengthPtr) + 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 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. + * 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. */ +{ + (void) numBytes; /*unused*/ + return Tk_MeasureChars(tkfont, source + rangeStart, rangeLength, + maxLength, flags, lengthPtr); +} + +/* + *--------------------------------------------------------------------------- + * * Tk_DrawChars -- * * Draw a string of characters on the screen. Tk_DrawChars() expands @@ -1249,9 +1308,51 @@ Tk_DrawChars( (unsigned) (x - xStart), (unsigned) fontPtr->barHeight); } } + +/* + *--------------------------------------------------------------------------- + * + * TkpDrawCharsInContext -- + * + * Draw a string of characters on the screen like Tk_DrawChars(), but + * with access to all the characters on the line for context. On X11 + * this context isn't consulted, so we just call Tk_DrawChars(). + * + * Results: + * None. + * + * Side effects: + * Information gets drawn on the screen. + * + *--------------------------------------------------------------------------- + */ - - +void +TkpDrawCharsInContext(display, drawable, gc, tkfont, source, numBytes, + rangeStart, rangeLength, x, y) + 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. */ +{ + (void) numBytes; /*unused*/ + Tk_DrawChars(display, drawable, gc, tkfont, + source + rangeStart, rangeLength, x, y); +} /* *------------------------------------------------------------------------- diff --git a/win/tkWinFont.c b/win/tkWinFont.c index 35619c7..bddaed9 100644 --- a/win/tkWinFont.c +++ b/win/tkWinFont.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkWinFont.c,v 1.26 2005/12/02 00:19:04 dkf Exp $ + * RCS: @(#) $Id: tkWinFont.c,v 1.27 2006/03/22 00:21:20 das Exp $ */ #include "tkWinInt.h" @@ -787,6 +787,64 @@ Tk_MeasureChars( /* *--------------------------------------------------------------------------- * + * 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. On Windows this context + * isn't consulted, so we just call Tk_MeasureChars(). + * + * 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 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. + * 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. */ +{ + (void) numBytes; /*unused*/ + return Tk_MeasureChars(tkfont, source + rangeStart, rangeLength, + maxLength, flags, lengthPtr); +} + +/* + *--------------------------------------------------------------------------- + * * Tk_DrawChars -- * * Draw a string of characters on the screen. @@ -947,6 +1005,50 @@ Tk_DrawChars( } /* + *--------------------------------------------------------------------------- + * + * TkpDrawCharsInContext -- + * + * Draw a string of characters on the screen like Tk_DrawChars(), but + * with access to all the characters on the line for context. On + * Windows this context isn't consulted, so we just call Tk_DrawChars(). + * + * Results: + * None. + * + * Side effects: + * Information gets drawn on the screen. + * + *--------------------------------------------------------------------------- + */ + +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. */ +{ + (void) numBytes; /*unused*/ + Tk_DrawChars(display, drawable, gc, tkfont, + source + rangeStart, rangeLength, x, y); +} + +/* *------------------------------------------------------------------------- * * MultiFontTextOut -- -- cgit v0.12