summaryrefslogtreecommitdiffstats
path: root/tk8.6/unix/tkUnixRFont.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2016-10-18 17:31:55 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2016-10-18 17:31:55 (GMT)
commit39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb (patch)
tree8e5374666c7f0b3017176ec9d6e6b6eae0dcabac /tk8.6/unix/tkUnixRFont.c
parent066971b1e6e77991d9161bb0216a63ba94ea04f9 (diff)
parent6b095f3c8521ca7215e6ff5dcbada52b197ef7d0 (diff)
downloadblt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.zip
blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.tar.gz
blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.tar.bz2
Merge commit '6b095f3c8521ca7215e6ff5dcbada52b197ef7d0' as 'tk8.6'
Diffstat (limited to 'tk8.6/unix/tkUnixRFont.c')
-rw-r--r--tk8.6/unix/tkUnixRFont.c1202
1 files changed, 1202 insertions, 0 deletions
diff --git a/tk8.6/unix/tkUnixRFont.c b/tk8.6/unix/tkUnixRFont.c
new file mode 100644
index 0000000..36e5462
--- /dev/null
+++ b/tk8.6/unix/tkUnixRFont.c
@@ -0,0 +1,1202 @@
+/*
+ * tkUnixRFont.c --
+ *
+ * Alternate implementation of tkUnixFont.c using Xft.
+ *
+ * Copyright (c) 2002-2003 Keith Packard
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "tkUnixInt.h"
+#include "tkFont.h"
+#include <X11/Xft/Xft.h>
+#include <ctype.h>
+
+#define MAX_CACHED_COLORS 16
+
+typedef struct {
+ XftFont *ftFont;
+ XftFont *ft0Font;
+ FcPattern *source;
+ FcCharSet *charset;
+ double angle;
+} UnixFtFace;
+
+typedef struct {
+ XftColor color;
+ int next;
+} UnixFtColorList;
+
+typedef struct {
+ TkFont font; /* Stuff used by generic font package. Must be
+ * first in structure. */
+ UnixFtFace *faces;
+ int nfaces;
+ FcFontSet *fontset;
+ FcPattern *pattern;
+
+ Display *display;
+ int screen;
+ XftDraw *ftDraw;
+ int ncolors;
+ int firstColor;
+ UnixFtColorList colors[MAX_CACHED_COLORS];
+} UnixFtFont;
+
+/*
+ * Used to describe the current clipping box. Can't be passed normally because
+ * the information isn't retrievable from the GC.
+ */
+
+typedef struct ThreadSpecificData {
+ Region clipRegion; /* The clipping region, or None. */
+} ThreadSpecificData;
+static Tcl_ThreadDataKey dataKey;
+
+/*
+ * Package initialization:
+ * Nothing to do here except register the fact that we're using Xft in
+ * the TIP 59 configuration database.
+ */
+
+#ifndef TCL_CFGVAL_ENCODING
+#define TCL_CFGVAL_ENCODING "ascii"
+#endif
+
+void
+TkpFontPkgInit(
+ TkMainInfo *mainPtr) /* The application being created. */
+{
+ static Tcl_Config cfg[] = {
+ { "fontsystem", "xft" },
+ { 0,0 }
+ };
+
+ Tcl_RegisterConfig(mainPtr->interp, "tk", cfg, TCL_CFGVAL_ENCODING);
+}
+
+static XftFont *
+GetFont(
+ UnixFtFont *fontPtr,
+ FcChar32 ucs4,
+ double angle)
+{
+ int i;
+
+ if (ucs4) {
+ for (i = 0; i < fontPtr->nfaces; i++) {
+ FcCharSet *charset = fontPtr->faces[i].charset;
+
+ if (charset && FcCharSetHasChar(charset, ucs4)) {
+ break;
+ }
+ }
+ if (i == fontPtr->nfaces) {
+ i = 0;
+ }
+ } else {
+ i = 0;
+ }
+ if ((angle == 0.0 && !fontPtr->faces[i].ft0Font) || (angle != 0.0 &&
+ (!fontPtr->faces[i].ftFont || fontPtr->faces[i].angle != angle))){
+ FcPattern *pat = FcFontRenderPrepare(0, fontPtr->pattern,
+ fontPtr->faces[i].source);
+ double s = sin(angle*PI/180.0), c = cos(angle*PI/180.0);
+ FcMatrix mat;
+ XftFont *ftFont;
+
+ /*
+ * Initialize the matrix manually so this can compile with HP-UX cc
+ * (which does not allow non-constant structure initializers). [Bug
+ * 2978410]
+ */
+
+ mat.xx = mat.yy = c;
+ mat.xy = -(mat.yx = s);
+
+ if (angle != 0.0) {
+ FcPatternAddMatrix(pat, FC_MATRIX, &mat);
+ }
+ ftFont = XftFontOpenPattern(fontPtr->display, pat);
+ if (!ftFont) {
+ /*
+ * The previous call to XftFontOpenPattern() should not fail, but
+ * sometimes does anyway. Usual cause appears to be a
+ * misconfigured fontconfig installation; see [Bug 1090382]. Try a
+ * fallback:
+ */
+
+ ftFont = XftFontOpen(fontPtr->display, fontPtr->screen,
+ FC_FAMILY, FcTypeString, "sans",
+ FC_SIZE, FcTypeDouble, 12.0,
+ FC_MATRIX, FcTypeMatrix, &mat,
+ NULL);
+ }
+ if (!ftFont) {
+ /*
+ * The previous call should definitely not fail. Impossible to
+ * proceed at this point.
+ */
+
+ Tcl_Panic("Cannot find a usable font");
+ }
+
+ if (angle == 0.0) {
+ fontPtr->faces[i].ft0Font = ftFont;
+ } else {
+ if (fontPtr->faces[i].ftFont) {
+ XftFontClose(fontPtr->display, fontPtr->faces[i].ftFont);
+ }
+ fontPtr->faces[i].ftFont = ftFont;
+ fontPtr->faces[i].angle = angle;
+ }
+ }
+ return (angle==0.0? fontPtr->faces[i].ft0Font : fontPtr->faces[i].ftFont);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetTkFontAttributes --
+ * Fill in TkFontAttributes from an XftFont.
+ */
+
+static void
+GetTkFontAttributes(
+ XftFont *ftFont,
+ TkFontAttributes *faPtr)
+{
+ const char *family = "Unknown";
+ const char *const *familyPtr = &family;
+ int weight, slant, size, pxsize;
+ double ptsize;
+
+ (void) XftPatternGetString(ftFont->pattern, XFT_FAMILY, 0, familyPtr);
+ if (XftPatternGetDouble(ftFont->pattern, XFT_SIZE, 0,
+ &ptsize) == XftResultMatch) {
+ size = (int) ptsize;
+ } else if (XftPatternGetInteger(ftFont->pattern, XFT_PIXEL_SIZE, 0,
+ &pxsize) == XftResultMatch) {
+ size = -pxsize;
+ } else {
+ size = 12;
+ }
+ if (XftPatternGetInteger(ftFont->pattern, XFT_WEIGHT, 0,
+ &weight) != XftResultMatch) {
+ weight = XFT_WEIGHT_MEDIUM;
+ }
+ if (XftPatternGetInteger(ftFont->pattern, XFT_SLANT, 0,
+ &slant) != XftResultMatch) {
+ slant = XFT_SLANT_ROMAN;
+ }
+
+#if DEBUG_FONTSEL
+ printf("family %s size %d weight %d slant %d\n",
+ family, size, weight, slant);
+#endif /* DEBUG_FONTSEL */
+
+ faPtr->family = Tk_GetUid(family);
+ faPtr->size = size;
+ faPtr->weight = (weight > XFT_WEIGHT_MEDIUM) ? TK_FW_BOLD : TK_FW_NORMAL;
+ faPtr->slant = (slant > XFT_SLANT_ROMAN) ? TK_FS_ITALIC : TK_FS_ROMAN;
+ faPtr->underline = 0;
+ faPtr->overstrike = 0;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetTkFontMetrics --
+ * Fill in TkFontMetrics from an XftFont.
+ */
+
+static void
+GetTkFontMetrics(
+ XftFont *ftFont,
+ TkFontMetrics *fmPtr)
+{
+ int spacing;
+
+ if (XftPatternGetInteger(ftFont->pattern, XFT_SPACING, 0,
+ &spacing) != XftResultMatch) {
+ spacing = XFT_PROPORTIONAL;
+ }
+
+ fmPtr->ascent = ftFont->ascent;
+ fmPtr->descent = ftFont->descent;
+ fmPtr->maxWidth = ftFont->max_advance_width;
+ fmPtr->fixed = spacing != XFT_PROPORTIONAL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * InitFont --
+ *
+ * Initializes the fields of a UnixFtFont structure. If fontPtr is NULL,
+ * also allocates a new UnixFtFont.
+ *
+ * Results:
+ * On error, frees fontPtr and returns NULL, otherwise returns fontPtr.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static UnixFtFont *
+InitFont(
+ Tk_Window tkwin,
+ FcPattern *pattern,
+ UnixFtFont *fontPtr)
+{
+ FcFontSet *set;
+ FcCharSet *charset;
+ FcResult result;
+ XftFont *ftFont;
+ int i, iWidth;
+
+ if (!fontPtr) {
+ fontPtr = ckalloc(sizeof(UnixFtFont));
+ }
+
+ FcConfigSubstitute(0, pattern, FcMatchPattern);
+ XftDefaultSubstitute(Tk_Display(tkwin), Tk_ScreenNumber(tkwin), pattern);
+
+ /*
+ * Generate the list of fonts
+ */
+
+ set = FcFontSort(0, pattern, FcTrue, NULL, &result);
+ if (!set) {
+ ckfree(fontPtr);
+ return NULL;
+ }
+
+ fontPtr->fontset = set;
+ fontPtr->pattern = pattern;
+ fontPtr->faces = ckalloc(set->nfont * sizeof(UnixFtFace));
+ fontPtr->nfaces = set->nfont;
+
+ /*
+ * Fill in information about each returned font
+ */
+
+ for (i = 0; i < set->nfont; i++) {
+ fontPtr->faces[i].ftFont = 0;
+ fontPtr->faces[i].ft0Font = 0;
+ fontPtr->faces[i].source = set->fonts[i];
+ if (FcPatternGetCharSet(set->fonts[i], FC_CHARSET, 0,
+ &charset) == FcResultMatch) {
+ fontPtr->faces[i].charset = FcCharSetCopy(charset);
+ } else {
+ fontPtr->faces[i].charset = 0;
+ }
+ fontPtr->faces[i].angle = 0.0;
+ }
+
+ fontPtr->display = Tk_Display(tkwin);
+ fontPtr->screen = Tk_ScreenNumber(tkwin);
+ fontPtr->ftDraw = 0;
+ fontPtr->ncolors = 0;
+ fontPtr->firstColor = -1;
+
+ /*
+ * Fill in platform-specific fields of TkFont.
+ */
+
+ ftFont = GetFont(fontPtr, 0, 0.0);
+ fontPtr->font.fid = XLoadFont(Tk_Display(tkwin), "fixed");
+ GetTkFontAttributes(ftFont, &fontPtr->font.fa);
+ GetTkFontMetrics(ftFont, &fontPtr->font.fm);
+
+ /*
+ * Fontconfig can't report any information about the position or thickness
+ * of underlines or overstrikes. Thus, we use some defaults that are
+ * hacked around from backup defaults in tkUnixFont.c, which are in turn
+ * based on recommendations in the X manual. The comments from that file
+ * leading to these computations were:
+ *
+ * If the XA_UNDERLINE_POSITION property does not exist, the X manual
+ * recommends using half the descent.
+ *
+ * If the XA_UNDERLINE_THICKNESS property does not exist, the X
+ * manual recommends using the width of the stem on a capital letter.
+ * I don't know of a way to get the stem width of a letter, so guess
+ * and use 1/3 the width of a capital I.
+ *
+ * Note that nothing corresponding to *either* property is reported by
+ * Fontconfig at all. [Bug 1961455]
+ */
+
+ {
+ TkFont *fPtr = &fontPtr->font;
+
+ fPtr->underlinePos = fPtr->fm.descent / 2;
+ Tk_MeasureChars((Tk_Font) fPtr, "I", 1, -1, 0, &iWidth);
+ fPtr->underlineHeight = iWidth / 3;
+ if (fPtr->underlineHeight == 0) {
+ fPtr->underlineHeight = 1;
+ }
+ if (fPtr->underlineHeight + fPtr->underlinePos > fPtr->fm.descent) {
+ fPtr->underlineHeight = fPtr->fm.descent - fPtr->underlinePos;
+ if (fPtr->underlineHeight == 0) {
+ fPtr->underlinePos--;
+ fPtr->underlineHeight = 1;
+ }
+ }
+ }
+
+ return fontPtr;
+}
+
+static void
+FinishedWithFont(
+ UnixFtFont *fontPtr)
+{
+ Display *display = fontPtr->display;
+ int i;
+ Tk_ErrorHandler handler =
+ Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL);
+
+ for (i = 0; i < fontPtr->nfaces; i++) {
+ if (fontPtr->faces[i].ftFont) {
+ XftFontClose(fontPtr->display, fontPtr->faces[i].ftFont);
+ }
+ if (fontPtr->faces[i].ft0Font) {
+ XftFontClose(fontPtr->display, fontPtr->faces[i].ft0Font);
+ }
+ if (fontPtr->faces[i].charset) {
+ FcCharSetDestroy(fontPtr->faces[i].charset);
+ }
+ }
+ if (fontPtr->faces) {
+ ckfree(fontPtr->faces);
+ }
+ if (fontPtr->pattern) {
+ FcPatternDestroy(fontPtr->pattern);
+ }
+ if (fontPtr->ftDraw) {
+ XftDrawDestroy(fontPtr->ftDraw);
+ }
+ if (fontPtr->font.fid) {
+ XUnloadFont(fontPtr->display, fontPtr->font.fid);
+ }
+ if (fontPtr->fontset) {
+ FcFontSetDestroy(fontPtr->fontset);
+ }
+ Tk_DeleteErrorHandler(handler);
+}
+
+TkFont *
+TkpGetNativeFont(
+ Tk_Window tkwin, /* For display where font will be used. */
+ const char *name) /* Platform-specific font name. */
+{
+ UnixFtFont *fontPtr;
+ FcPattern *pattern;
+#if DEBUG_FONTSEL
+ printf("TkpGetNativeFont %s\n", name);
+#endif /* DEBUG_FONTSEL */
+
+ pattern = XftXlfdParse(name, FcFalse, FcFalse);
+ if (!pattern) {
+ return NULL;
+ }
+
+ /*
+ * Should also try: pattern = FcNameParse(name); but generic/tkFont.c
+ * expects TkpGetNativeFont() to only work on XLFD names under Unix.
+ */
+
+ fontPtr = InitFont(tkwin, pattern, NULL);
+ if (!fontPtr) {
+ FcPatternDestroy(pattern);
+ return NULL;
+ }
+ return &fontPtr->font;
+}
+
+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. */
+{
+ XftPattern *pattern;
+ int weight, slant;
+ UnixFtFont *fontPtr;
+
+#if DEBUG_FONTSEL
+ printf("TkpGetFontFromAttributes %s-%d %d %d\n", faPtr->family,
+ faPtr->size, faPtr->weight, faPtr->slant);
+#endif /* DEBUG_FONTSEL */
+ pattern = XftPatternCreate();
+ if (faPtr->family) {
+ XftPatternAddString(pattern, XFT_FAMILY, faPtr->family);
+ }
+ if (faPtr->size > 0) {
+ XftPatternAddDouble(pattern, XFT_SIZE, (double)faPtr->size);
+ } else if (faPtr->size < 0) {
+ XftPatternAddInteger(pattern, XFT_PIXEL_SIZE, -faPtr->size);
+ } else {
+ XftPatternAddDouble(pattern, XFT_SIZE, 12.0);
+ }
+ switch (faPtr->weight) {
+ case TK_FW_NORMAL:
+ default:
+ weight = XFT_WEIGHT_MEDIUM;
+ break;
+ case TK_FW_BOLD:
+ weight = XFT_WEIGHT_BOLD;
+ break;
+ }
+ XftPatternAddInteger(pattern, XFT_WEIGHT, weight);
+ switch (faPtr->slant) {
+ case TK_FS_ROMAN:
+ default:
+ slant = XFT_SLANT_ROMAN;
+ break;
+ case TK_FS_ITALIC:
+ slant = XFT_SLANT_ITALIC;
+ break;
+ case TK_FS_OBLIQUE:
+ slant = XFT_SLANT_OBLIQUE;
+ break;
+ }
+ XftPatternAddInteger(pattern, XFT_SLANT, slant);
+
+ fontPtr = (UnixFtFont *) tkFontPtr;
+ if (fontPtr != NULL) {
+ FinishedWithFont(fontPtr);
+ }
+ fontPtr = InitFont(tkwin, pattern, fontPtr);
+
+ /*
+ * Hack to work around issues with weird issues with Xft/Xrender
+ * connection. For details, see comp.lang.tcl thread starting from
+ * <adcc99ed-c73e-4efc-bb5d-e57a57a051e8@l35g2000pra.googlegroups.com>
+ */
+
+ if (!fontPtr) {
+ XftPatternAddBool(pattern, XFT_RENDER, FcFalse);
+ fontPtr = InitFont(tkwin, pattern, fontPtr);
+ }
+
+ if (!fontPtr) {
+ FcPatternDestroy(pattern);
+ return NULL;
+ }
+
+ fontPtr->font.fa.underline = faPtr->underline;
+ fontPtr->font.fa.overstrike = faPtr->overstrike;
+ return &fontPtr->font;
+}
+
+void
+TkpDeleteFont(
+ TkFont *tkFontPtr) /* Token of font to be deleted. */
+{
+ UnixFtFont *fontPtr = (UnixFtFont *) tkFontPtr;
+
+ FinishedWithFont(fontPtr);
+ /* XXX tkUnixFont.c doesn't free tkFontPtr... */
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkpGetFontFamilies --
+ *
+ * Return information about the font families that are available on the
+ * display of the given window.
+ *
+ * Results:
+ * Modifies interp's result object to hold a list of all the available
+ * font families.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+TkpGetFontFamilies(
+ Tcl_Interp *interp, /* Interp to hold result. */
+ Tk_Window tkwin) /* For display to query. */
+{
+ Tcl_Obj *resultPtr;
+ XftFontSet *list;
+ int i;
+
+ resultPtr = Tcl_NewListObj(0, NULL);
+
+ list = XftListFonts(Tk_Display(tkwin), Tk_ScreenNumber(tkwin),
+ (char *) 0, /* pattern elements */
+ XFT_FAMILY, (char*) 0); /* fields */
+ for (i = 0; i < list->nfont; i++) {
+ char *family, **familyPtr = &family;
+
+ if (XftPatternGetString(list->fonts[i], XFT_FAMILY, 0, familyPtr)
+ == XftResultMatch) {
+ Tcl_Obj *strPtr = Tcl_NewStringObj(family, -1);
+
+ Tcl_ListObjAppendElement(NULL, resultPtr, strPtr);
+ }
+ }
+ XftFontSetDestroy(list);
+
+ Tcl_SetObjResult(interp, resultPtr);
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * TkpGetSubFonts --
+ *
+ * Called by [testfont subfonts] in the Tk testing package.
+ *
+ * Results:
+ * Sets interp's result to a list of the faces used by tkfont
+ *
+ *-------------------------------------------------------------------------
+ */
+
+void
+TkpGetSubFonts(
+ Tcl_Interp *interp,
+ Tk_Font tkfont)
+{
+ Tcl_Obj *objv[3], *listPtr, *resultPtr;
+ UnixFtFont *fontPtr = (UnixFtFont *) tkfont;
+ FcPattern *pattern;
+ const char *family = "Unknown";
+ const char *const *familyPtr = &family;
+ const char *foundry = "Unknown";
+ const char *const *foundryPtr = &foundry;
+ const char *encoding = "Unknown";
+ const char *const *encodingPtr = &encoding;
+ int i;
+
+ resultPtr = Tcl_NewListObj(0, NULL);
+
+ for (i = 0; i < fontPtr->nfaces ; ++i) {
+ pattern = FcFontRenderPrepare(0, fontPtr->pattern,
+ fontPtr->faces[i].source);
+
+ XftPatternGetString(pattern, XFT_FAMILY, 0, familyPtr);
+ XftPatternGetString(pattern, XFT_FOUNDRY, 0, foundryPtr);
+ XftPatternGetString(pattern, XFT_ENCODING, 0, encodingPtr);
+ objv[0] = Tcl_NewStringObj(family, -1);
+ objv[1] = Tcl_NewStringObj(foundry, -1);
+ objv[2] = Tcl_NewStringObj(encoding, -1);
+ listPtr = Tcl_NewListObj(3, objv);
+ Tcl_ListObjAppendElement(NULL, resultPtr, listPtr);
+ }
+ Tcl_SetObjResult(interp, resultPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetFontAttrsForChar --
+ *
+ * Retrieve the font attributes of the actual font used to render a given
+ * character.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpGetFontAttrsForChar(
+ Tk_Window tkwin, /* Window on the font's display */
+ Tk_Font tkfont, /* Font to query */
+ Tcl_UniChar c, /* Character of interest */
+ TkFontAttributes *faPtr) /* Output: Font attributes */
+{
+ UnixFtFont *fontPtr = (UnixFtFont *) tkfont;
+ /* Structure describing the logical font */
+ FcChar32 ucs4 = (FcChar32) c;
+ /* UCS-4 character to map */
+ XftFont *ftFont = GetFont(fontPtr, ucs4, 0.0);
+ /* Actual font used to render the character */
+
+ GetTkFontAttributes(ftFont, faPtr);
+ faPtr->underline = fontPtr->font.fa.underline;
+ faPtr->overstrike = fontPtr->font.fa.overstrike;
+}
+
+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 in pixels; 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. */
+{
+ UnixFtFont *fontPtr = (UnixFtFont *) tkfont;
+ XftFont *ftFont;
+ FcChar32 c;
+ XGlyphInfo extents;
+ int clen, curX, newX, curByte, newByte, sawNonSpace;
+ int termByte = 0, termX = 0;
+#if DEBUG_FONTSEL
+ char string[256];
+ int len = 0;
+#endif /* DEBUG_FONTSEL */
+
+ curX = 0;
+ curByte = 0;
+ sawNonSpace = 0;
+ while (numBytes > 0) {
+ Tcl_UniChar unichar;
+
+ clen = Tcl_UtfToUniChar(source, &unichar);
+ c = (FcChar32) unichar;
+
+ if (clen <= 0) {
+ /*
+ * This can't happen (but see #1185640)
+ */
+
+ *lengthPtr = curX;
+ return curByte;
+ }
+
+ source += clen;
+ numBytes -= clen;
+ if (c < 256 && isspace(c)) { /* I18N: ??? */
+ if (sawNonSpace) {
+ termByte = curByte;
+ termX = curX;
+ sawNonSpace = 0;
+ }
+ } else {
+ sawNonSpace = 1;
+ }
+
+#if DEBUG_FONTSEL
+ string[len++] = (char) c;
+#endif /* DEBUG_FONTSEL */
+ ftFont = GetFont(fontPtr, c, 0.0);
+
+ XftTextExtents32(fontPtr->display, ftFont, &c, 1, &extents);
+
+ newX = curX + extents.xOff;
+ newByte = curByte + clen;
+ if (maxLength >= 0 && newX > maxLength) {
+ if (flags & TK_PARTIAL_OK ||
+ (flags & TK_AT_LEAST_ONE && curByte == 0)) {
+ curX = newX;
+ curByte = newByte;
+ } else if (flags & TK_WHOLE_WORDS && termX != 0) {
+ curX = termX;
+ curByte = termByte;
+ }
+ break;
+ }
+
+ curX = newX;
+ curByte = newByte;
+ }
+#if DEBUG_FONTSEL
+ string[len] = '\0';
+ printf("MeasureChars %s length %d bytes %d\n", string, curX, curByte);
+#endif /* DEBUG_FONTSEL */
+ *lengthPtr = curX;
+ return curByte;
+}
+
+int
+TkpMeasureCharsInContext(
+ Tk_Font tkfont,
+ const char *source,
+ int numBytes,
+ int rangeStart,
+ int rangeLength,
+ int maxLength,
+ int flags,
+ int *lengthPtr)
+{
+ (void) numBytes; /*unused*/
+
+ return Tk_MeasureChars(tkfont, source + rangeStart, rangeLength,
+ maxLength, flags, lengthPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LookUpColor --
+ *
+ * Convert a pixel value to an XftColor. This can be slow due to the
+ * need to call XQueryColor, which involves a server round-trip. To
+ * avoid that, a least-recently-used cache of up to MAX_CACHED_COLORS
+ * is kept, in the form of a linked list. The returned color is moved
+ * to the front of the list, so repeatedly asking for the same one
+ * should be fast.
+ *
+ * Results:
+ * A pointer to the XftColor structure for the requested color is
+ * returned.
+ *
+ * Side effects:
+ * The converted color is stored in a cache in the UnixFtFont structure. The cache
+ * can hold at most MAX_CACHED_COLORS colors. If no more slots are available, the least
+ * recently used color is replaced with the new one.
+ *----------------------------------------------------------------------
+ */
+
+static XftColor *
+LookUpColor(Display *display, /* Display to lookup colors on */
+ UnixFtFont *fontPtr, /* Font to search for cached colors */
+ unsigned long pixel) /* Pixel value to translate to XftColor */
+{
+ int i, last = -1, last2 = -1;
+ XColor xcolor;
+
+ for (i = fontPtr->firstColor;
+ i >= 0; last2 = last, last = i, i = fontPtr->colors[i].next) {
+
+ if (pixel == fontPtr->colors[i].color.pixel) {
+ /*
+ * Color found in cache. Move it to the front of the list and return it.
+ */
+ if (last >= 0) {
+ fontPtr->colors[last].next = fontPtr->colors[i].next;
+ fontPtr->colors[i].next = fontPtr->firstColor;
+ fontPtr->firstColor = i;
+ }
+
+ return &fontPtr->colors[i].color;
+ }
+ }
+
+ /*
+ * Color wasn't found, so it needs to be added to the cache.
+ * If a spare slot is available, it can be put there. If not, last
+ * will now point to the least recently used color, so replace that one.
+ */
+
+ if (fontPtr->ncolors < MAX_CACHED_COLORS) {
+ last2 = -1;
+ last = fontPtr->ncolors++;
+ }
+
+ /*
+ * Translate the pixel value to a color. Needs a server round-trip.
+ */
+ xcolor.pixel = pixel;
+ XQueryColor(display, DefaultColormap(display, fontPtr->screen), &xcolor);
+
+ fontPtr->colors[last].color.color.red = xcolor.red;
+ fontPtr->colors[last].color.color.green = xcolor.green;
+ fontPtr->colors[last].color.color.blue = xcolor.blue;
+ fontPtr->colors[last].color.color.alpha = 0xffff;
+ fontPtr->colors[last].color.pixel = pixel;
+
+ /*
+ * Put at the front of the list.
+ */
+ if (last2 >= 0) {
+ fontPtr->colors[last2].next = fontPtr->colors[last].next;
+ }
+ fontPtr->colors[last].next = fontPtr->firstColor;
+ fontPtr->firstColor = last;
+
+ return &fontPtr->colors[last].color;
+}
+
+#define NUM_SPEC 1024
+
+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. */
+{
+ const int maxCoord = 0x7FFF;/* Xft coordinates are 16 bit values */
+ UnixFtFont *fontPtr = (UnixFtFont *) tkfont;
+ XGCValues values;
+ XftColor *xftcolor;
+ int clen, nspec, xStart = x;
+ XftGlyphFontSpec specs[NUM_SPEC];
+ XGlyphInfo metrics;
+ ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ if (fontPtr->ftDraw == 0) {
+#if DEBUG_FONTSEL
+ printf("Switch to drawable 0x%x\n", drawable);
+#endif /* DEBUG_FONTSEL */
+ fontPtr->ftDraw = XftDrawCreate(display, drawable,
+ DefaultVisual(display, fontPtr->screen),
+ DefaultColormap(display, fontPtr->screen));
+ } else {
+ Tk_ErrorHandler handler =
+ Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL);
+
+ XftDrawChange(fontPtr->ftDraw, drawable);
+ Tk_DeleteErrorHandler(handler);
+ }
+ XGetGCValues(display, gc, GCForeground, &values);
+ xftcolor = LookUpColor(display, fontPtr, values.foreground);
+ if (tsdPtr->clipRegion != None) {
+ XftDrawSetClip(fontPtr->ftDraw, tsdPtr->clipRegion);
+ }
+ nspec = 0;
+ while (numBytes > 0 && x <= maxCoord && y <= maxCoord) {
+ XftFont *ftFont;
+ FcChar32 c;
+
+ clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes);
+ if (clen <= 0) {
+ /*
+ * This should not happen, but it can.
+ */
+
+ goto doUnderlineStrikeout;
+ }
+ source += clen;
+ numBytes -= clen;
+
+ ftFont = GetFont(fontPtr, c, 0.0);
+ if (ftFont) {
+ specs[nspec].font = ftFont;
+ specs[nspec].glyph = XftCharIndex(fontPtr->display, ftFont, c);
+ specs[nspec].x = x;
+ specs[nspec].y = y;
+ XftGlyphExtents(fontPtr->display, ftFont, &specs[nspec].glyph, 1,
+ &metrics);
+ x += metrics.xOff;
+ y += metrics.yOff;
+ nspec++;
+ if (nspec == NUM_SPEC) {
+ XftDrawGlyphFontSpec(fontPtr->ftDraw, xftcolor,
+ specs, nspec);
+ nspec = 0;
+ }
+ }
+ }
+ if (nspec) {
+ XftDrawGlyphFontSpec(fontPtr->ftDraw, xftcolor, specs, nspec);
+ }
+
+ doUnderlineStrikeout:
+ if (tsdPtr->clipRegion != None) {
+ XftDrawSetClip(fontPtr->ftDraw, None);
+ }
+ if (fontPtr->font.fa.underline != 0) {
+ XFillRectangle(display, drawable, gc, xStart,
+ y + fontPtr->font.underlinePos, (unsigned) (x - xStart),
+ (unsigned) fontPtr->font.underlineHeight);
+ }
+ if (fontPtr->font.fa.overstrike != 0) {
+ y -= fontPtr->font.fm.descent + (fontPtr->font.fm.ascent) / 10;
+ XFillRectangle(display, drawable, gc, xStart, y,
+ (unsigned) (x - xStart),
+ (unsigned) fontPtr->font.underlineHeight);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkDrawAngledChars --
+ *
+ * Draw some characters at an angle. This would be simple code, except
+ * Xft has bugs with cumulative errors in character positioning which are
+ * caused by trying to perform all calculations internally with integers.
+ * So we have to do the work ourselves with floating-point math.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Target drawable is updated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+TkDrawAngledChars(
+ Display *display, /* Display on which to draw. */
+ Drawable drawable, /* Window or pixmap in which to draw. */
+ GC gc, /* Graphics context for drawing characters. */
+ Tk_Font tkfont, /* Font in which characters will be drawn;
+ * must be the same as font used in GC. */
+ const char *source, /* UTF-8 string to be displayed. Need not be
+ * '\0' terminated. All Tk meta-characters
+ * (tabs, control characters, and newlines)
+ * should be stripped out of the string that
+ * is passed to this function. If they are not
+ * stripped out, they will be displayed as
+ * regular printing characters. */
+ int numBytes, /* Number of bytes in string. */
+ double x, double y, /* Coordinates at which to place origin of
+ * string when drawing. */
+ double angle) /* What angle to put text at, in degrees. */
+{
+ const int maxCoord = 0x7FFF;/* Xft coordinates are 16 bit values */
+ const int minCoord = -1000; /* Should be good enough... */
+ UnixFtFont *fontPtr = (UnixFtFont *) tkfont;
+ XGCValues values;
+ XftColor *xftcolor;
+ int xStart = x, yStart = y;
+ ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+#ifdef XFT_HAS_FIXED_ROTATED_PLACEMENT
+ int clen, nglyph;
+ FT_UInt glyphs[NUM_SPEC];
+ XGlyphInfo metrics;
+ XftFont *currentFtFont;
+ int originX, originY;
+
+ if (fontPtr->ftDraw == 0) {
+#if DEBUG_FONTSEL
+ printf("Switch to drawable 0x%x\n", drawable);
+#endif /* DEBUG_FONTSEL */
+ fontPtr->ftDraw = XftDrawCreate(display, drawable,
+ DefaultVisual(display, fontPtr->screen),
+ DefaultColormap(display, fontPtr->screen));
+ } else {
+ Tk_ErrorHandler handler =
+ Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL);
+
+ XftDrawChange(fontPtr->ftDraw, drawable);
+ Tk_DeleteErrorHandler(handler);
+ }
+
+ XGetGCValues(display, gc, GCForeground, &values);
+ xftcolor = LookUpColor(display, fontPtr, values.foreground);
+ if (tsdPtr->clipRegion != None) {
+ XftDrawSetClip(fontPtr->ftDraw, tsdPtr->clipRegion);
+ }
+
+ nglyph = 0;
+ currentFtFont = NULL;
+ originX = originY = 0; /* lint */
+
+ while (numBytes > 0 && x <= maxCoord && x >= minCoord && y <= maxCoord
+ && y >= minCoord) {
+ XftFont *ftFont;
+ FcChar32 c;
+
+ clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes);
+ if (clen <= 0) {
+ /*
+ * This should not happen, but it can.
+ */
+
+ goto doUnderlineStrikeout;
+ }
+ source += clen;
+ numBytes -= clen;
+
+ ftFont = GetFont(fontPtr, c, angle);
+ if (!ftFont) {
+ continue;
+ }
+
+ if (ftFont != currentFtFont || nglyph == NUM_SPEC) {
+ if (nglyph) {
+ /*
+ * We pass multiple glyphs at once to enable the code to
+ * perform better rendering of sub-pixel inter-glyph spacing.
+ * If only the current Xft implementation could make use of
+ * this information... but we'll be ready when it does!
+ */
+
+ XftDrawGlyphs(fontPtr->ftDraw, xftcolor, currentFtFont,
+ originX, originY, glyphs, nglyph);
+ }
+ originX = ROUND16(x);
+ originY = ROUND16(y);
+ if (nglyph) {
+ XftGlyphExtents(fontPtr->display, currentFtFont, glyphs,
+ nglyph, &metrics);
+ nglyph = 0;
+ x += metrics.xOff;
+ y += metrics.yOff;
+ }
+ currentFtFont = ftFont;
+ }
+ glyphs[nglyph++] = XftCharIndex(fontPtr->display, ftFont, c);
+ }
+ if (nglyph) {
+ XftDrawGlyphs(fontPtr->ftDraw, xftcolor, currentFtFont,
+ originX, originY, glyphs, nglyph);
+ }
+#else /* !XFT_HAS_FIXED_ROTATED_PLACEMENT */
+ int clen, nspec;
+ XftGlyphFontSpec specs[NUM_SPEC];
+ XGlyphInfo metrics;
+ double sinA = sin(angle * PI/180.0), cosA = cos(angle * PI/180.0);
+
+ if (fontPtr->ftDraw == 0) {
+#if DEBUG_FONTSEL
+ printf("Switch to drawable 0x%x\n", drawable);
+#endif /* DEBUG_FONTSEL */
+ fontPtr->ftDraw = XftDrawCreate(display, drawable,
+ DefaultVisual(display, fontPtr->screen),
+ DefaultColormap(display, fontPtr->screen));
+ } else {
+ Tk_ErrorHandler handler =
+ Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL);
+
+ XftDrawChange(fontPtr->ftDraw, drawable);
+ Tk_DeleteErrorHandler(handler);
+ }
+ XGetGCValues(display, gc, GCForeground, &values);
+ xftcolor = LookUpColor(display, fontPtr, values.foreground);
+ if (tsdPtr->clipRegion != None) {
+ XftDrawSetClip(fontPtr->ftDraw, tsdPtr->clipRegion);
+ }
+ nspec = 0;
+ while (numBytes > 0 && x <= maxCoord && x >= minCoord
+ && y <= maxCoord && y >= minCoord) {
+ XftFont *ftFont, *ft0Font;
+ FcChar32 c;
+
+ clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes);
+ if (clen <= 0) {
+ /*
+ * This should not happen, but it can.
+ */
+
+ goto doUnderlineStrikeout;
+ }
+ source += clen;
+ numBytes -= clen;
+
+ ftFont = GetFont(fontPtr, c, angle);
+ ft0Font = GetFont(fontPtr, c, 0.0);
+ if (ftFont && ft0Font) {
+ specs[nspec].font = ftFont;
+ specs[nspec].glyph = XftCharIndex(fontPtr->display, ftFont, c);
+ specs[nspec].x = ROUND16(x);
+ specs[nspec].y = ROUND16(y);
+ XftGlyphExtents(fontPtr->display, ft0Font, &specs[nspec].glyph, 1,
+ &metrics);
+ x += metrics.xOff*cosA + metrics.yOff*sinA;
+ y += metrics.yOff*cosA - metrics.xOff*sinA;
+ nspec++;
+ if (nspec == NUM_SPEC) {
+ XftDrawGlyphFontSpec(fontPtr->ftDraw, xftcolor,
+ specs, nspec);
+ nspec = 0;
+ }
+ }
+ }
+ if (nspec) {
+ XftDrawGlyphFontSpec(fontPtr->ftDraw, xftcolor, specs, nspec);
+ }
+#endif /* XFT_HAS_FIXED_ROTATED_PLACEMENT */
+
+ doUnderlineStrikeout:
+ if (tsdPtr->clipRegion != None) {
+ XftDrawSetClip(fontPtr->ftDraw, None);
+ }
+ if (fontPtr->font.fa.underline || fontPtr->font.fa.overstrike) {
+ XPoint points[5];
+ double width = (x - xStart) * cosA + (yStart - y) * sinA;
+ double barHeight = fontPtr->font.underlineHeight;
+ double dy = fontPtr->font.underlinePos;
+
+ if (fontPtr->font.fa.underline != 0) {
+ if (fontPtr->font.underlineHeight == 1) {
+ dy++;
+ }
+ points[0].x = xStart + ROUND16(dy*sinA);
+ points[0].y = yStart + ROUND16(dy*cosA);
+ points[1].x = xStart + ROUND16(dy*sinA + width*cosA);
+ points[1].y = yStart + ROUND16(dy*cosA - width*sinA);
+ if (fontPtr->font.underlineHeight == 1) {
+ XDrawLines(display, drawable, gc, points, 2, CoordModeOrigin);
+ } else {
+ points[2].x = xStart + ROUND16(dy*sinA + width*cosA
+ + barHeight*sinA);
+ points[2].y = yStart + ROUND16(dy*cosA - width*sinA
+ + barHeight*cosA);
+ points[3].x = xStart + ROUND16(dy*sinA + barHeight*sinA);
+ points[3].y = yStart + ROUND16(dy*cosA + barHeight*cosA);
+ points[4].x = points[0].x;
+ points[4].y = points[0].y;
+ XFillPolygon(display, drawable, gc, points, 5, Complex,
+ CoordModeOrigin);
+ XDrawLines(display, drawable, gc, points, 5, CoordModeOrigin);
+ }
+ }
+ if (fontPtr->font.fa.overstrike != 0) {
+ dy = -fontPtr->font.fm.descent
+ - (fontPtr->font.fm.ascent) / 10;
+ points[0].x = xStart + ROUND16(dy*sinA);
+ points[0].y = yStart + ROUND16(dy*cosA);
+ points[1].x = xStart + ROUND16(dy*sinA + width*cosA);
+ points[1].y = yStart + ROUND16(dy*cosA - width*sinA);
+ if (fontPtr->font.underlineHeight == 1) {
+ XDrawLines(display, drawable, gc, points, 2, CoordModeOrigin);
+ } else {
+ points[2].x = xStart + ROUND16(dy*sinA + width*cosA
+ + barHeight*sinA);
+ points[2].y = yStart + ROUND16(dy*cosA - width*sinA
+ + barHeight*cosA);
+ points[3].x = xStart + ROUND16(dy*sinA + barHeight*sinA);
+ points[3].y = yStart + ROUND16(dy*cosA + barHeight*cosA);
+ points[4].x = points[0].x;
+ points[4].y = points[0].y;
+ XFillPolygon(display, drawable, gc, points, 5, Complex,
+ CoordModeOrigin);
+ XDrawLines(display, drawable, gc, points, 5, CoordModeOrigin);
+ }
+ }
+ }
+}
+
+void
+TkUnixSetXftClipRegion(
+ TkRegion clipRegion) /* The clipping region to install. */
+{
+ ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ tsdPtr->clipRegion = (Region) clipRegion;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */