Tk Source Code

Artifact [6d2e402e]
Login

Artifact 6d2e402e89efb0e6eac32bc38439cf8c394b3869:

Attachment "rotate.patch" to ticket [1611359f] added by dkf 2008-11-18 08:39:03.
Index: generic/tkCanvText.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkCanvText.c,v
retrieving revision 1.32
diff -u -r1.32 tkCanvText.c
--- generic/tkCanvText.c	9 Nov 2008 20:51:28 -0000	1.32
+++ generic/tkCanvText.c	18 Nov 2008 01:31:28 -0000
@@ -57,6 +57,8 @@
 				 * means no word-wrap. */
     int underline;		/* Index of character to put underline beneath
 				 * or -1 for no underlining. */
+    double angle;		/* What angle, in degrees, to draw the text
+				 * at. */
 
     /*
      * Fields whose values are derived from the current values of the
@@ -66,18 +68,18 @@
     int numChars;		/* Length of text in characters. */
     int numBytes;		/* Length of text in bytes. */
     Tk_TextLayout textLayout;	/* Cached text layout information. */
-    int leftEdge;		/* Pixel location of the left edge of the text
-				 * item; where the left border of the text
-				 * layout is drawn. */
-    int rightEdge;		/* Pixel just to right of right edge of area
-				 * of text item. Used for selecting up to end
-				 * of line. */
+    int actualWidth;		/* Width of text as computed. Used to make
+				 * selections of wrapped text display
+				 * right. */ 
+    double drawOrigin[2];	/* Where we start drawing from. */
     GC gc;			/* Graphics context for drawing text. */
     GC selTextGC;		/* Graphics context for selected text. */
     GC cursorOffGC;		/* If not None, this gives a graphics context
 				 * to use to draw the insertion cursor when
 				 * it's off. Used if the selection and
 				 * insertion cursor colors are the same. */
+    double sine;		/* Sine of angle field. */
+    double cosine;		/* Cosine of angle field. */
 } TextItem;
 
 /*
@@ -101,6 +103,8 @@
 	NULL, Tk_Offset(TextItem, activeStipple), TK_CONFIG_NULL_OK},
     {TK_CONFIG_ANCHOR, "-anchor", NULL, NULL,
 	"center", Tk_Offset(TextItem, anchor), TK_CONFIG_DONT_SET_DEFAULT},
+    {TK_CONFIG_DOUBLE, "-angle", NULL, NULL,
+	"0.0", Tk_Offset(TextItem, angle), TK_CONFIG_DONT_SET_DEFAULT},
     {TK_CONFIG_COLOR, "-disabledfill", NULL, NULL,
 	NULL, Tk_Offset(TextItem, disabledColor), TK_CONFIG_NULL_OK},
     {TK_CONFIG_BITMAP, "-disabledstipple", NULL, NULL,
@@ -199,6 +203,9 @@
     TextDeleteChars,		/* dTextProc */
     NULL,			/* nextPtr */
 };
+
+#define PI	3.14159265358979323846
+#define ROUND(d) ((int) floor((d) + 0.5))
 
 /*
  *--------------------------------------------------------------
@@ -258,15 +265,18 @@
     textPtr->text	= NULL;
     textPtr->width	= 0;
     textPtr->underline	= -1;
+    textPtr->angle	= 0.0;
 
     textPtr->numChars	= 0;
     textPtr->numBytes	= 0;
     textPtr->textLayout = NULL;
-    textPtr->leftEdge	= 0;
-    textPtr->rightEdge	= 0;
+    textPtr->actualWidth = 0;
+    textPtr->drawOrigin[0] = textPtr->drawOrigin[1] = 0.0;
     textPtr->gc		= None;
     textPtr->selTextGC	= None;
     textPtr->cursorOffGC = None;
+    textPtr->sine	= 0.0;
+    textPtr->cosine	= 1.0;
 
     /*
      * Process the arguments to fill in the item record. Only 1 (list) or 2 (x
@@ -325,14 +335,14 @@
 
     if (objc == 0) {
 	Tcl_Obj *obj = Tcl_NewObj();
-
 	Tcl_Obj *subobj = Tcl_NewDoubleObj(textPtr->x);
+
 	Tcl_ListObjAppendElement(interp, obj, subobj);
 	subobj = Tcl_NewDoubleObj(textPtr->y);
 	Tcl_ListObjAppendElement(interp, obj, subobj);
 	Tcl_SetObjResult(interp, obj);
     } else if (objc < 3) {
-	if (objc==1) {
+	if (objc == 1) {
 	    if (Tcl_ListObjGetElements(interp, objv[0], &objc,
 		    (Tcl_Obj ***) &objv) != TCL_OK) {
 		return TCL_ERROR;
@@ -415,24 +425,24 @@
 	itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT;
     }
 
-    if(state == TK_STATE_NULL) {
+    if (state == TK_STATE_NULL) {
 	state = Canvas(canvas)->canvas_state;
     }
 
     color = textPtr->color;
     stipple = textPtr->stipple;
     if (Canvas(canvas)->currentItemPtr == itemPtr) {
-	if (textPtr->activeColor!=NULL) {
+	if (textPtr->activeColor != NULL) {
 	    color = textPtr->activeColor;
 	}
-	if (textPtr->activeStipple!=None) {
+	if (textPtr->activeStipple != None) {
 	    stipple = textPtr->activeStipple;
 	}
-    } else if (state==TK_STATE_DISABLED) {
-	if (textPtr->disabledColor!=NULL) {
+    } else if (state == TK_STATE_DISABLED) {
+	if (textPtr->disabledColor != NULL) {
 	    color = textPtr->disabledColor;
 	}
-	if (textPtr->disabledStipple!=None) {
+	if (textPtr->disabledStipple != None) {
 	    stipple = textPtr->disabledStipple;
 	}
     }
@@ -513,6 +523,10 @@
 	textPtr->insertPos = textPtr->numChars;
     }
 
+    textPtr->angle = fmod(textPtr->angle, 360.0);
+    textPtr->sine = sin(textPtr->angle * PI/180.0);
+    textPtr->cosine = cos(textPtr->angle * PI/180.0);
+
     ComputeTextBbox(canvas, textPtr);
     return TCL_OK;
 }
@@ -603,10 +617,11 @@
     TextItem *textPtr)		/* Item whose bbox is to be recomputed. */
 {
     Tk_CanvasTextInfo *textInfoPtr;
-    int leftX, topY, width, height, fudge;
+    int leftX, topY, width, height, fudge, i;
     Tk_State state = textPtr->header.state;
+    double x[4], y[4], dx[4], dy[4], sinA, cosA, tmp;
 
-    if(state == TK_STATE_NULL) {
+    if (state == TK_STATE_NULL) {
 	state = Canvas(canvas)->canvas_state;
     }
 
@@ -624,8 +639,11 @@
      * bounding box for the text item.
      */
 
-    leftX = (int) floor(textPtr->x + 0.5);
-    topY = (int) floor(textPtr->y + 0.5);
+    leftX = ROUND(textPtr->x);
+    topY = ROUND(textPtr->y);
+    for (i=0 ; i<4 ; i++) {
+	dx[i] = dy[i] = 0.0;
+    }
     switch (textPtr->anchor) {
     case TK_ANCHOR_NW:
     case TK_ANCHOR_N:
@@ -636,12 +654,18 @@
     case TK_ANCHOR_CENTER:
     case TK_ANCHOR_E:
 	topY -= height / 2;
+	for (i=0 ; i<4 ; i++) {
+	    dy[i] = -height / 2;
+	}
 	break;
 
     case TK_ANCHOR_SW:
     case TK_ANCHOR_S:
     case TK_ANCHOR_SE:
 	topY -= height;
+	for (i=0 ; i<4 ; i++) {
+	    dy[i] = -height;
+	}
 	break;
     }
     switch (textPtr->anchor) {
@@ -654,17 +678,27 @@
     case TK_ANCHOR_CENTER:
     case TK_ANCHOR_S:
 	leftX -= width / 2;
+	for (i=0 ; i<4 ; i++) {
+	    dx[i] = -width / 2;
+	}
 	break;
 
     case TK_ANCHOR_NE:
     case TK_ANCHOR_E:
     case TK_ANCHOR_SE:
 	leftX -= width;
+	for (i=0 ; i<4 ; i++) {
+	    dx[i] = -width;
+	}
 	break;
     }
 
-    textPtr->leftEdge = leftX;
-    textPtr->rightEdge = leftX + width;
+    textPtr->actualWidth = width;
+
+    sinA = textPtr->sine;
+    cosA = textPtr->cosine;
+    textPtr->drawOrigin[0] = textPtr->x + dx[0]*cosA + dy[0]*sinA;
+    textPtr->drawOrigin[1] = textPtr->y + dy[0]*cosA - dx[0]*sinA;
 
     /*
      * Last of all, update the bounding box for the item. The item's bounding
@@ -677,10 +711,50 @@
     if (textInfoPtr->selBorderWidth > fudge) {
 	fudge = textInfoPtr->selBorderWidth;
     }
-    textPtr->header.x1 = leftX - fudge;
-    textPtr->header.y1 = topY;
-    textPtr->header.x2 = leftX + width + fudge;
-    textPtr->header.y2 = topY + height;
+
+    /*
+     * Apply the rotation before computing the bounding box.
+     */
+
+    dx[0] -= fudge;
+    dx[1] += width + fudge;
+    dx[2] += width + fudge;
+    dy[2] += height;
+    dx[3] -= fudge;
+    dy[3] += height;
+    for (i=0 ; i<4 ; i++) {
+	x[i] = textPtr->x + dx[i] * cosA + dy[i] * sinA;
+	y[i] = textPtr->y + dy[i] * cosA - dx[i] * sinA;
+    }
+
+    /*
+     * Convert to a rectilinear bounding box.
+     */
+
+    for (i=1,tmp=x[0] ; i<4 ; i++) {
+	if (x[i] < tmp) {
+	    tmp = x[i];
+	}
+    }
+    textPtr->header.x1 = ROUND(tmp);
+    for (i=1,tmp=y[0] ; i<4 ; i++) {
+	if (y[i] < tmp) {
+	    tmp = y[i];
+	}
+    }
+    textPtr->header.y1 = ROUND(tmp);
+    for (i=1,tmp=x[0] ; i<4 ; i++) {
+	if (x[i] > tmp) {
+	    tmp = x[i];
+	}
+    }
+    textPtr->header.x2 = ROUND(tmp);
+    for (i=1,tmp=y[0] ; i<4 ; i++) {
+	if (y[i] > tmp) {
+	    tmp = y[i];
+	}
+    }
+    textPtr->header.y2 = ROUND(tmp);
 }
 
 /*
@@ -720,16 +794,16 @@
     textPtr = (TextItem *) itemPtr;
     textInfoPtr = textPtr->textInfoPtr;
 
-    if(state == TK_STATE_NULL) {
+    if (state == TK_STATE_NULL) {
 	state = Canvas(canvas)->canvas_state;
     }
     stipple = textPtr->stipple;
     if (Canvas(canvas)->currentItemPtr == itemPtr) {
-	if (textPtr->activeStipple!=None) {
+	if (textPtr->activeStipple != None) {
 	    stipple = textPtr->activeStipple;
 	}
-    } else if (state==TK_STATE_DISABLED) {
-	if (textPtr->disabledStipple!=None) {
+    } else if (state == TK_STATE_DISABLED) {
+	if (textPtr->disabledStipple != None) {
 	    stipple = textPtr->disabledStipple;
 	}
     }
@@ -750,6 +824,8 @@
 
     selFirstChar = -1;
     selLastChar = 0;		/* lint. */
+    Tk_CanvasDrawableCoords(canvas, textPtr->drawOrigin[0],
+	    textPtr->drawOrigin[1], &drawableX, &drawableY);
 
     if (textInfoPtr->selItemPtr == itemPtr) {
 	selFirstChar = textInfoPtr->selectFirst;
@@ -780,20 +856,30 @@
 	    x = xFirst;
 	    height = hFirst;
 	    for (y = yFirst ; y <= yLast; y += height) {
+		int dx1, dy1, dx2, dy2;
+		double s = textPtr->sine, c = textPtr->cosine;
+		XPoint points[4];
+
 		if (y == yLast) {
 		    width = xLast + wLast - x;
 		} else {
-		    width = textPtr->rightEdge - textPtr->leftEdge - x;
+		    width = textPtr->actualWidth - x;
 		}
-		Tk_CanvasDrawableCoords(canvas,
-			(double) (textPtr->leftEdge + x
-				- textInfoPtr->selBorderWidth),
-			(double) (textPtr->header.y1 + y),
-			&drawableX, &drawableY);
-		Tk_Fill3DRectangle(Tk_CanvasTkwin(canvas), drawable,
-			textInfoPtr->selBorder, drawableX, drawableY,
-			width + 2 * textInfoPtr->selBorderWidth,
-			height, textInfoPtr->selBorderWidth, TK_RELIEF_RAISED);
+		dx1 = x - textInfoPtr->selBorderWidth;
+		dy1 = y;
+		dx2 = width + 2 * textInfoPtr->selBorderWidth;
+		dy2 = height;
+		points[0].x = drawableX + dx1*c + dy1*s;
+		points[0].y = drawableY + dy1*c - dx1*s;
+		points[1].x = drawableX + (dx1+dx2)*c + dy1*s;
+		points[1].y = drawableY + dy1*c - (dx1+dx2)*s;
+		points[2].x = drawableX + (dx1+dx2)*c + (dy1+dy2)*s;
+		points[2].y = drawableY + (dy1+dy2)*c - (dx1+dx2)*s;
+		points[3].x = drawableX + dx1*c + (dy1+dy2)*s;
+		points[3].y = drawableY + (dy1+dy2)*c - dx1*s;
+		Tk_Fill3DPolygon(Tk_CanvasTkwin(canvas), drawable,
+			textInfoPtr->selBorder, points, 4,
+			textInfoPtr->selBorderWidth, TK_RELIEF_RAISED);
 		x = 0;
 	    }
 	}
@@ -811,18 +897,28 @@
     if ((textInfoPtr->focusItemPtr == itemPtr) && (textInfoPtr->gotFocus)) {
 	if (Tk_CharBbox(textPtr->textLayout, textPtr->insertPos,
 		&x, &y, NULL, &height)) {
-	    Tk_CanvasDrawableCoords(canvas,
-		    (double) (textPtr->leftEdge + x
-			    - (textInfoPtr->insertWidth / 2)),
-		    (double) (textPtr->header.y1 + y),
-		    &drawableX, &drawableY);
-	    Tk_SetCaretPos(Tk_CanvasTkwin(canvas), drawableX, drawableY,
+	    int dx1, dy1, dx2, dy2;
+	    double s = textPtr->sine, c = textPtr->cosine;
+	    XPoint points[4];
+
+	    dx1 = x - (textInfoPtr->insertWidth / 2);
+	    dy1 = y;
+	    dx2 = textInfoPtr->insertWidth;
+	    dy2 = height;
+	    points[0].x = drawableX + dx1*c + dy1*s;
+	    points[0].y = drawableY + dy1*c - dx1*s;
+	    points[1].x = drawableX + (dx1+dx2)*c + dy1*s;
+	    points[1].y = drawableY + dy1*c - (dx1+dx2)*s;
+	    points[2].x = drawableX + (dx1+dx2)*c + (dy1+dy2)*s;
+	    points[2].y = drawableY + (dy1+dy2)*c - (dx1+dx2)*s;
+	    points[3].x = drawableX + dx1*c + (dy1+dy2)*s;
+	    points[3].y = drawableY + (dy1+dy2)*c - dx1*s;
+
+	    Tk_SetCaretPos(Tk_CanvasTkwin(canvas), points[0].x, points[0].y,
 		    height);
 	    if (textInfoPtr->cursorOn) {
-		Tk_Fill3DRectangle(Tk_CanvasTkwin(canvas), drawable,
-			textInfoPtr->insertBorder,
-			drawableX, drawableY,
-			textInfoPtr->insertWidth, height,
+		Tk_Fill3DPolygon(Tk_CanvasTkwin(canvas), drawable,
+			textInfoPtr->insertBorder, points, 4,
 			textInfoPtr->insertBorderWidth, TK_RELIEF_RAISED);
 	    } else if (textPtr->cursorOffGC != None) {
 		/*
@@ -832,10 +928,8 @@
 		 * where both may be drawn in the same color.
 		 */
 
-		XFillRectangle(display, drawable, textPtr->cursorOffGC,
-			drawableX, drawableY,
-			(unsigned) textInfoPtr->insertWidth,
-			(unsigned) height);
+		XFillPolygon(display, drawable, textPtr->cursorOffGC,
+			points, 4, Convex, CoordModeOrigin);
 	    }
 	}
     }
@@ -850,23 +944,24 @@
      * anti-aliasing colors would blend together.
      */
 
-    Tk_CanvasDrawableCoords(canvas, (double) textPtr->leftEdge,
-	    (double) textPtr->header.y1, &drawableX, &drawableY);
-
     if ((selFirstChar >= 0) && (textPtr->selTextGC != textPtr->gc)) {
-	Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
-	    drawableX, drawableY, 0, selFirstChar);
-	Tk_DrawTextLayout(display, drawable, textPtr->selTextGC,
-	    textPtr->textLayout, drawableX, drawableY, selFirstChar,
-	    selLastChar + 1);
-	Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
-	    drawableX, drawableY, selLastChar + 1, -1);
+	TkDrawAngledTextLayout(display, drawable, textPtr->gc,
+		textPtr->textLayout, drawableX, drawableY, textPtr->angle,
+		0, selFirstChar);
+	TkDrawAngledTextLayout(display, drawable, textPtr->selTextGC,
+		textPtr->textLayout, drawableX, drawableY, textPtr->angle,
+		selFirstChar, selLastChar + 1);
+	TkDrawAngledTextLayout(display, drawable, textPtr->gc,
+		textPtr->textLayout, drawableX, drawableY, textPtr->angle,
+		selLastChar + 1, -1);
     } else {
-	Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
-	    drawableX, drawableY, 0, -1);
-    }
-    Tk_UnderlineTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
-	    drawableX, drawableY, textPtr->underline);
+	TkDrawAngledTextLayout(display, drawable, textPtr->gc,
+		textPtr->textLayout, drawableX, drawableY, textPtr->angle,
+		0, -1);
+    }
+    TkUnderlineAngledTextLayout(display, drawable, textPtr->gc,
+	    textPtr->textLayout, drawableX, drawableY, textPtr->angle,
+	    textPtr->underline);
 
     if (stipple != None) {
 	XSetTSOrigin(display, textPtr->gc, 0, 0);
@@ -1076,15 +1171,17 @@
 {
     TextItem *textPtr;
     Tk_State state = itemPtr->state;
-    double value;
+    double value, px, py;
 
     if (state == TK_STATE_NULL) {
 	state = Canvas(canvas)->canvas_state;
     }
     textPtr = (TextItem *) itemPtr;
+    px = pointPtr[0] - textPtr->drawOrigin[0];
+    py = pointPtr[1] - textPtr->drawOrigin[1];
     value = (double) Tk_DistanceToTextLayout(textPtr->textLayout,
-	    (int) pointPtr[0] - textPtr->leftEdge,
-	    (int) pointPtr[1] - textPtr->header.y1);
+	    (int) px*textPtr->cosine - py*textPtr->sine,
+	    (int) py*textPtr->cosine + px*textPtr->sine);
 
     if ((state == TK_STATE_HIDDEN) || (textPtr->color == NULL) ||
 	    (textPtr->text == NULL) || (*textPtr->text == 0)) {
@@ -1128,11 +1225,12 @@
     }
 
     textPtr = (TextItem *) itemPtr;
-    return Tk_IntersectTextLayout(textPtr->textLayout,
-	    (int) (rectPtr[0] + 0.5) - textPtr->leftEdge,
-	    (int) (rectPtr[1] + 0.5) - textPtr->header.y1,
+    return TkIntersectAngledTextLayout(textPtr->textLayout,
+	    (int) (rectPtr[0] + 0.5) - textPtr->drawOrigin[0],
+	    (int) (rectPtr[1] + 0.5) - textPtr->drawOrigin[1],
 	    (int) (rectPtr[2] - rectPtr[0] + 0.5),
-	    (int) (rectPtr[3] - rectPtr[1] + 0.5));
+	    (int) (rectPtr[3] - rectPtr[1] + 0.5),
+	    textPtr->angle);
 }
 
 /*
@@ -1261,7 +1359,7 @@
 	*indexPtr = textInfoPtr->selectLast;
     } else if (c == '@') {
 	int x, y;
-	double tmp;
+	double tmp, c = textPtr->cosine, s = textPtr->sine;
 	char *end, *p;
 
 	p = string+1;
@@ -1276,9 +1374,9 @@
 	    goto badIndex;
 	}
 	y = (int) ((tmp < 0) ? tmp - 0.5 : tmp + 0.5);
-	*indexPtr = Tk_PointToChar(textPtr->textLayout,
-		x + canvasPtr->scrollX1 - textPtr->leftEdge,
-		y + canvasPtr->scrollY1 - textPtr->header.y1);
+	x += canvasPtr->scrollX1 - textPtr->drawOrigin[0];
+	y += canvasPtr->scrollY1 - textPtr->drawOrigin[1];
+	*indexPtr = Tk_PointToChar(textPtr->textLayout, x*c-y*s, y*c+x*s);
     } else if (Tcl_GetIntFromObj(NULL, obj, indexPtr) == TCL_OK) {
 	if (*indexPtr < 0) {
 	    *indexPtr = 0;
@@ -1423,7 +1521,6 @@
     int x, y;
     Tk_FontMetrics fm;
     const char *justify;
-    char buffer[500];
     XColor *color;
     Pixmap stipple;
     Tk_State state = itemPtr->state;
@@ -1437,17 +1534,17 @@
 	    textPtr->text == NULL || *textPtr->text == 0) {
 	return TCL_OK;
     } else if (Canvas(canvas)->currentItemPtr == itemPtr) {
-	if (textPtr->activeColor!=NULL) {
+	if (textPtr->activeColor != NULL) {
 	    color = textPtr->activeColor;
 	}
-	if (textPtr->activeStipple!=None) {
+	if (textPtr->activeStipple != None) {
 	    stipple = textPtr->activeStipple;
 	}
-    } else if (state==TK_STATE_DISABLED) {
-	if (textPtr->disabledColor!=NULL) {
+    } else if (state == TK_STATE_DISABLED) {
+	if (textPtr->disabledColor != NULL) {
 	    color = textPtr->disabledColor;
 	}
-	if (textPtr->disabledStipple!=None) {
+	if (textPtr->disabledStipple != None) {
 	    stipple = textPtr->disabledStipple;
 	}
     }
@@ -1467,9 +1564,8 @@
 	Tcl_AppendResult(interp, "} bind def\n", NULL);
     }
 
-    sprintf(buffer, "%.15g %.15g [\n", textPtr->x,
-	    Tk_CanvasPsY(canvas, textPtr->y));
-    Tcl_AppendResult(interp, buffer, NULL);
+    Tcl_AppendPrintfToObj(Tcl_GetObjResult(interp), "%.15g %.15g %.15g [\n",
+	    textPtr->angle, textPtr->x, Tk_CanvasPsY(canvas, textPtr->y));
 
     Tk_TextLayoutToPostscript(interp, textPtr->textLayout);
 
@@ -1492,15 +1588,10 @@
     }
 
     Tk_GetFontMetrics(textPtr->tkfont, &fm);
-    sprintf(buffer, "] %d ", fm.linespace);
-    Tcl_AppendResult(interp, buffer, NULL);
-    Tcl_PrintDouble(NULL, x / -2.0, buffer);
-    Tcl_AppendResult(interp, buffer, " ", NULL);
-    Tcl_PrintDouble(NULL, y / 2.0, buffer);
-    Tcl_AppendResult(interp, buffer, NULL);
-    sprintf(buffer, " %s %s DrawText\n", justify,
+    Tcl_AppendPrintfToObj(Tcl_GetObjResult(interp),
+	    "] %d %g %g %s %s DrawText\n",
+	    fm.linespace, x / -2.0, y / 2.0, justify,
 	    ((stipple == None) ? "false" : "true"));
-    Tcl_AppendResult(interp, buffer, NULL);
 
     return TCL_OK;
 }
Index: generic/tkFont.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkFont.c,v
retrieving revision 1.48
diff -u -r1.48 tkFont.c
--- generic/tkFont.c	2 Nov 2008 09:54:02 -0000	1.48
+++ generic/tkFont.c	18 Nov 2008 01:31:32 -0000
@@ -354,6 +354,8 @@
     NULL,			/* updateStringProc */
     SetFontFromAny		/* setFromAnyProc */
 };
+
+#define PI	3.14159265358979323846
 
 /*
  *---------------------------------------------------------------------------
@@ -888,7 +890,7 @@
 	    Tk_GetClassProc(winPtr->classProcsPtr, worldChangedProc);
 
     if (proc != NULL) {
-	(*proc)(winPtr->instanceData);
+	proc(winPtr->instanceData);
     }
 
     /*
@@ -1347,7 +1349,7 @@
     Tcl_GetString(objPtr);
     typePtr = objPtr->typePtr;
     if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
-	(*typePtr->freeIntRepProc)(objPtr);
+	typePtr->freeIntRepProc(objPtr);
     }
     objPtr->typePtr = &tkFontObjType;
     objPtr->internalRep.twoPtrValue.ptr1 = NULL;
@@ -2310,6 +2312,69 @@
     }
 }
 
+void
+TkDrawAngledTextLayout(
+    Display *display,		/* Display on which to draw. */
+    Drawable drawable,		/* Window or pixmap in which to draw. */
+    GC gc,			/* Graphics context to use for drawing text. */
+    Tk_TextLayout layout,	/* Layout information, from a previous call to
+				 * Tk_ComputeTextLayout(). */
+    int x, int y,		/* Upper-left hand corner of rectangle in
+				 * which to draw (pixels). */
+    double angle,
+    int firstChar,		/* The index of the first character to draw
+				 * from the given text item. 0 specfies the
+				 * beginning. */
+    int lastChar)		/* The index just after the last character to
+				 * draw from the given text item. A number < 0
+				 * means to draw all characters. */
+{
+    TextLayout *layoutPtr = (TextLayout *) layout;
+    int i, numDisplayChars, drawX;
+    const char *firstByte, *lastByte;
+    LayoutChunk *chunkPtr;
+    double sinA = sin(angle*PI/180.0), cosA = cos(angle*PI/180.0);
+
+    if (layoutPtr == NULL) {
+	return;
+    }
+
+    if (lastChar < 0) {
+	lastChar = 100000000;
+    }
+    chunkPtr = layoutPtr->chunks;
+    for (i = 0; i < layoutPtr->numChunks; i++) {
+	numDisplayChars = chunkPtr->numDisplayChars;
+	if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) {
+	    double dx, dy;
+
+	    if (firstChar <= 0) {
+		drawX = 0;
+		firstChar = 0;
+		firstByte = chunkPtr->start;
+	    } else {
+		firstByte = Tcl_UtfAtIndex(chunkPtr->start, firstChar);
+		Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start,
+			firstByte - chunkPtr->start, -1, 0, &drawX);
+	    }
+	    if (lastChar < numDisplayChars) {
+		numDisplayChars = lastChar;
+	    }
+	    lastByte = Tcl_UtfAtIndex(chunkPtr->start, numDisplayChars);
+	    dx = cosA * (chunkPtr->x + drawX) + sinA * (chunkPtr->y);
+	    dy = -sinA * (chunkPtr->x + drawX) + cosA * (chunkPtr->y);
+	    TkpDrawAngledChars(display, drawable, gc, layoutPtr->tkfont,
+		    firstByte, lastByte - firstByte, x+dx, y+dy, angle);
+	}
+	firstChar -= chunkPtr->numChars;
+	lastChar -= chunkPtr->numChars;
+	if (lastChar <= 0) {
+	    break;
+	}
+	chunkPtr++;
+    }
+}
+
 /*
  *---------------------------------------------------------------------------
  *
@@ -2358,6 +2423,68 @@
     }
 }
 
+void
+TkUnderlineAngledTextLayout(
+    Display *display,		/* Display on which to draw. */
+    Drawable drawable,		/* Window or pixmap in which to draw. */
+    GC gc,			/* Graphics context to use for drawing text. */
+    Tk_TextLayout layout,	/* Layout information, from a previous call to
+				 * Tk_ComputeTextLayout(). */
+    int x, int y,		/* Upper-left hand corner of rectangle in
+				 * which to draw (pixels). */
+    double angle,
+    int underline)		/* Index of the single character to underline,
+				 * or -1 for no underline. */
+{
+    int xx, yy, width, height;
+
+    if (angle == 0.0) {
+	Tk_UnderlineTextLayout(display, drawable, gc, layout, x,y, underline);
+	return;
+    }
+
+    if ((Tk_CharBbox(layout, underline, &xx, &yy, &width, &height) != 0)
+	    && (width != 0)) {
+	TextLayout *layoutPtr = (TextLayout *) layout;
+	TkFont *fontPtr = (TkFont *) layoutPtr->tkfont;
+	double sinA = sin(angle*PI/180), cosA = cos(angle*PI/180);
+	double dy = yy + fontPtr->fm.ascent + fontPtr->underlinePos;
+	XPoint points[5];
+
+	/*
+	 * Note that we're careful to only round a double value once, which
+	 * minimizes roundoff errors.
+	 */
+
+	points[0].x = x + round(xx*cosA + dy*sinA);
+	points[0].y = y + round(dy*cosA - xx*sinA);
+	points[1].x = x + round(xx*cosA + dy*sinA + width*cosA);
+	points[1].y = y + round(dy*cosA - xx*sinA - width*sinA);
+	if (fontPtr->underlineHeight == 1) {
+	    /*
+	     * Thin underlines look better when rotated when drawn as a line
+	     * rather than a rectangle; the rasterizer copes better.
+	     */
+
+	    XDrawLines(display, drawable, gc, points, 2, CoordModeOrigin);
+	} else {
+	    points[2].x = x + round(xx*cosA + dy*sinA + width*cosA
+		    - fontPtr->underlineHeight*sinA);
+	    points[2].y = y + round(dy*cosA - xx*sinA - width*sinA
+		    + fontPtr->underlineHeight*cosA);
+	    points[3].x = x + round(xx*cosA + dy*sinA
+		    - fontPtr->underlineHeight*sinA);
+	    points[3].y = y + round(dy*cosA - xx*sinA
+		    + fontPtr->underlineHeight*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);
+	}
+    }
+}
+
 /*
  *---------------------------------------------------------------------------
  *
@@ -2806,6 +2933,263 @@
 /*
  *---------------------------------------------------------------------------
  *
+ * TkIntersectAngledTextLayout --
+ *
+ *	Determines whether a text layout that has been turned by an angle
+ *	about its top-left coordinae lies entirely inside, entirely outside,
+ *	or overlaps a given rectangle. Non-displaying space characters that
+ *	occur at the end of individual lines in the text layout are ignored
+ *	for intersection calculations.
+ *
+ * Results:
+ *	The return value is -1 if the text layout is entirely outside of the
+ *	rectangle, 0 if it overlaps, and 1 if it is entirely inside of the
+ *	rectangle.
+ *
+ * Side effects:
+ *	None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static inline int
+PointInQuadrilateral(
+    double qx[],
+    double qy[],
+    double x,
+    double y)
+{
+    int i;
+
+    for (i=0 ; i<4 ; i++) {
+	double sideDX = qx[(i+1)%4] - qx[i];
+	double sideDY = qy[(i+1)%4] - qy[i];
+	double dx = x - qx[i];
+	double dy = y - qy[i];
+
+	if (sideDX*dy < sideDY*dx) {
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+static inline int
+sign(
+    double value)
+{
+    if (value < 0.0) return -1;
+    if (value > 0.0) return 1;
+    return 0;
+}
+
+static inline int
+SidesIntersect(
+    double ax1, double ay1, double ax2, double ay2,
+    double bx1, double by1, double bx2, double by2)
+{
+#if 0
+/* http://www.freelunchdesign.com/cgi-bin/codwiki.pl?DiscussionTopics/CollideMeUpBaby */
+
+    double a1, b1, c1, a2, b2, c2, r1, r2, r3, r4, denom;
+
+    a1 = ay2 - ay1;
+    b1 = ax1 - ax2;
+    c1 = (ax2 * ay1) - (ax1 * ay2);
+    r3 = (a1 * bx1) + (b1 * by1) + c1;
+    r4 = (a1 * bx2) + (b1 * by2) + c1;
+    if ((r3 != 0.0) && (r4 != 0.0) && (r3*r4 > 0.0)) {
+	return 0;
+    }
+
+    a2 = by2 - by1;
+    b2 = bx1 - bx2;
+    c2 = (bx2 * by1) - (bx1 * by2);
+    r1 = (a2 * ax1) + (b2 * ay1) + c2;
+    r2 = (a2 * ax2) + (b2 * ay2) + c2;
+    if ((r1 != 0.0) && (r2 != 0.0) && (r1*r2 > 0.0)) {
+	return 0;
+    }
+
+    denom = (a1 * b2) - (a2 * b1);
+    return (denom != 0.0);
+#else
+    /*
+     * A more efficient version. Two line segments intersect if, when seen
+     * from the perspective of one line, the two endpoints of the other
+     * segment lie on opposite sides of the line, and vice versa. "Lie on
+     * opposite sides" is computed by taking the cross products and seeing if
+     * they are of opposite signs.
+     */
+
+    double dx, dy, dx1, dy1;
+
+    dx = ax2 - ax1;
+    dy = ay2 - ay1;
+    dx1 = bx1 - ax1;
+    dy1 = by1 - ay1;
+    if ((dx*dy1-dy*dx1 > 0.0) == (dx*(by2-ay1)-dy*(bx2-ax1) > 0.0)) {
+	return 0;
+    }
+    dx = bx2 - bx1;
+    dy = by2 - by1;
+    if ((dy*dx1-dx*dy1 > 0.0) == (dx*(ay2-by1)-dy*(ax2-bx1) > 0.0)) {
+	return 0;
+    }
+    return 1;
+#endif
+}
+
+int
+TkIntersectAngledTextLayout(
+    Tk_TextLayout layout,	/* Layout information, from a previous call to
+				 * Tk_ComputeTextLayout(). */
+    int x, int y,		/* Upper-left hand corner, in pixels, of
+				 * rectangular area to compare with text
+				 * layout. Coordinates are with respect to the
+				 * upper-left hand corner of the text layout
+				 * itself. */
+    int width, int height,	/* The width and height of the above
+				 * rectangular area, in pixels. */
+    double angle)
+{
+    int i, x1, y1, x2, y2;
+    TextLayout *layoutPtr;
+    LayoutChunk *chunkPtr;
+    TkFont *fontPtr;
+    double c = cos(angle * PI/180.0), s = sin(angle * PI/180.0);
+    double rx[4], ry[4];
+
+    if (angle == 0.0) {
+	return Tk_IntersectTextLayout(layout, x, y, width, height);
+    }
+
+    /*
+     * Compute the coordinates of the rectangle, rotated into text layout
+     * space.
+     */
+
+    rx[0] = x*c - y*s;
+    ry[0] = y*c + x*s;
+    rx[1] = (x+width)*c - y*s;
+    ry[1] = y*c + (x+width)*s;
+    rx[2] = (x+width)*c - (y+height)*s;
+    ry[2] = (y+height)*c + (x+width)*s;
+    rx[3] = x*c - (y+height)*s;
+    ry[3] = (y+height)*c + x*s;
+
+    /*
+     * Want to know if all chunks are inside the rectangle, or if there is any
+     * overlap. First, we check to see if all chunks are inside; if and only
+     * if they are, we're in the "inside" case.
+     */
+
+    layoutPtr = (TextLayout *) layout;
+    chunkPtr = layoutPtr->chunks;
+    fontPtr = (TkFont *) layoutPtr->tkfont;
+
+    for (i=0 ; i<layoutPtr->numChunks ; i++,chunkPtr++) {
+	if (chunkPtr->start[0] == '\n') {
+	    /*
+	     * Newline characters are not counted when computing area
+	     * intersection (but tab characters would still be considered).
+	     */
+
+	    continue;
+	}
+
+	x1 = chunkPtr->x;
+	y1 = chunkPtr->y - fontPtr->fm.ascent;
+	x2 = chunkPtr->x + chunkPtr->displayWidth;
+	y2 = chunkPtr->y + fontPtr->fm.descent;
+	if (	!PointInQuadrilateral(rx, ry, x1, y1) ||
+		!PointInQuadrilateral(rx, ry, x2, y1) ||
+		!PointInQuadrilateral(rx, ry, x2, y2) ||
+		!PointInQuadrilateral(rx, ry, x1, y2)) {
+	    goto notInside;
+	}
+    }
+    return 1;
+
+    /*
+     * Next, check to see if all the points of the rectangle are inside a
+     * single chunk; if they are, we're in an "overlap" case.
+     */
+
+  notInside:
+    chunkPtr = layoutPtr->chunks;
+
+    for (i=0 ; i<layoutPtr->numChunks ; i++,chunkPtr++) {
+	double cx[4], cy[4];
+	if (chunkPtr->start[0] == '\n') {
+	    /*
+	     * Newline characters are not counted when computing area
+	     * intersection (but tab characters would still be considered).
+	     */
+
+	    continue;
+	}
+
+	cx[0] = cx[3] = chunkPtr->x;
+	cy[0] = cy[1] = chunkPtr->y - fontPtr->fm.ascent;
+	cx[1] = cx[2] = chunkPtr->x + chunkPtr->displayWidth;
+	cy[2] = cy[3] = chunkPtr->y + fontPtr->fm.descent;
+	if (	!PointInQuadrilateral(cx, cy, rx[0], ry[0]) ||
+		!PointInQuadrilateral(cx, cy, rx[1], ry[1]) ||
+		!PointInQuadrilateral(cx, cy, rx[2], ry[2]) ||
+		!PointInQuadrilateral(cx, cy, rx[3], ry[3])) {
+	    goto notReverseInside;
+	}
+    }
+    return 0;
+
+    /*
+     * If we're overlapping now, we must be partially in and out of at least
+     * one chunk. If that is the case, there must be one line segment of the
+     * rectangle that is touching or crossing a line segment of a chunk.
+     */
+
+  notReverseInside:
+    chunkPtr = layoutPtr->chunks;
+
+    for (i=0 ; i<layoutPtr->numChunks ; i++,chunkPtr++) {
+	int j;
+
+	if (chunkPtr->start[0] == '\n') {
+	    /*
+	     * Newline characters are not counted when computing area
+	     * intersection (but tab characters would still be considered).
+	     */
+
+	    continue;
+	}
+
+	x1 = chunkPtr->x;
+	y1 = chunkPtr->y - fontPtr->fm.ascent;
+	x2 = chunkPtr->x + chunkPtr->displayWidth;
+	y2 = chunkPtr->y + fontPtr->fm.descent;
+
+	for (j=0 ; j<4 ; j++) {
+	    int k = (j+1) % 4;
+	    if (    SidesIntersect(rx[j],ry[j], rx[k],ry[k], x1,y1, x2,y1) ||
+		    SidesIntersect(rx[j],ry[j], rx[k],ry[k], x2,y1, x2,y2) ||
+		    SidesIntersect(rx[j],ry[j], rx[k],ry[k], x2,y2, x1,y2) ||
+		    SidesIntersect(rx[j],ry[j], rx[k],ry[k], x1,y2, x1,y1)) {
+		return 0;
+	    }
+	}
+    }
+
+    /*
+     * They must be wholly non-overlapping.
+     */
+
+    return -1;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
  * Tk_TextLayoutToPostscript --
  *
  *	Outputs the contents of a text layout in Postscript format. The set of
Index: generic/tkInt.h
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkInt.h,v
retrieving revision 1.92
diff -u -r1.92 tkInt.h
--- generic/tkInt.h	9 Nov 2008 21:53:39 -0000	1.92
+++ generic/tkInt.h	18 Nov 2008 01:31:33 -0000
@@ -1214,6 +1214,19 @@
 			    Window *parentPtr, Tk_Window tkParent,
 			    TkBusy busy);
 
+MODULE_SCOPE void	TkDrawAngledTextLayout(Display *display,
+			    Drawable drawable, GC gc, Tk_TextLayout layout,
+			    int x, int y, double angle, int firstChar,
+			    int lastChar);
+MODULE_SCOPE void	TkpDrawAngledChars(Display *display,Drawable drawable,
+			    GC gc, Tk_Font tkfont, const char *source,
+			    int numBytes, double x, double y, double angle);
+MODULE_SCOPE void	TkUnderlineAngledTextLayout(Display *display,
+			    Drawable drawable, GC gc, Tk_TextLayout layout,
+			    int x, int y, double angle, int underline);
+MODULE_SCOPE int	TkIntersectAngledTextLayout(Tk_TextLayout layout,
+			    int x,int y, int width, int height, double angle);
+
 /*
  * Unsupported commands.
  */
Index: library/prolog.ps
===================================================================
RCS file: /cvsroot/tktoolkit/tk/library/prolog.ps,v
retrieving revision 1.3
diff -u -r1.3 prolog.ps
--- library/prolog.ps	4 Feb 1999 20:59:48 -0000	1.3
+++ library/prolog.ps	18 Nov 2008 01:31:34 -0000
@@ -189,11 +189,12 @@
     } if
 } bind def
 
-% x y strings spacing xoffset yoffset justify stipple DrawText --
+% angle x y strings spacing xoffset yoffset justify stipple DrawText --
 % This procedure does all of the real work of drawing text.  The
 % color and font must already have been set by the caller, and the
 % following arguments must be on the stack:
 %
+% angle -	Angle through which to rotate text, in degrees.
 % x, y -	Coordinates at which to draw text.
 % strings -	An array of strings, one for each line of the text item,
 %		in order from top to bottom.
@@ -235,11 +236,12 @@
     exch pop exch sub /height exch def pop
     newpath
 
-    % Translate coordinates first so that the origin is at the upper-left
-    % corner of the text's bounding box. Remember that x and y for
-    % positioning are still on the stack.
+    % Translate and rotatecoordinates first so that the origin is at the
+    % upper-left corner of the text's bounding box. Remember that angle, x and
+    % y for positioning are still on the stack.
 
     translate
+    rotate
     lineLength xoffset mul
     strings length 1 sub spacing mul height add yoffset mul translate
 
Index: macosx/tkMacOSXFont.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/macosx/tkMacOSXFont.c,v
retrieving revision 1.40
diff -u -r1.40 tkMacOSXFont.c
--- macosx/tkMacOSXFont.c	2 Nov 2008 09:54:02 -0000	1.40
+++ macosx/tkMacOSXFont.c	18 Nov 2008 01:31:38 -0000
@@ -262,7 +262,10 @@
 static void InitSystemFonts(TkMainInfo *mainPtr);
 static int CreateNamedSystemFont(Tcl_Interp *interp, Tk_Window tkwin,
 	const char* name, TkFontAttributes *faPtr);
-
+static void		DrawCharsInContext(Display *display,
+			    Drawable drawable, GC gc, Tk_Font tkfont,
+			    const char *source, int numBytes, int rangeStart,
+			    int rangeLength, int x, int y, double angle);
 
 /*
  *-------------------------------------------------------------------------
@@ -1135,8 +1138,31 @@
     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);
+    DrawCharsInContext(display, drawable, gc, tkfont, source, numBytes,
+	    0, numBytes, x, y, 0.0);
+}
+
+void
+TkpDrawAngledChars(
+    Display *display,		/* Display on which to draw. */
+    Drawable drawable,		/* Window or pixmap in which to draw. */
+    GC gc,			/* Graphics context for drawing characters. */
+    Tk_Font tkfont,		/* Font in which characters will be drawn;
+				 * must be the same as font used in GC. */
+    const char *source,		/* UTF-8 string to be displayed. Need not be
+				 * '\0' terminated. All Tk meta-characters
+				 * (tabs, control characters, and newlines)
+				 * should be stripped out of the string that
+				 * is passed to this function. If they are not
+				 * stripped out, they will be displayed as
+				 * regular printing characters. */
+    int numBytes,		/* Number of bytes in string. */
+    double x, double y,		/* Coordinates at which to place origin of
+				 * string when drawing. */
+    double angle)		/* What angle to put text at, in degrees. */
+{
+    DrawCharsInContext(display, drawable, gc, tkfont, source, numBytes,
+	    0, numBytes, x, y, angle);
 }
 
 /*
@@ -1180,6 +1206,32 @@
 				 * whole (not just the range) string when
 				 * drawing. */
 {
+    DrawCharsInContext(display, drawable, gc, tkfont, source, numBytes,
+	    rangeStart, rangeLength, x, y, 0.0);
+}
+
+static void
+DrawCharsInContext(
+    Display *display,		/* Display on which to draw. */
+    Drawable drawable,		/* Window or pixmap in which to draw. */
+    GC gc,			/* Graphics context for drawing characters. */
+    Tk_Font tkfont,		/* Font in which characters will be drawn; must
+				 * be the same as font used in GC. */
+    const char * source,	/* UTF-8 string to be displayed. Need not be
+				 * '\0' terminated. All Tk meta-characters
+				 * (tabs, control characters, and newlines)
+				 * should be stripped out of the string that
+				 * is passed to this function. If they are not
+				 * stripped out, they will be displayed as
+				 * regular printing characters. */
+    int numBytes,		/* Number of bytes in string. */
+    int rangeStart,		/* Index of first byte to draw. */
+    int rangeLength,		/* Length of range to draw in bytes. */
+    int x, int y,		/* Coordinates at which to place origin of the
+				 * whole (not just the range) string when
+				 * drawing. */
+    double angle)
+{
     const MacFont * fontPtr = (const MacFont *) tkfont;
     MacDrawable *macWin = (MacDrawable *) drawable;
     Fixed fx, fy;
@@ -1242,6 +1294,14 @@
     urstart = Tcl_NumUtfChars(source, rangeStart);
     urlen = Tcl_NumUtfChars(source+rangeStart,rangeLength);
 
+    /* Rotate the coordinate system for Quarz drawing. */
+    if (drawingContext.context && angle != 0.0) {
+#define PI	3.14159265358979323846
+	CGContextTranslateCTM(drawingContext.context, x, y);
+	CGContextRotateCTM(drawingContext.context, angle * PI/180.0);
+	CGContextTranslateCTM(drawingContext.context, -x, -y);
+    }
+
     ChkErr(ATSUDrawText, fontPtr->atsuLayout, lineOffset+urstart, urlen, fx,
 	    fy);
 
Index: unix/tkUnixFont.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/unix/tkUnixFont.c,v
retrieving revision 1.38
diff -u -r1.38 tkUnixFont.c
--- unix/tkUnixFont.c	5 Nov 2008 22:20:59 -0000	1.38
+++ unix/tkUnixFont.c	18 Nov 2008 01:32:02 -0000
@@ -139,9 +139,9 @@
  */
 
 typedef struct EncodingAlias {
-    const char *realName;		/* The real name of the encoding to load if
+    const char *realName;	/* The real name of the encoding to load if
 				 * the provided name matched the pattern. */
-    const char *aliasPattern;		/* Pattern for encoding name, of the form that
+    const char *aliasPattern;	/* Pattern for encoding name, of the form that
 				 * is acceptable to Tcl_StringMatch. */
 } EncodingAlias;
 
@@ -3018,6 +3018,284 @@
     return name;
 }
 
+static inline XImage *
+GetImageOfText(
+    Display *display,		/* Display on which to draw. */
+    Drawable drawable,		/* Window or pixmap in which to draw. */
+    Tk_Font tkfont,		/* Font in which characters will be drawn. */
+    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 *realWidthPtr, int *realHeightPtr)
+{
+    int width, height;
+    TkFont *fontPtr = (TkFont *) tkfont;
+    Pixmap bitmap;
+    GC bitmapGC;
+    XGCValues values;
+    XImage *image;
+
+    (void) Tk_MeasureChars(tkfont, source, numBytes, -1, 0, &width);
+    height = fontPtr->fm.ascent + fontPtr->fm.descent;
+
+    bitmap = Tk_GetPixmap(display, drawable, width, height, 1);
+    values.graphics_exposures = False;
+    values.foreground = BlackPixel(display, DefaultScreen(display));
+    bitmapGC = XCreateGC(display, bitmap, GCGraphicsExposures|GCForeground,
+	    &values);
+    XFillRectangle(display, bitmap, bitmapGC, 0, 0, width, height);
+
+    values.font = Tk_FontId(tkfont);
+    values.foreground = WhitePixel(display, DefaultScreen(display));
+    values.background = BlackPixel(display, DefaultScreen(display));
+    XChangeGC(display, bitmapGC, GCFont|GCForeground|GCBackground, &values);
+    Tk_DrawChars(display, bitmap, bitmapGC, tkfont, source, numBytes, 0,
+	    fontPtr->fm.ascent);
+    XFreeGC(display, bitmapGC);
+
+    image = XGetImage(display, bitmap, 0, 0, width, height, AllPlanes,
+	    ZPixmap);
+    Tk_FreePixmap(display, bitmap);
+
+    *realWidthPtr = width;
+    *realHeightPtr = height;
+    return image;
+}
+
+static inline XImage *
+InitDestImage(
+    Display *display,
+    Drawable drawable,
+    int width,
+    int height,
+    Pixmap *bitmapPtr)
+{
+    Pixmap bitmap;
+    XImage *image;
+    GC bitmapGC;
+    XGCValues values;
+
+    bitmap = Tk_GetPixmap(display, drawable, width, height, 1);
+    values.graphics_exposures = False;
+    values.foreground = BlackPixel(display, DefaultScreen(display));
+    bitmapGC = XCreateGC(display, bitmap, GCGraphicsExposures|GCForeground,
+	    &values);
+    XFillRectangle(display, bitmap, bitmapGC, 0, 0, width, height);
+    XFreeGC(display, bitmapGC);
+
+    image = XGetImage(display, bitmap, 0, 0, width, height, AllPlanes,
+	    ZPixmap);
+    *bitmapPtr = bitmap;
+    return image;
+}
+
+void
+TkpDrawAngledChars(
+    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,
+    double angle)
+{
+    if (angle == 0.0) {
+	Tk_DrawChars(display, drawable, gc, tkfont, source, numBytes, x, y);
+    } else {
+#define PI	3.14159265358979323846
+	double sinA = sin(angle * PI/180.0), cosA = cos(angle * PI/180.0);
+	int bufHeight, bufWidth, srcWidth, srcHeight, i, j, dx, dy;
+	Pixmap buf;
+	XImage *srcImage = GetImageOfText(display, drawable, tkfont, source,
+		numBytes, &srcWidth, &srcHeight);
+	XImage *dstImage;
+	enum {Q0=1,R1,Q1,R2,Q2,R3,Q3} quadrant;
+	GC bwgc, cpgc;
+	XGCValues values;
+	int ascent = ((TkFont *) tkfont)->fm.ascent;
+
+	/*
+	 * First, work out what quadrant we are operating in. We also handle
+	 * the rectilinear rotations as special cases. Conceptually, there's
+	 * also R0 (angle == 0.0) but that has been already handled as a
+	 * special case above.
+	 *
+	 *        R1
+	 *   Q1   |   Q0
+	 *        |
+	 * R2 ----+---- R0
+	 *        |
+	 *   Q2   |   Q3
+	 *        R3
+	 */
+
+	if (angle < 90.0) {
+	    quadrant = Q0;
+	} else if (angle == 90.0) {
+	    quadrant = R1;
+	} else if (angle < 180.0) {
+	    quadrant = Q1;
+	} else if (angle == 180.0) {
+	    quadrant = R2;
+	} else if (angle < 270.0) {
+	    quadrant = Q2;
+	} else if (angle == 270.0) {
+	    quadrant = R3;
+	} else {
+	    quadrant = Q3;
+	}
+
+	if (srcImage == NULL) {
+	    return;
+	}
+	bufWidth = srcWidth*fabs(cosA) + srcHeight*fabs(sinA);
+	bufHeight = srcHeight*fabs(cosA) + srcWidth*fabs(sinA);
+	dstImage = InitDestImage(display, drawable, bufWidth,bufHeight, &buf);
+	if (dstImage == NULL) {
+	    Tk_FreePixmap(display, buf);
+	    XDestroyImage(srcImage);
+	    return;
+	}
+
+	/*
+	 * Do the rotation, setting or resetting pixels in the destination
+	 * image dependent on whether the corresponding pixel (after rotation
+	 * to source image space) is set.
+	 */
+
+	for (i=0 ; i<srcWidth ; i++) {
+	    for (j=0 ; j<srcHeight ; j++) {
+		switch (quadrant) {
+		case Q0:
+		    dx = floor(i*cosA + j*sinA + 0.5);
+		    dy = floor(j*cosA + (srcWidth - i)*sinA + 0.5);
+		    break;
+		case R1:
+		    dx = j;
+		    dy = srcWidth - i;
+		    break;
+		case Q1:
+		    dx = floor((i - srcWidth)*cosA + j*sinA + 0.5);
+		    dy = floor((srcWidth-i)*sinA + (j-srcHeight)*cosA + 0.5);
+		    break;
+		case R2:
+		    dx = srcWidth - i;
+		    dy = srcHeight - j;
+		    break;
+		case Q2:
+		    dx = floor((i-srcWidth)*cosA + (j-srcHeight)*sinA + 0.5);
+		    dy = floor((j - srcHeight)*cosA - i*sinA + 0.5);
+		    break;
+		case R3:
+		    dx = srcHeight - j;
+		    dy = i;
+		    break;
+		default:
+		    dx = floor(i*cosA + (j - srcHeight)*sinA + 0.5);
+		    dy = floor(j*cosA - i*sinA + 0.5);
+		}
+
+		if (dx < 0 || dy < 0 || dx >= bufWidth || dy >= bufHeight) {
+		    continue;
+		}
+		XPutPixel(dstImage, dx, dy,
+			XGetPixel(dstImage,dx,dy) | XGetPixel(srcImage,i,j));
+	    }
+	}
+	XDestroyImage(srcImage);
+
+	/*
+	 * Schlep the data back to the Xserver.
+	 */
+
+	values.function = GXcopy;
+	values.foreground = WhitePixel(display, DefaultScreen(display));
+	values.background = BlackPixel(display, DefaultScreen(display));
+	bwgc = XCreateGC(display, buf, GCFunction|GCForeground|GCBackground,
+		&values);
+	XPutImage(display, buf, bwgc, dstImage, 0,0, 0,0, bufWidth,bufHeight);
+	XFreeGC(display, bwgc);
+	XDestroyImage(dstImage);
+
+	/*
+	 * Calculate where we want to draw the text.
+	 */
+
+	switch (quadrant) {
+	case Q0:
+	    dx = x;
+	    dy = y - srcWidth*sinA;
+	    break;
+	case R1:
+	    dx = x;
+	    dy = y - srcWidth;
+	    break;
+	case Q1:
+	    dx = x + srcWidth*cosA;
+	    dy = y + srcHeight*cosA - srcWidth*sinA;
+	    break;
+	case R2:
+	    dx = x - srcWidth;
+	    dy = y - srcHeight;
+	    break;
+	case Q2:
+	    dx = x + srcWidth*cosA + srcHeight*sinA;
+	    dy = y + srcHeight*cosA;
+	    break;
+	case R3:
+	    dx = x - srcHeight;
+	    dy = y;
+	    break;
+	default:
+	    dx = x + srcHeight*sinA;
+	    dy = y;
+	}
+
+	/*
+	 * Apply a correction to deal with the fact that we aren't told to
+	 * draw from our top-left corner but rather from the left-end of our
+	 * baseline.
+	 */
+
+	dx -= ascent*sinA;
+	dy -= ascent*cosA;
+
+	/*
+	 * Transfer the text to the screen. This is done by using it as a mask
+	 * and then drawing through that mask with the original drawing color.
+	 */
+
+	values.function = GXcopy;
+	values.fill_style = FillSolid;
+	values.clip_mask = buf;
+	values.clip_x_origin = dx;
+	values.clip_y_origin = dy;
+	cpgc = XCreateGC(display, drawable,
+		GCFunction|GCFillStyle|GCClipMask|GCClipXOrigin|GCClipYOrigin,
+		&values);
+	XCopyGC(display, gc, GCForeground, cpgc);
+	XFillRectangle(display, drawable, cpgc, dx, dy, bufWidth,
+		bufHeight);
+	XFreeGC(display, cpgc);
+
+	Tk_FreePixmap(display, buf);
+	return;
+    }
+}
+
 /*
  * Local Variables:
  * mode: c
Index: unix/tkUnixRFont.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/unix/tkUnixRFont.c,v
retrieving revision 1.26
diff -u -r1.26 tkUnixRFont.c
--- unix/tkUnixRFont.c	5 Nov 2008 22:20:59 -0000	1.26
+++ unix/tkUnixRFont.c	18 Nov 2008 01:32:03 -0000
@@ -16,10 +16,14 @@
 #include <X11/Xft/Xft.h>
 #include <ctype.h>
 
+#define PI	3.14159265358979323846
+
 typedef struct {
     XftFont *ftFont;
+    XftFont *ft0Font;
     FcPattern *source;
     FcCharSet *charset;
+    double angle;
 } UnixFtFace;
 
 typedef struct {
@@ -62,13 +66,15 @@
 static XftFont *
 GetFont(
     UnixFtFont *fontPtr,
-    FcChar32 ucs4)
+    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;
 	    }
@@ -79,34 +85,52 @@
     } else {
 	i = 0;
     }
-    if (!fontPtr->faces[i].ftFont) {
-	FcPattern *pat = FcFontRenderPrepare(0,
-	    fontPtr->pattern, fontPtr->faces[i].source);
-	XftFont *ftFont = XftFontOpenPattern(fontPtr->display, pat);
+    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 = {c, -s, s, c};
+	XftFont *ftFont;
 
+	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:
+	     * 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,
-			NULL);
+		    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.
+	     * The previous call should definitely not fail. Impossible to
+	     * proceed at this point.
 	     */
+
 	    Tcl_Panic("Cannot find a usable font.");
 	}
 
-	fontPtr->faces[i].ftFont = ftFont;
+	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 fontPtr->faces[i].ftFont;
+    return (angle==0.0? fontPtr->faces[i].ft0Font : fontPtr->faces[i].ftFont);
 }
 
 /*
@@ -126,10 +150,10 @@
     int weight, slant, size, pxsize;
     double ptsize;
 
-    (void)XftPatternGetString(ftFont->pattern, XFT_FAMILY, 0, familyPtr);
+    (void) XftPatternGetString(ftFont->pattern, XFT_FAMILY, 0, familyPtr);
     if (XftPatternGetDouble(ftFont->pattern, XFT_SIZE, 0,
 	    &ptsize) == XftResultMatch) {
-	size = (int)ptsize;
+	size = (int) ptsize;
     } else if (XftPatternGetInteger(ftFont->pattern, XFT_PIXEL_SIZE, 0,
 	    &pxsize) == XftResultMatch) {
 	size = -pxsize;
@@ -165,7 +189,8 @@
  * 	Fill in TkFontMetrics from an XftFont.
  */
 
-static void GetTkFontMetrics(
+static void
+GetTkFontMetrics(
     XftFont *ftFont,
     TkFontMetrics *fmPtr)
 {
@@ -222,7 +247,7 @@
     set = FcFontSort(0, pattern, FcTrue, NULL, &result);
     if (!set) {
 	FcPatternDestroy(pattern);
-	ckfree((char *)fontPtr);
+	ckfree((char *) fontPtr);
 	return NULL;
     }
 
@@ -237,6 +262,7 @@
 
     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) {
@@ -244,6 +270,7 @@
 	} else {
 	    fontPtr->faces[i].charset = 0;
 	}
+	fontPtr->faces[i].angle = 0.0;
     }
 
     fontPtr->display = Tk_Display(tkwin);
@@ -258,7 +285,8 @@
     /*
      * Fill in platform-specific fields of TkFont.
      */
-    ftFont = GetFont(fontPtr, 0);
+
+    ftFont = GetFont(fontPtr, 0, 0.0);
     fontPtr->font.fid = XLoadFont(Tk_Display(tkwin), "fixed");
     GetTkFontAttributes(ftFont, &fontPtr->font.fa);
     GetTkFontMetrics(ftFont, &fontPtr->font.fm);
@@ -272,19 +300,22 @@
 {
     Display *display = fontPtr->display;
     int i;
-    Tk_ErrorHandler handler = Tk_CreateErrorHandler(display, -1, -1, -1, NULL,
-	    (ClientData) NULL);
+    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((char *)fontPtr->faces);
+	ckfree((char *) fontPtr->faces);
     }
     if (fontPtr->pattern) {
 	FcPatternDestroy(fontPtr->pattern);
@@ -432,14 +463,15 @@
     resultPtr = Tcl_NewListObj(0, NULL);
 
     list = XftListFonts(Tk_Display(tkwin), Tk_ScreenNumber(tkwin),
-		(char*)0,		/* pattern elements */
-		XFT_FAMILY, (char*)0);	/* fields */
+		(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)
-	{
+		== XftResultMatch) {
 	    Tcl_Obj *strPtr = Tcl_NewStringObj(family, -1);
+
 	    Tcl_ListObjAppendElement(NULL, resultPtr, strPtr);
 	}
     }
@@ -517,7 +549,7 @@
 				/* Structure describing the logical font */
     FcChar32 ucs4 = (FcChar32) c;
 				/* UCS-4 character to map */
-    XftFont *ftFont = GetFont(fontPtr, ucs4);
+    XftFont *ftFont = GetFont(fontPtr, ucs4, 0.0);
 				/* Actual font used to render the character */
 
     GetTkFontAttributes(ftFont, faPtr);
@@ -566,7 +598,7 @@
 	Tcl_UniChar unichar;
 
 	clen = Tcl_UtfToUniChar(source, &unichar);
-	c = (FcChar32)unichar;
+	c = (FcChar32) unichar;
 
 	if (clen <= 0) {
 	    /*
@@ -592,7 +624,7 @@
 #if DEBUG_FONTSEL
 	string[len++] = (char) c;
 #endif /* DEBUG_FONTSEL */
-	ftFont = GetFont(fontPtr, c);
+	ftFont = GetFont(fontPtr, c, 0.0);
 
 	XftTextExtents32(fontPtr->display, ftFont, &c, 1, &extents);
 
@@ -674,8 +706,8 @@
 		DefaultVisual(display, fontPtr->screen),
 		DefaultColormap(display, fontPtr->screen));
     } else {
-	Tk_ErrorHandler handler = Tk_CreateErrorHandler(display, -1, -1, -1,
-		NULL, (ClientData) NULL);
+	Tk_ErrorHandler handler =
+		Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL);
 
 	XftDrawChange(fontPtr->ftDraw, drawable);
 	Tk_DeleteErrorHandler(handler);
@@ -707,7 +739,7 @@
 	source += clen;
 	numBytes -= clen;
 
-	ftFont = GetFont(fontPtr, c);
+	ftFont = GetFont(fontPtr, c, 0.0);
 	if (ftFont) {
 	    specs[nspec].font = ftFont;
 	    specs[nspec].glyph = XftCharIndex(fontPtr->display, ftFont, c);
@@ -729,3 +761,195 @@
 	XftDrawGlyphFontSpec(fontPtr->ftDraw, &fontPtr->color, specs, nspec);
     }
 }
+
+void
+TkpDrawAngledChars(
+    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;
+    XColor xcolor;
+#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);
+    if (values.foreground != fontPtr->color.pixel) {
+	xcolor.pixel = values.foreground;
+	XQueryColor(display, DefaultColormap(display, fontPtr->screen),
+		&xcolor);
+	fontPtr->color.color.red = xcolor.red;
+	fontPtr->color.color.green = xcolor.green;
+	fontPtr->color.color.blue = xcolor.blue;
+	fontPtr->color.color.alpha = 0xffff;
+	fontPtr->color.pixel = values.foreground;
+    }
+
+    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.
+	     */
+
+	    return;
+	}
+	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, &fontPtr->color, currentFtFont,
+			originX, originY, glyphs, nglyph);
+	    }
+	    originX = (int) floor(x + 0.5);
+	    originY = (int) floor(y + 0.5);
+	    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, &fontPtr->color, 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);
+    if (values.foreground != fontPtr->color.pixel) {
+	xcolor.pixel = values.foreground;
+	XQueryColor(display, DefaultColormap(display, fontPtr->screen),
+		&xcolor);
+	fontPtr->color.color.red = xcolor.red;
+	fontPtr->color.color.green = xcolor.green;
+	fontPtr->color.color.blue = xcolor.blue;
+	fontPtr->color.color.alpha = 0xffff;
+	fontPtr->color.pixel = values.foreground;
+    }
+    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.
+	     */
+
+	    return;
+	}
+	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 = (int) floor(x + 0.5);
+	    specs[nspec].y = (int) floor(y + 0.5);
+	    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, &fontPtr->color,
+			specs, nspec);
+		nspec = 0;
+	    }
+	}
+    }
+    if (nspec) {
+	XftDrawGlyphFontSpec(fontPtr->ftDraw, &fontPtr->color, specs, nspec);
+    }
+#endif /* XFT_HAS_FIXED_ROTATED_PLACEMENT */
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */