Tk Source Code

Artifact [4564e6a1]
Login

Artifact 4564e6a15f788f2b606af92e264796da28783e03:

Attachment "tk8.5a5_atext.patch" to ticket [1611359f] added by dkf 2006-12-08 16:08:51.
diff -Naur tk8.5a5/generic/tkCanvText.c tk8.5a5_with_atext/generic/tkCanvText.c
--- tk8.5a5/generic/tkCanvText.c	2006-01-12 13:38:22.000000000 +0000
+++ tk8.5a5_with_atext/generic/tkCanvText.c	2006-12-07 23:19:50.602634413 +0000
@@ -38,6 +38,7 @@
     double x, y;		/* Positioning point for text. */
     int insertPos;		/* Character index of character just before
 				 * which the insertion cursor is displayed. */
+    double angle;               /* Angle of inclination of the text */        
 
     /*
      * Configuration settings that are updated by Tk_ConfigureWidget.
@@ -105,6 +106,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", (char *) NULL, (char *) NULL,
+        "0.0", Tk_Offset(TextItem, angle), TK_CONFIG_NULL_OK},
     {TK_CONFIG_COLOR, "-disabledfill", NULL, NULL,
 	NULL, Tk_Offset(TextItem, disabledColor), TK_CONFIG_NULL_OK},
     {TK_CONFIG_BITMAP, "-disabledstipple", NULL, NULL,
@@ -271,6 +274,7 @@
     textPtr->gc		= None;
     textPtr->selTextGC	= None;
     textPtr->cursorOffGC = None;
+    textPtr->angle = 0.e0;
 
     /*
      * Process the arguments to fill in the item record. Only 1 (list) or 2 (x
@@ -610,6 +614,7 @@
 {
     Tk_CanvasTextInfo *textInfoPtr;
     int leftX, topY, width, height, fudge;
+
     Tk_State state = textPtr->header.state;
 
     if(state == TK_STATE_NULL) {
@@ -619,7 +624,7 @@
     Tk_FreeTextLayout(textPtr->textLayout);
     textPtr->textLayout = Tk_ComputeTextLayout(textPtr->tkfont,
 	    textPtr->text, textPtr->numChars, textPtr->width,
-	    textPtr->justify, 0, &width, &height);
+	    textPtr->justify, 0, textPtr->angle, &width, &height);
 
     if (state == TK_STATE_HIDDEN || textPtr->color == NULL) {
 	width = height = 0;
@@ -687,6 +692,19 @@
     textPtr->header.y1 = topY;
     textPtr->header.x2 = leftX + width + fudge;
     textPtr->header.y2 = topY + height;
+
+    /* Calculate a bounding-box for angles text */
+    /* This is WIP */
+    if (fabs(textPtr->angle) > 1.e-2) {
+      /* Rotate the box about x1,y1 */
+      double c_ang = cos(textPtr->angle*0.0174532925199);
+      double s_ang = sin(textPtr->angle*0.0174532925199);
+      double xmax = textPtr->header.x1 + (textPtr->header.x2-textPtr->header.x1)*c_ang - (textPtr->header.y2-textPtr->header.y1)*s_ang;
+      double ymax = textPtr->header.y1 + (textPtr->header.x2-textPtr->header.x1)*s_ang + (textPtr->header.y2-textPtr->header.y1)*c_ang;
+      printf("ComputeTextBox: (%d,%d) <-> (%d,%d)\n",textPtr->header.x2,textPtr->header.y2,(int) xmax, (int) ymax);
+/*       textPtr->header.x2 = (int) xmax; */
+/*       textPtr->header.y2 = (int) ymax; */
+    }
 }
 
 /*
@@ -725,7 +743,6 @@
 
     textPtr = (TextItem *) itemPtr;
     textInfoPtr = textPtr->textInfoPtr;
-
     if(state == TK_STATE_NULL) {
 	state = ((TkCanvas *)canvas)->canvas_state;
     }
@@ -861,15 +878,15 @@
 
     if ((selFirstChar >= 0) && (textPtr->selTextGC != textPtr->gc)) {
 	Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
-	    drawableX, drawableY, 0, selFirstChar);
+	    drawableX, drawableY, 0, selFirstChar, textPtr->angle);
 	Tk_DrawTextLayout(display, drawable, textPtr->selTextGC,
 	    textPtr->textLayout, drawableX, drawableY, selFirstChar,
-	    selLastChar + 1);
+	    selLastChar + 1, textPtr->angle);
 	Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
-	    drawableX, drawableY, selLastChar + 1, -1);
+	    drawableX, drawableY, selLastChar + 1, -1, textPtr->angle);
     } else {
 	Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
-	    drawableX, drawableY, 0, -1);
+	    drawableX, drawableY, 0, -1, textPtr->angle);
     }
     Tk_UnderlineTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
 	    drawableX, drawableY, textPtr->underline);
diff -Naur tk8.5a5/generic/tkDecls.h tk8.5a5_with_atext/generic/tkDecls.h
--- tk8.5a5/generic/tkDecls.h	2006-10-23 14:45:21.000000000 +0100
+++ tk8.5a5_with_atext/generic/tkDecls.h	2006-12-07 20:30:24.485785095 +0000
@@ -246,7 +246,7 @@
 EXTERN Tk_TextLayout	Tk_ComputeTextLayout _ANSI_ARGS_((Tk_Font font, 
 				CONST char * str, int numChars, 
 				int wrapLength, Tk_Justify justify, 
-				int flags, int * widthPtr, int * heightPtr));
+				int flags, double angle, int * widthPtr, int * heightPtr));
 #endif
 #ifndef Tk_CoordsToWindow_TCL_DECLARED
 #define Tk_CoordsToWindow_TCL_DECLARED
@@ -431,7 +431,7 @@
 EXTERN void		Tk_DrawChars _ANSI_ARGS_((Display * display, 
 				Drawable drawable, GC gc, Tk_Font tkfont, 
 				CONST char * source, int numBytes, int x, 
-				int y));
+				int y, double angle));
 #endif
 #ifndef Tk_DrawFocusHighlight_TCL_DECLARED
 #define Tk_DrawFocusHighlight_TCL_DECLARED
@@ -445,7 +445,7 @@
 EXTERN void		Tk_DrawTextLayout _ANSI_ARGS_((Display * display, 
 				Drawable drawable, GC gc, 
 				Tk_TextLayout layout, int x, int y, 
-				int firstChar, int lastChar));
+				int firstChar, int lastChar, double angle));
 #endif
 #ifndef Tk_Fill3DPolygon_TCL_DECLARED
 #define Tk_Fill3DPolygon_TCL_DECLARED
@@ -1793,7 +1793,7 @@
     int (*tk_ConfigureValue) _ANSI_ARGS_((Tcl_Interp * interp, Tk_Window tkwin, Tk_ConfigSpec * specs, char * widgRec, CONST char * argvName, int flags)); /* 28 */
     int (*tk_ConfigureWidget) _ANSI_ARGS_((Tcl_Interp * interp, Tk_Window tkwin, Tk_ConfigSpec * specs, int argc, CONST84 char ** argv, char * widgRec, int flags)); /* 29 */
     void (*tk_ConfigureWindow) _ANSI_ARGS_((Tk_Window tkwin, unsigned int valueMask, XWindowChanges * valuePtr)); /* 30 */
-    Tk_TextLayout (*tk_ComputeTextLayout) _ANSI_ARGS_((Tk_Font font, CONST char * str, int numChars, int wrapLength, Tk_Justify justify, int flags, int * widthPtr, int * heightPtr)); /* 31 */
+    Tk_TextLayout (*tk_ComputeTextLayout) _ANSI_ARGS_((Tk_Font font, CONST char * str, int numChars, int wrapLength, Tk_Justify justify, int flags, double angle, int * widthPtr, int * heightPtr)); /* 31 */
     Tk_Window (*tk_CoordsToWindow) _ANSI_ARGS_((int rootX, int rootY, Tk_Window tkwin)); /* 32 */
     unsigned long (*tk_CreateBinding) _ANSI_ARGS_((Tcl_Interp * interp, Tk_BindingTable bindingTable, ClientData object, CONST char * eventStr, CONST char * command, int append)); /* 33 */
     Tk_BindingTable (*tk_CreateBindingTable) _ANSI_ARGS_((Tcl_Interp * interp)); /* 34 */
@@ -1821,9 +1821,9 @@
     int (*tk_DistanceToTextLayout) _ANSI_ARGS_((Tk_TextLayout layout, int x, int y)); /* 56 */
     void (*tk_Draw3DPolygon) _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable, Tk_3DBorder border, XPoint * pointPtr, int numPoints, int borderWidth, int leftRelief)); /* 57 */
     void (*tk_Draw3DRectangle) _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable, Tk_3DBorder border, int x, int y, int width, int height, int borderWidth, int relief)); /* 58 */
-    void (*tk_DrawChars) _ANSI_ARGS_((Display * display, Drawable drawable, GC gc, Tk_Font tkfont, CONST char * source, int numBytes, int x, int y)); /* 59 */
+    void (*tk_DrawChars) _ANSI_ARGS_((Display * display, Drawable drawable, GC gc, Tk_Font tkfont, CONST char * source, int numBytes, int x, int y, double angle)); /* 59 */
     void (*tk_DrawFocusHighlight) _ANSI_ARGS_((Tk_Window tkwin, GC gc, int width, Drawable drawable)); /* 60 */
-    void (*tk_DrawTextLayout) _ANSI_ARGS_((Display * display, Drawable drawable, GC gc, Tk_TextLayout layout, int x, int y, int firstChar, int lastChar)); /* 61 */
+    void (*tk_DrawTextLayout) _ANSI_ARGS_((Display * display, Drawable drawable, GC gc, Tk_TextLayout layout, int x, int y, int firstChar, int lastChar, double angle)); /* 61 */
     void (*tk_Fill3DPolygon) _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable, Tk_3DBorder border, XPoint * pointPtr, int numPoints, int borderWidth, int leftRelief)); /* 62 */
     void (*tk_Fill3DRectangle) _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable, Tk_3DBorder border, int x, int y, int width, int height, int borderWidth, int relief)); /* 63 */
     Tk_PhotoHandle (*tk_FindPhoto) _ANSI_ARGS_((Tcl_Interp * interp, CONST char * imageName)); /* 64 */
diff -Naur tk8.5a5/generic/tkEntry.c tk8.5a5_with_atext/generic/tkEntry.c
--- tk8.5a5/generic/tkEntry.c	2006-09-10 18:06:31.000000000 +0100
+++ tk8.5a5_with_atext/generic/tkEntry.c	2006-12-07 20:04:19.797253260 +0000
@@ -1693,8 +1693,8 @@
      */
 
     Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->textGC,
-	    entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
-	    entryPtr->leftIndex, entryPtr->numChars);
+		      entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
+	    entryPtr->leftIndex, entryPtr->numChars, 0.0);
 
     if (showSelection && (entryPtr->state != STATE_DISABLED)
 	    && (entryPtr->selTextGC != entryPtr->textGC)
@@ -1708,7 +1708,7 @@
 	}
 	Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->selTextGC,
 		entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
-		selFirst, entryPtr->selectLast);
+		selFirst, entryPtr->selectLast, 0.0);
     }
 
     if (entryPtr->type == TK_SPINBOX) {
@@ -1907,7 +1907,7 @@
     Tk_FreeTextLayout(entryPtr->textLayout);
     entryPtr->textLayout = Tk_ComputeTextLayout(entryPtr->tkfont,
 	    entryPtr->displayString, entryPtr->numChars, 0,
-	    entryPtr->justify, TK_IGNORE_NEWLINES, &totalLength, &height);
+	    entryPtr->justify, TK_IGNORE_NEWLINES, 0.0, &totalLength, &height);
 
     entryPtr->layoutY = (Tk_Height(entryPtr->tkwin) - height) / 2;
 
diff -Naur tk8.5a5/generic/tkFont.c tk8.5a5_with_atext/generic/tkFont.c
--- tk8.5a5/generic/tkFont.c	2006-03-22 00:21:16.000000000 +0000
+++ tk8.5a5_with_atext/generic/tkFont.c	2006-12-07 23:06:47.443756422 +0000
@@ -83,6 +83,7 @@
     int totalWidth;		/* Width in pixels of this chunk. Used when
 				 * hit testing the invisible spaces at the end
 				 * of a chunk. */
+    int disp_x, disp_y;         /* As for (x,y) but for angled text strings */
     int displayWidth;		/* Width in pixels of the displayable
 				 * characters in this chunk. Can be less than
 				 * width if extra space characters were
@@ -335,7 +336,7 @@
 			    CONST TkFontAttributes *faPtr, Tcl_Obj *objPtr);
 static LayoutChunk *	NewChunk(TextLayout **layoutPtrPtr, int *maxPtr,
 			    CONST char *start, int numChars, int curX,
-			    int newX, int y);
+				 int newX, int y, int height, double dir[2]);
 static int		ParseFontNameObj(Tcl_Interp *interp, Tk_Window tkwin,
 			    Tcl_Obj *objPtr, TkFontAttributes *faPtr);
 static void		RecomputeWidgets(TkWindow *winPtr);
@@ -1844,6 +1845,7 @@
 				 * expanded. TK_IGNORE_NEWLINES means that
 				 * newline characters should not cause a line
 				 * break. */
+    double angle,               /* Angle of string */
     int *widthPtr,		/* Filled with width of string. */
     int *heightPtr)		/* Filled with height of string. */
 {
@@ -1857,6 +1859,7 @@
     Tcl_DString lineBuffer;
     int *lineLengths;
     int curLine, layoutHeight;
+    double dir[2];
 
     Tcl_DStringInit(&lineBuffer);
 
@@ -1875,6 +1878,11 @@
 
     height = fmPtr->ascent + fmPtr->descent;
 
+    /* Set the direction at which the text is descending as we  move to the next line, i.e. (0,-1) for horizontal text */
+    /* For tabs the direction will be (-dir[1],dir[0]) */
+    dir[0] = sin(angle*0.0174532925199);
+    dir[1] = -cos(angle*0.0174532925199);
+
     if (numChars < 0) {
 	numChars = Tcl_NumUtfChars(string, -1);
     }
@@ -1942,7 +1950,7 @@
 	    flags &= ~TK_AT_LEAST_ONE;
 	    if (bytesThisChunk > 0) {
 		chunkPtr = NewChunk(&layoutPtr, &maxChunks, start,
-			bytesThisChunk, curX, newX, baseline);
+			bytesThisChunk, curX, newX, baseline, height, dir);
 
 		start += bytesThisChunk;
 		curX = newX;
@@ -1962,7 +1970,7 @@
 		newX = curX + fontPtr->tabWidth;
 		newX -= newX % fontPtr->tabWidth;
 		NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX,
-			baseline)->numDisplayChars = -1;
+			baseline, height, dir)->numDisplayChars = -1;
 		start++;
 		if ((start < end) &&
 			((wrapLength <= 0) || (newX <= wrapLength))) {
@@ -1976,7 +1984,7 @@
 		}
 	    } else {
 		NewChunk(&layoutPtr, &maxChunks, start, 1, curX, curX,
-			baseline)->numDisplayChars = -1;
+			baseline, height, dir)->numDisplayChars = -1;
 		start++;
 		goto wrapLine;
 	    }
@@ -2052,7 +2060,7 @@
     if ((layoutPtr->numChunks > 0) && ((flags & TK_IGNORE_NEWLINES) == 0)) {
 	if (layoutPtr->chunks[layoutPtr->numChunks - 1].start[0] == '\n') {
 	    chunkPtr = NewChunk(&layoutPtr, &maxChunks, start, 0, curX,
-		    curX, baseline);
+		    curX, baseline, height, dir);
 	    chunkPtr->numDisplayChars = -1;
 	    Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
 	    baseline += height;
@@ -2077,6 +2085,8 @@
 	layoutPtr->chunks[0].numDisplayChars	= -1;
 	layoutPtr->chunks[0].x			= 0;
 	layoutPtr->chunks[0].y			= fmPtr->ascent;
+	layoutPtr->chunks[0].disp_x		= 0;
+	layoutPtr->chunks[0].disp_y		= fmPtr->ascent;
 	layoutPtr->chunks[0].totalWidth		= 0;
 	layoutPtr->chunks[0].displayWidth	= 0;
     } else {
@@ -2178,9 +2188,11 @@
     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
+    int lastChar,		/* The index just after the last character to
 				 * draw from the given text item. A number < 0
 				 * means to draw all characters. */
+    double angle)               /* The angle at which to draw the text */
+
 {
     TextLayout *layoutPtr;
     int i, numDisplayChars, drawX;
@@ -2215,7 +2227,7 @@
 	    lastByte = Tcl_UtfAtIndex(chunkPtr->start, numDisplayChars);
 	    Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont,
 		    firstByte, lastByte - firstByte,
-		    x + chunkPtr->x + drawX, y + chunkPtr->y);
+		    x + chunkPtr->disp_x + drawX , y + (chunkPtr->disp_y), angle);
 	}
 	firstChar -= chunkPtr->numChars;
 	lastChar -= chunkPtr->numChars;
@@ -3237,7 +3249,9 @@
     int numBytes,
     int curX,
     int newX,
-    int y)
+    int y,
+    int height,
+    double dir[2])
 {
     TextLayout *layoutPtr;
     LayoutChunk *chunkPtr;
@@ -3264,6 +3278,24 @@
     chunkPtr->y			= y;
     chunkPtr->totalWidth	= newX - curX;
     chunkPtr->displayWidth	= newX - curX;
+    if (0 == layoutPtr->numChunks) {
+      /* Initialize the display position */
+      chunkPtr->disp_x		= curX;
+      chunkPtr->disp_y		= y;
+      printf("\n");
+    } else {
+      chunkPtr->disp_x		= curX + (int) ((layoutPtr->numChunks)/2)*height*dir[0];
+      chunkPtr->disp_y		= y    - (int) height*(1+dir[1]);
+    }
+
+    char* str = malloc(sizeof(char)*(numChars+1));
+    strncpy(str,start,numChars);
+    str[numChars] = '\0';
+    printf("NewChunk %d: curX = %d, y = %d\n",(layoutPtr->numChunks)/2,curX,y);
+    printf("\t||%s|| at (%d,%d); dir = (%f,%f)\n",
+	   str,chunkPtr->disp_x,chunkPtr->disp_y,dir[0],dir[1]);
+    free(str);
+
     layoutPtr->numChunks++;
 
     return chunkPtr;
diff -Naur tk8.5a5/generic/tkFrame.c tk8.5a5_with_atext/generic/tkFrame.c
--- tk8.5a5/generic/tkFrame.c	2005-11-17 10:57:35.000000000 +0000
+++ tk8.5a5_with_atext/generic/tkFrame.c	2006-12-07 20:47:25.038813065 +0000
@@ -1095,7 +1095,7 @@
 	    labelText = Tcl_GetString(labelframePtr->textPtr);
 	    Tk_FreeTextLayout(labelframePtr->textLayout);
 	    labelframePtr->textLayout = Tk_ComputeTextLayout(labelframePtr->tkfont,
-		    labelText, -1, 0, TK_JUSTIFY_CENTER, 0,
+		    labelText, -1, 0, TK_JUSTIFY_CENTER, 0, 0.0,
 		    &labelframePtr->labelReqWidth, &labelframePtr->labelReqHeight);
 	    labelframePtr->labelReqWidth += 2 * LABELSPACING;
 	    labelframePtr->labelReqHeight += 2 * LABELSPACING;
@@ -1506,7 +1506,7 @@
 	    Tk_DrawTextLayout(framePtr->display, pixmap,
 		    labelframePtr->textGC, labelframePtr->textLayout,
 		    labelframePtr->labelTextX + LABELSPACING,
-		    labelframePtr->labelTextY + LABELSPACING, 0, -1);
+			      labelframePtr->labelTextY + LABELSPACING, 0, -1, 0.0);
 
 	    if (clipRegion != NULL) {
 		XSetClipMask(framePtr->display, labelframePtr->textGC, None);
diff -Naur tk8.5a5/generic/tkInt.h tk8.5a5_with_atext/generic/tkInt.h
--- tk8.5a5/generic/tkInt.h	2006-09-06 23:39:28.000000000 +0100
+++ tk8.5a5_with_atext/generic/tkInt.h	2006-12-07 23:14:04.769284569 +0000
@@ -1165,7 +1165,7 @@
 MODULE_SCOPE void	TkpDrawCharsInContext(Display * display,
 			    Drawable drawable, GC gc, Tk_Font tkfont,
 			    const char * source, int numBytes, int rangeStart,
-			    int rangeLength, int x, int y);
+					      int rangeLength, int x, int y, double angle);
 MODULE_SCOPE int	TkpMeasureCharsInContext(Tk_Font tkfont,
 			    const char * source, int numBytes, int rangeStart,
 			    int rangeLength, int maxLength, int flags,
diff -Naur tk8.5a5/generic/tkListbox.c tk8.5a5_with_atext/generic/tkListbox.c
--- tk8.5a5/generic/tkListbox.c	2006-05-29 22:53:16.000000000 +0100
+++ tk8.5a5_with_atext/generic/tkListbox.c	2006-12-07 20:04:19.872232023 +0000
@@ -2044,7 +2044,7 @@
 	Tcl_ListObjIndex(listPtr->interp, listPtr->listObj, i, &curElement);
 	stringRep = Tcl_GetStringFromObj(curElement, &stringLen);
 	Tk_DrawChars(listPtr->display, pixmap, gc, listPtr->tkfont,
-		stringRep, stringLen, x, y);
+		stringRep, stringLen, x, y, 0.0);
 
 	/*
 	 * If this is the active element, apply the activestyle to it.
diff -Naur tk8.5a5/generic/tkMessage.c tk8.5a5_with_atext/generic/tkMessage.c
--- tk8.5a5/generic/tkMessage.c	2006-05-26 00:50:00.000000000 +0100
+++ tk8.5a5_with_atext/generic/tkMessage.c	2006-12-07 20:04:19.883228908 +0000
@@ -625,7 +625,7 @@
     for ( ; ; inc /= 2) {
 	msgPtr->textLayout = Tk_ComputeTextLayout(msgPtr->tkfont,
 		msgPtr->string, msgPtr->numChars, width, msgPtr->justify,
-		0, &thisWidth, &thisHeight);
+		0, 0.0, &thisWidth, &thisHeight);
 	maxWidth = thisWidth + 2 * (inset + msgPtr->padX);
 	height = thisHeight + 2 * (inset + msgPtr->padY);
 
@@ -698,7 +698,7 @@
     TkComputeAnchor(msgPtr->anchor, tkwin, msgPtr->padX, msgPtr->padY,
 	    msgPtr->msgWidth, msgPtr->msgHeight, &x, &y);
     Tk_DrawTextLayout(Tk_Display(tkwin), Tk_WindowId(tkwin), msgPtr->textGC,
-	    msgPtr->textLayout, x, y, 0, -1);
+	    msgPtr->textLayout, x, y, 0, -1, 0.0);
 
     if (borderWidth > msgPtr->highlightWidth) {
 	Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), msgPtr->border,
diff -Naur tk8.5a5/generic/tkTextDisp.c tk8.5a5_with_atext/generic/tkTextDisp.c
--- tk8.5a5/generic/tkTextDisp.c	2006-10-18 00:44:45.000000000 +0100
+++ tk8.5a5_with_atext/generic/tkTextDisp.c	2006-12-07 23:12:28.049593054 +0000
@@ -7431,7 +7431,7 @@
 	TkpDrawCharsInContext(display, dst, stylePtr->fgGC, sValuePtr->tkfont,
 		string, numBytes, start, len,
 		ciPtr->baseChunkPtr->x + xDisplacement,
-		y + baseline - sValuePtr->offset);
+			      y + baseline - sValuePtr->offset, 0.0);
 
 	if (sValuePtr->underline) {
 	    TkUnderlineCharsInContext(display, dst, stylePtr->fgGC,
@@ -7459,7 +7459,7 @@
 	    numBytes--;
 	}
 	Tk_DrawChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont, string,
-		numBytes, offsetX, y + baseline - sValuePtr->offset);
+		numBytes, offsetX, y + baseline - sValuePtr->offset, 0.0);
 	if (sValuePtr->underline) {
 	    Tk_UnderlineChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont,
 		    string, offsetX,
diff -Naur tk8.5a5/unix/Makefile.in tk8.5a5_with_atext/unix/Makefile.in
--- tk8.5a5/unix/Makefile.in	2006-10-23 14:45:33.000000000 +0100
+++ tk8.5a5_with_atext/unix/Makefile.in	2006-12-07 20:04:19.941212484 +0000
@@ -341,7 +341,7 @@
 
 CANV_OBJS = tkCanvas.o tkCanvArc.o tkCanvBmap.o tkCanvImg.o \
 	tkCanvLine.o tkCanvPoly.o tkCanvPs.o tkCanvText.o \
-	tkCanvUtil.o tkCanvWind.o tkRectOval.o tkTrig.o
+	tkCanvUtil.o tkCanvWind.o tkRectOval.o tkTrig.o rotated.o
 
 IMAGE_OBJS = tkImage.o tkImgBmap.o tkImgGIF.o tkImgPPM.o tkImgPhoto.o
 
@@ -445,7 +445,8 @@
 	$(UNIX_DIR)/tkUnixScale.c $(UNIX_DIR)/tkUnixScrlbr.c \
 	$(UNIX_DIR)/tkUnixSelect.c \
 	$(UNIX_DIR)/tkUnixSend.c $(UNIX_DIR)/tkUnixWm.c \
-	$(UNIX_DIR)/tkUnixXId.c
+	$(UNIX_DIR)/tkUnixXId.c \
+	$(UNIX_DIR)/rotated.c
 
 AQUA_SRCS = \
 	$(MAC_OSX_DIR)/tkMacOSXBitmap.c $(MAC_OSX_DIR)/tkMacOSXButton.c \
diff -Naur tk8.5a5/unix/rotated.c tk8.5a5_with_atext/unix/rotated.c
--- tk8.5a5/unix/rotated.c	1970-01-01 01:00:00.000000000 +0100
+++ tk8.5a5_with_atext/unix/rotated.c	2006-12-07 20:04:19.949210219 +0000
@@ -0,0 +1,1579 @@
+/* ********************************************************************** */
+
+/* xvertext 5.0, Copyright (c) 1993 Alan Richardson ([email protected])
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both the
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  All work developed as a consequence of the use of
+ * this program should duly acknowledge such use. No representations are
+ * made about the suitability of this software for any purpose.  It is
+ * provided "as is" without express or implied warranty.
+ */
+
+/* ********************************************************************** */
+
+
+/* BETTER: xvertext now does rotation at any angle!!
+ *
+ * BEWARE: function arguments have CHANGED since version 2.0!!
+ */
+
+/* ********************************************************************** */
+
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "rotated.h"
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* Make sure cache size is set */
+
+#ifndef CACHE_SIZE_LIMIT
+#define CACHE_SIZE_LIMIT 0
+#endif /*CACHE_SIZE_LIMIT */
+    
+/* Make sure a cache method is specified */
+
+#ifndef CACHE_XIMAGES
+#ifndef CACHE_BITMAPS
+#define CACHE_BITMAPS
+#endif /*CACHE_BITMAPS*/
+#endif /*CACHE_XIMAGES*/
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* Debugging macros */
+
+#ifdef DEBUG
+static int debug=1;
+#else
+static int debug=0;
+#endif /*DEBUG*/
+
+#define DEBUG_PRINT1(a) if (debug) printf (a)
+#define DEBUG_PRINT2(a, b) if (debug) printf (a, b)
+#define DEBUG_PRINT3(a, b, c) if (debug) printf (a, b, c)
+#define DEBUG_PRINT4(a, b, c, d) if (debug) printf (a, b, c, d)
+#define DEBUG_PRINT5(a, b, c, d, e) if (debug) printf (a, b, c, d, e)
+
+
+/* ---------------------------------------------------------------------- */
+
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* A structure holding everything needed for a rotated string */
+
+typedef struct rotated_text_item_template {
+    Pixmap bitmap;
+    XImage *ximage;
+    
+    char *text;
+    char *font_name;
+    Font fid;
+    float angle;
+    int align;
+    float magnify;
+    
+    unsigned int cols_in;
+    unsigned int rows_in;
+    unsigned int cols_out;
+    unsigned int rows_out;
+    
+    int nl;
+    int max_width;
+    float *corners_x;
+    float *corners_y;
+    
+    long int size;
+    int cached;
+
+    struct rotated_text_item_template *next;
+} RotatedTextItem;
+
+RotatedTextItem *first_text_item=NULL;
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* A structure holding current magnification and bounding box padding */
+
+static struct style_template {
+    float magnify;
+    int bbx_pad;
+} style={
+    1.,
+    0
+    };
+
+
+/* ---------------------------------------------------------------------- */
+
+
+static char            *my_strdup();
+static char            *my_strtok();
+
+float                   XRotVersion();
+void                    XRotSetMagnification();
+void                    XRotSetBoundingBoxPad();
+int                     XRotDrawString();
+int                     XRotDrawImageString();
+int                     XRotDrawAlignedString();
+int                     XRotDrawAlignedImageString();
+XPoint                 *XRotTextExtents();
+
+static XImage          *MakeXImage();
+static int              XRotPaintAlignedString();
+static int              XRotDrawHorizontalString();
+static RotatedTextItem *XRotRetrieveFromCache();
+static RotatedTextItem *XRotCreateTextItem();
+static void             XRotAddToLinkedList();
+static void             XRotFreeTextItem();
+static XImage          *XRotMagnifyImage();
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Routine to mimic `strdup()' (some machines don't have it)              */
+/**************************************************************************/
+
+static char *my_strdup(str)
+    char *str;
+{
+    char *s;
+    
+    if(str==NULL)
+	return NULL;
+    
+    s=(char *)malloc((unsigned)(strlen(str)+1));
+    if(s!=NULL) 
+	strcpy(s, str);
+    
+    return s;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Routine to replace `strtok' : this one returns a zero length string if */
+/* it encounters two consecutive delimiters                               */
+/**************************************************************************/
+
+static char *my_strtok(str1, str2)
+    char *str1, *str2;
+{
+    char *ret;
+    int i, j, stop;
+    static int start, len;
+    static char *stext;
+    
+    if(str2==NULL)
+	return NULL;
+    
+    /* initialise if str1 not NULL */
+    if(str1!=NULL) {
+	start=0;
+	stext=str1;
+	len=strlen(str1);
+    }
+    
+    /* run out of tokens ? */
+    if(start>=len)
+	return NULL;
+    
+    /* loop through characters */
+    for(i=start; i<len; i++) {
+	/* loop through delimiters */
+	stop=0;
+	for(j=0; j<strlen(str2); j++)
+	    if(stext[i]==str2[j])
+		stop=1;
+	
+	if(stop)
+	    break;
+    }
+    
+    stext[i]='\0';
+    
+    ret=stext+start;
+    
+    start=i+1;
+    
+    return ret;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Return version/copyright information                                   */
+/**************************************************************************/
+
+float XRotVersion(str, n)
+    char *str;
+    unsigned int n;
+{
+    if(str!=NULL)
+	strncpy(str, XV_COPYRIGHT, n);
+    return XV_VERSION;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Set the font magnification factor for all subsequent operations        */
+/**************************************************************************/
+
+void XRotSetMagnification(m)
+    float m;
+{
+    if(m>0.)
+	style.magnify=m;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Set the padding used when calculating bounding boxes                   */
+/**************************************************************************/
+
+void XRotSetBoundingBoxPad(p)
+    int p;
+{
+    if(p>=0)
+	style.bbx_pad=p;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  Create an XImage structure and allocate memory for it                 */
+/**************************************************************************/
+
+static XImage *MakeXImage(dpy, w, h)
+    Display *dpy;
+    unsigned int w, h;
+{
+    XImage *I;
+    char *data;
+    
+    /* reserve memory for image */
+    data=(char *)calloc((unsigned)(((w-1)/8+1)*h), 1);
+    if(data==NULL)
+	return NULL;
+    
+    /* create the XImage */
+    I=XCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 1, XYBitmap,
+                   0, data, w, h, 8, 0);
+    if(I==NULL)
+	return NULL;
+    
+    I->byte_order=I->bitmap_bit_order=MSBFirst;
+    return I;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  A front end to XRotPaintAlignedString:                                */
+/*      -no alignment, no background                                      */
+/**************************************************************************/
+
+int XRotDrawString(dpy, font, angle, drawable, gc, x, y, str)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    char *str;
+{
+    return (XRotPaintAlignedString(dpy, font, angle, drawable, gc,
+				   x, y, str, NONE, 0));
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  A front end to XRotPaintAlignedString:                                */
+/*      -no alignment, paints background                                  */
+/**************************************************************************/
+
+int XRotDrawImageString(dpy, font, angle, drawable, gc, x, y, str)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    char *str;
+{
+    return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
+				  x, y, str, NONE, 1));
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  A front end to XRotPaintAlignedString:                                */
+/*      -does alignment, no background                                    */
+/**************************************************************************/
+
+int XRotDrawAlignedString(dpy, font, angle, drawable, gc, x, y, text, align)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    char *text;
+    int align;
+{
+    return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
+				  x, y, text, align, 0));
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  A front end to XRotPaintAlignedString:                                */
+/*      -does alignment, paints background                                */
+/**************************************************************************/
+
+int XRotDrawAlignedImageString(dpy, font, angle, drawable, gc, x, y, text,
+			       align)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    char *text;
+    int align;
+{
+    return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
+				  x, y, text, align, 1));
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  Aligns and paints a rotated string                                    */
+/**************************************************************************/
+
+static int XRotPaintAlignedString(dpy, font, angle, drawable, gc, x, y, text,
+				  align, bg)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    char *text;
+    int align;
+    int bg;
+{
+    int i;
+    GC my_gc;
+    int xp, yp;
+    float hot_x, hot_y;
+    float hot_xp, hot_yp;
+    float sin_angle, cos_angle;
+    RotatedTextItem *item;
+    Pixmap bitmap_to_paint;
+    
+    /* return early for NULL/empty strings */
+    if(text==NULL)
+        return 0;
+    
+    if(strlen(text)==0)
+	return 0;
+
+    /* manipulate angle to 0<=angle<360 degrees */
+    while(angle<0)
+        angle+=360;
+    
+    while(angle>=360)
+        angle-=360;
+    
+    angle*=M_PI/180;
+    
+    /* horizontal text made easy */
+    if(angle==0. && style.magnify==1.) 
+	return(XRotDrawHorizontalString(dpy, font, drawable, gc, x, y,
+					text, align, bg));
+    
+    /* get a rotated bitmap */
+    item=XRotRetrieveFromCache(dpy, font, angle, text, align);
+    if(item==NULL)
+	return 0;
+    
+    /* this gc has similar properties to the user's gc */
+    my_gc=XCreateGC(dpy, drawable, 0, 0);
+    XCopyGC(dpy, gc, GCForeground|GCBackground|GCFunction|GCPlaneMask,
+	    my_gc);
+
+    /* alignment : which point (hot_x, hot_y) relative to bitmap centre
+       coincides with user's specified point? */
+    
+    /* y position */
+    if(align==TLEFT || align==TCENTRE || align==TRIGHT)
+        hot_y=(float)item->rows_in/2*style.magnify;
+    else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
+	hot_y=0;
+    else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
+	hot_y=-(float)item->rows_in/2*style.magnify;
+    else
+	hot_y=-((float)item->rows_in/2-(float)font->descent)*style.magnify;
+    
+    /* x position */
+    if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
+	hot_x=-(float)item->max_width/2*style.magnify;
+    else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
+	hot_x=0;
+    else
+        hot_x=(float)item->max_width/2*style.magnify;
+    
+    /* pre-calculate sin and cos */
+    sin_angle=sin(angle);
+    cos_angle=cos(angle);
+    
+    /* rotate hot_x and hot_y around bitmap centre */
+    hot_xp= hot_x*cos_angle - hot_y*sin_angle;
+    hot_yp= hot_x*sin_angle + hot_y*cos_angle;
+    
+    /* text background will be drawn using XFillPolygon */
+    if(bg) {
+	GC depth_one_gc;
+	XPoint *xpoints;
+	Pixmap empty_stipple;
+	
+	/* reserve space for XPoints */
+	xpoints=(XPoint *)malloc((unsigned)(4*item->nl*sizeof(XPoint)));
+	if(!xpoints)
+	    return 1;
+	
+	/* rotate corner positions */
+	for(i=0; i<4*item->nl; i++) {
+	    xpoints[i].x=(float)x + ( (item->corners_x[i]-hot_x)*cos_angle + 
+				      (item->corners_y[i]+hot_y)*sin_angle);
+	    xpoints[i].y=(float)y + (-(item->corners_x[i]-hot_x)*sin_angle + 
+				      (item->corners_y[i]+hot_y)*cos_angle);
+	}
+	
+	/* we want to swap foreground and background colors here;
+	   XGetGCValues() is only available in R4+ */
+	
+	empty_stipple=XCreatePixmap(dpy, drawable, 1, 1, 1);
+	
+	depth_one_gc=XCreateGC(dpy, empty_stipple, 0, 0);
+	XSetForeground(dpy, depth_one_gc, 0);
+	XFillRectangle(dpy, empty_stipple, depth_one_gc, 0, 0, 2, 2);
+
+	XSetStipple(dpy, my_gc, empty_stipple);
+	XSetFillStyle(dpy, my_gc, FillOpaqueStippled);
+	
+	XFillPolygon(dpy, drawable, my_gc, xpoints, 4*item->nl, Nonconvex,
+		     CoordModeOrigin);
+	
+	/* free our resources */
+	free((char *)xpoints);
+	XFreeGC(dpy, depth_one_gc);
+	XFreePixmap(dpy, empty_stipple);
+    }
+    
+    /* where should top left corner of bitmap go ? */
+    xp=(float)x-((float)item->cols_out/2 +hot_xp);
+    yp=(float)y-((float)item->rows_out/2 -hot_yp);
+    
+    /* by default we draw the rotated bitmap, solid */
+    bitmap_to_paint=item->bitmap;
+
+    /* handle user stippling */
+#ifndef X11R3
+    {
+	GC depth_one_gc;
+	XGCValues values;
+	Pixmap new_bitmap, inverse;
+	
+	/* try and get some GC properties */
+	if(XGetGCValues(dpy, gc, 
+			GCStipple|GCFillStyle|GCForeground|GCBackground|
+			GCTileStipXOrigin|GCTileStipYOrigin,
+			&values)) {
+
+	    /* only do this if stippling requested */
+	    if((values.fill_style==FillStippled ||
+		values.fill_style==FillOpaqueStippled) && !bg) {
+
+		/* opaque stipple: draw rotated text in background colour */
+		if(values.fill_style==FillOpaqueStippled) {
+		    XSetForeground(dpy, my_gc, values.background);
+		    XSetFillStyle(dpy, my_gc, FillStippled);
+		    XSetStipple(dpy, my_gc, item->bitmap);
+		    XSetTSOrigin(dpy, my_gc, xp, yp);
+		    XFillRectangle(dpy, drawable, my_gc, xp, yp,
+				   item->cols_out, item->rows_out);
+		    XSetForeground(dpy, my_gc, values.foreground);
+		}
+
+		/* this will merge the rotated text and the user's stipple */
+		new_bitmap=XCreatePixmap(dpy, drawable,
+					 item->cols_out, item->rows_out, 1);
+
+		/* create a GC */
+		depth_one_gc=XCreateGC(dpy, new_bitmap, 0, 0);
+		XSetForeground(dpy, depth_one_gc, 1);
+		XSetBackground(dpy, depth_one_gc, 0);
+
+		/* set the relative stipple origin */
+		XSetTSOrigin(dpy, depth_one_gc, 
+			     values.ts_x_origin-xp, values.ts_y_origin-yp);
+
+		/* fill the whole bitmap with the user's stipple */
+		XSetStipple(dpy, depth_one_gc, values.stipple);
+		XSetFillStyle(dpy, depth_one_gc, FillOpaqueStippled);
+		XFillRectangle(dpy, new_bitmap, depth_one_gc,
+			       0, 0, item->cols_out, item->rows_out);
+
+		/* set stipple origin back to normal */
+		XSetTSOrigin(dpy, depth_one_gc, 0, 0);
+
+		/* this will contain an inverse copy of the rotated text */
+		inverse=XCreatePixmap(dpy, drawable,
+				      item->cols_out, item->rows_out, 1);
+
+		/* invert text */
+		XSetFillStyle(dpy, depth_one_gc, FillSolid);
+		XSetFunction(dpy, depth_one_gc, GXcopyInverted);
+		XCopyArea(dpy, item->bitmap, inverse, depth_one_gc,
+			  0, 0, item->cols_out, item->rows_out, 0, 0);
+
+		/* now delete user's stipple everywhere EXCEPT on text */
+                XSetForeground(dpy, depth_one_gc, 0);
+                XSetBackground(dpy, depth_one_gc, 1);
+		XSetStipple(dpy, depth_one_gc, inverse);
+		XSetFillStyle(dpy, depth_one_gc, FillStippled);
+		XSetFunction(dpy, depth_one_gc, GXcopy);
+		XFillRectangle(dpy, new_bitmap, depth_one_gc,
+                               0, 0, item->cols_out, item->rows_out);
+
+		/* free resources */
+		XFreePixmap(dpy, inverse);
+		XFreeGC(dpy, depth_one_gc);
+
+		/* this is the new bitmap */
+		bitmap_to_paint=new_bitmap;
+	    }
+	}
+    }
+#endif /*X11R3*/
+
+    /* paint text using stipple technique */
+    XSetFillStyle(dpy, my_gc, FillStippled);
+    XSetStipple(dpy, my_gc, bitmap_to_paint);
+    XSetTSOrigin(dpy, my_gc, xp, yp);
+    XFillRectangle(dpy, drawable, my_gc, xp, yp, 
+		   item->cols_out, item->rows_out);
+    
+    /* free our resources */
+    XFreeGC(dpy, my_gc);
+
+    /* stippled bitmap no longer needed */
+    if(bitmap_to_paint!=item->bitmap)
+	XFreePixmap(dpy, bitmap_to_paint);
+
+#ifdef CACHE_XIMAGES
+    XFreePixmap(dpy, item->bitmap);
+#endif /*CACHE_XIMAGES*/
+
+    /* if item isn't cached, destroy it completely */
+    if(!item->cached) 
+	XRotFreeTextItem(dpy,item);
+
+    /* we got to the end OK! */
+    return 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  Draw a horizontal string in a quick fashion                           */
+/**************************************************************************/
+
+static int XRotDrawHorizontalString(dpy, font, drawable, gc, x, y, text, 
+			     align, bg)
+    Display *dpy;
+    XFontStruct *font;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    char *text;
+    int align;
+    int bg;
+{
+    GC my_gc;
+    int nl=1, i;
+    int height;
+    int xp, yp;
+    char *str1, *str2, *str3;
+    char *str2_a="\0", *str2_b="\n\0";
+    int dir, asc, desc;
+    XCharStruct overall;
+
+    DEBUG_PRINT1("**\nHorizontal text.\n");
+
+    /* this gc has similar properties to the user's gc (including stipple) */
+    my_gc=XCreateGC(dpy, drawable, 0, 0);
+    XCopyGC(dpy, gc,
+	    GCForeground|GCBackground|GCFunction|GCStipple|GCFillStyle|
+	    GCTileStipXOrigin|GCTileStipYOrigin|GCPlaneMask, my_gc);
+    XSetFont(dpy, my_gc, font->fid);
+	
+    /* count number of sections in string */
+    if(align!=NONE)
+	for(i=0; i<strlen(text)-1; i++)
+	    if(text[i]=='\n')
+		nl++;
+    
+    /* ignore newline characters if not doing alignment */
+    if(align==NONE)
+	str2=str2_a;
+    else
+	str2=str2_b;
+    
+    /* overall font height */
+    height=font->ascent+font->descent;
+    
+    /* y position */
+    if(align==TLEFT || align==TCENTRE || align==TRIGHT)
+	yp=y+font->ascent;
+    else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
+	yp=y-nl*height/2+font->ascent;
+    else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
+	yp=y-nl*height+font->ascent;
+    else
+	yp=y;
+    
+    str1=my_strdup(text);
+    if(str1==NULL)
+	return 1;
+    
+    str3=my_strtok(str1, str2);
+    
+    /* loop through each section in the string */
+    do {
+        XTextExtents(font, str3, (int) strlen(str3), &dir, &asc, &desc,
+                     &overall);
+
+	/* where to draw section in x ? */
+	if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
+	    xp=x;
+	else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
+	    xp=x-overall.rbearing/2;
+	else
+	    xp=x-overall.rbearing;
+	
+	/* draw string onto bitmap */
+	if(!bg)
+	    XDrawString(dpy, drawable, my_gc, xp, yp, str3, (int) strlen(str3));
+	else
+	    XDrawImageString(dpy, drawable, my_gc, xp, yp, str3, (int) strlen(str3));
+	
+	/* move to next line */
+	yp+=height;
+	
+	str3=my_strtok((char *)NULL, str2);
+    }
+    while(str3!=NULL);
+    
+    free(str1);
+    XFreeGC(dpy, my_gc);
+
+    return 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*   Query cache for a match with this font/text/angle/alignment          */
+/*       request, otherwise arrange for its creation                      */
+/**************************************************************************/
+
+static RotatedTextItem *XRotRetrieveFromCache(dpy, font, angle, text, align)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    char *text;
+    int align;
+{
+    Font fid;
+    char *font_name=NULL;
+    unsigned long name_value;
+    RotatedTextItem *item=NULL;
+    RotatedTextItem *i1=first_text_item;
+    
+    /* get font name, if it exists */
+    if(XGetFontProperty(font, XA_FONT, &name_value)) {
+	DEBUG_PRINT1("got font name OK\n");
+	font_name=XGetAtomName(dpy, name_value);
+	fid=0;
+    }
+#ifdef CACHE_FID
+    /* otherwise rely (unreliably?) on font ID */
+    else {
+	DEBUG_PRINT1("can't get fontname, caching FID\n");
+	font_name=NULL;
+	fid=font->fid;
+    }
+#else
+    /* not allowed to cache font ID's */
+    else {
+	DEBUG_PRINT1("can't get fontname, can't cache\n");
+	font_name=NULL;
+	fid=0;
+    }
+#endif /*CACHE_FID*/
+    
+    /* look for a match in cache */
+
+    /* matching formula:
+       identical text;
+       identical fontname (if defined, font ID's if not);
+       angles close enough (<0.00001 here, could be smaller);
+       HORIZONTAL alignment matches, OR it's a one line string;
+       magnifications the same */
+
+    while(i1 && !item) {
+	/* match everything EXCEPT fontname/ID */
+	if(strcmp(text, i1->text)==0 &&
+	   fabs(angle-i1->angle)<0.00001 &&
+	   style.magnify==i1->magnify &&
+	   (i1->nl==1 ||
+	    ((align==0)?9:(align-1))%3==
+	      ((i1->align==0)?9:(i1->align-1))%3)) {
+
+	    /* now match fontname/ID */
+	    if(font_name!=NULL && i1->font_name!=NULL) {
+		if(strcmp(font_name, i1->font_name)==0) {
+		    item=i1;
+		    DEBUG_PRINT1("Matched against font names\n");
+		}
+		else
+		    i1=i1->next;
+	    }
+#ifdef CACHE_FID
+	    else if(font_name==NULL && i1->font_name==NULL) {
+		if(fid==i1->fid) {
+		    item=i1;
+		    DEBUG_PRINT1("Matched against FID's\n");
+                }
+		else
+                    i1=i1->next;
+	    }
+#endif /*CACHE_FID*/
+	    else
+		i1=i1->next;
+	}
+	else
+	    i1=i1->next;
+    }
+    
+    if(item)
+	DEBUG_PRINT1("**\nFound target in cache.\n");
+    if(!item)
+	DEBUG_PRINT1("**\nNo match in cache.\n");
+
+    /* no match */
+    if(!item) {
+	/* create new item */
+	item=XRotCreateTextItem(dpy, font, angle, text, align);
+	if(!item)
+	    return NULL;
+
+	/* record what it shows */
+	item->text=my_strdup(text);
+
+	/* fontname or ID */
+	if(font_name!=NULL) {
+	    item->font_name=my_strdup(font_name);
+	    item->fid=0;
+	}
+	else {
+	    item->font_name=NULL;
+	    item->fid=fid;
+	}
+
+	item->angle=angle;
+	item->align=align;
+	item->magnify=style.magnify;
+
+	/* cache it */
+	XRotAddToLinkedList(dpy, item);
+    }
+
+    if(font_name)
+	XFree(font_name);
+
+    /* if XImage is cached, need to recreate the bitmap */
+
+#ifdef CACHE_XIMAGES
+    {
+	GC depth_one_gc;
+
+	/* create bitmap to hold rotated text */
+	item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
+				   item->cols_out, item->rows_out, 1);
+	
+	/* depth one gc */
+	depth_one_gc=XCreateGC(dpy, item->bitmap, NULL, 0);
+	XSetBackground(dpy, depth_one_gc, 0);
+	XSetForeground(dpy, depth_one_gc, 1);
+
+	/* make the text bitmap from XImage */
+	XPutImage(dpy, item->bitmap, depth_one_gc, item->ximage, 0, 0, 0, 0,
+		  item->cols_out, item->rows_out);
+
+	XFreeGC(dpy, depth_one_gc);
+    }
+#endif /*CACHE_XIMAGES*/
+    
+    return item;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  Create a rotated text item                                            */
+/**************************************************************************/
+
+static RotatedTextItem *XRotCreateTextItem(dpy, font, angle, text, align)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    char *text;
+    int align;
+{
+    RotatedTextItem *item=NULL;
+    Pixmap canvas;
+    GC font_gc;
+    XImage *I_in;
+    register int i, j;
+    char *str1, *str2, *str3;
+    char *str2_a="\0", *str2_b="\n\0";
+    int height;
+    int byte_w_in, byte_w_out;
+    int xp, yp;
+    float sin_angle, cos_angle;
+    int it, jt;
+    float di, dj;
+    int ic=0;
+    float xl, xr, xinc;
+    int byte_out;
+    int dir, asc, desc;
+    XCharStruct overall;
+    int old_cols_in=0, old_rows_in=0;
+    
+    /* allocate memory */
+    item=(RotatedTextItem *)malloc((unsigned)sizeof(RotatedTextItem));
+    if(!item)
+	return NULL;
+	
+    /* count number of sections in string */
+    item->nl=1;
+    if(align!=NONE)
+	for(i=0; i<strlen(text)-1; i++)
+	    if(text[i]=='\n')
+		item->nl++;
+    
+    /* ignore newline characters if not doing alignment */
+    if(align==NONE)
+	str2=str2_a;
+    else
+	str2=str2_b;
+    
+    /* find width of longest section */
+    str1=my_strdup(text);
+    if(str1==NULL)
+	return NULL;
+    
+    str3=my_strtok(str1, str2);
+
+    XTextExtents(font, str3, (int) strlen(str3), &dir, &asc, &desc,
+		 &overall);
+    
+    item->max_width=overall.rbearing;
+    
+    /* loop through each section */
+    do {
+	str3=my_strtok((char *)NULL, str2);
+
+	if(str3!=NULL) {
+	    XTextExtents(font, str3, (int) strlen(str3), &dir, &asc, &desc,
+			 &overall);
+
+	    if(overall.rbearing>item->max_width)
+		item->max_width=overall.rbearing;
+	}
+    }
+    while(str3!=NULL);
+    
+    free(str1);
+    
+    /* overall font height */
+    height=font->ascent+font->descent;
+    
+    /* dimensions horizontal text will have */
+    item->cols_in=item->max_width;
+    item->rows_in=item->nl*height;
+    
+    /* bitmap for drawing on */
+    canvas=XCreatePixmap(dpy, DefaultRootWindow(dpy),
+			 item->cols_in, item->rows_in, 1);
+    
+    /* create a GC for the bitmap */
+    font_gc=XCreateGC(dpy, canvas, 0, 0);
+    XSetBackground(dpy, font_gc, 0);
+    XSetFont(dpy, font_gc, font->fid);
+    
+    /* make sure the bitmap is blank */
+    XSetForeground(dpy, font_gc, 0);
+    XFillRectangle(dpy, canvas, font_gc, 0, 0, 
+		   item->cols_in+1, item->rows_in+1);
+    XSetForeground(dpy, font_gc, 1);
+    
+    /* pre-calculate sin and cos */
+    sin_angle=sin(angle);
+    cos_angle=cos(angle);
+    
+    /* text background will be drawn using XFillPolygon */
+    item->corners_x=
+	(float *)malloc((unsigned)(4*item->nl*sizeof(float)));
+    if(!item->corners_x)
+	return NULL;
+    
+    item->corners_y=
+	(float *)malloc((unsigned)(4*item->nl*sizeof(float)));
+    if(!item->corners_y)
+	return NULL;
+    
+    /* draw text horizontally */
+    
+    /* start at top of bitmap */
+    yp=font->ascent;
+    
+    str1=my_strdup(text);
+    if(str1==NULL)
+	return NULL;
+    
+    str3=my_strtok(str1, str2);
+    
+    /* loop through each section in the string */
+    do {
+	XTextExtents(font, str3, (int) strlen(str3), &dir, &asc, &desc,
+		&overall);
+
+	/* where to draw section in x ? */
+	if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
+	    xp=0;
+	else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
+	    xp=(item->max_width-overall.rbearing)/2;
+	else
+            xp=item->max_width-overall.rbearing;
+
+	/* draw string onto bitmap */
+	XDrawString(dpy, canvas, font_gc, xp, yp, str3, (int) strlen(str3));
+	
+	/* keep a note of corner positions of this string */
+	item->corners_x[ic]=((float)xp-(float)item->cols_in/2)*style.magnify;
+	item->corners_y[ic]=((float)(yp-font->ascent)-(float)item->rows_in/2)
+	    *style.magnify;
+	item->corners_x[ic+1]=item->corners_x[ic];
+	item->corners_y[ic+1]=item->corners_y[ic]+(float)height*style.magnify;
+	item->corners_x[item->nl*4-1-ic]=item->corners_x[ic]+
+	    (float)overall.rbearing*style.magnify;
+	item->corners_y[item->nl*4-1-ic]=item->corners_y[ic];
+	item->corners_x[item->nl*4-2-ic]=
+	    item->corners_x[item->nl*4-1-ic];
+	item->corners_y[item->nl*4-2-ic]=item->corners_y[ic+1];
+	
+	ic+=2;
+	
+	/* move to next line */
+	yp+=height;
+	
+	str3=my_strtok((char *)NULL, str2);
+    }
+    while(str3!=NULL);
+    
+    free(str1);
+    
+    /* create image to hold horizontal text */
+    I_in=MakeXImage(dpy, item->cols_in, item->rows_in);
+    if(I_in==NULL)
+	return NULL;
+    
+    /* extract horizontal text */
+    XGetSubImage(dpy, canvas, 0, 0, item->cols_in, item->rows_in,
+		 1, XYPixmap, I_in, 0, 0);
+    I_in->format=XYBitmap;
+    
+    /* magnify horizontal text */
+    if(style.magnify!=1.) {
+	I_in=XRotMagnifyImage(dpy, I_in);
+
+	old_cols_in=item->cols_in;
+	old_rows_in=item->rows_in;
+	item->cols_in=(float)item->cols_in*style.magnify;
+	item->rows_in=(float)item->rows_in*style.magnify;
+    }
+
+    /* how big will rotated text be ? */
+    item->cols_out=fabs((float)item->rows_in*sin_angle) +
+	fabs((float)item->cols_in*cos_angle) +0.99999 +2;
+
+    item->rows_out=fabs((float)item->rows_in*cos_angle) +
+	fabs((float)item->cols_in*sin_angle) +0.99999 +2;
+
+    if(item->cols_out%2==0)
+	item->cols_out++;
+    
+    if(item->rows_out%2==0)
+	item->rows_out++;
+    
+    /* create image to hold rotated text */
+    item->ximage=MakeXImage(dpy, item->cols_out, item->rows_out);
+    if(item->ximage==NULL)
+	return NULL;
+    
+    byte_w_in=(item->cols_in-1)/8+1;
+    byte_w_out=(item->cols_out-1)/8+1;
+    
+    /* we try to make this bit as fast as possible - which is why it looks
+       a bit over-the-top */
+    
+    /* vertical distance from centre */
+    dj=0.5-(float)item->rows_out/2;
+
+    /* where abouts does text actually lie in rotated image? */
+    if(angle==0 || angle==M_PI/2 || 
+       angle==M_PI || angle==3*M_PI/2) {
+	xl=0;
+	xr=(float)item->cols_out;
+	xinc=0;
+    }
+    else if(angle<M_PI) {
+	xl=(float)item->cols_out/2+
+	    (dj-(float)item->rows_in/(2*cos_angle))/
+		tan(angle)-2;
+	xr=(float)item->cols_out/2+
+	    (dj+(float)item->rows_in/(2*cos_angle))/
+		tan(angle)+2;
+	xinc=1./tan(angle);
+    }
+    else {
+	xl=(float)item->cols_out/2+
+	    (dj+(float)item->rows_in/(2*cos_angle))/
+		tan(angle)-2;
+	xr=(float)item->cols_out/2+
+	    (dj-(float)item->rows_in/(2*cos_angle))/
+		tan(angle)+2;
+	
+	xinc=1./tan(angle);
+    }
+
+    /* loop through all relevent bits in rotated image */
+    for(j=0; j<item->rows_out; j++) {
+	
+	/* no point re-calculating these every pass */
+	di=(float)((xl<0)?0:(int)xl)+0.5-(float)item->cols_out/2;
+	byte_out=(item->rows_out-j-1)*byte_w_out;
+	
+	/* loop through meaningful columns */
+	for(i=((xl<0)?0:(int)xl); 
+	    i<((xr>=item->cols_out)?item->cols_out:(int)xr); i++) {
+	    
+	    /* rotate coordinates */
+	    it=(float)item->cols_in/2 + ( di*cos_angle + dj*sin_angle);
+	    jt=(float)item->rows_in/2 - (-di*sin_angle + dj*cos_angle);
+	    
+            /* set pixel if required */
+            if(it>=0 && it<item->cols_in && jt>=0 && jt<item->rows_in)
+                if((I_in->data[jt*byte_w_in+it/8] & 128>>(it%8))>0)
+                    item->ximage->data[byte_out+i/8]|=128>>i%8;
+	    
+	    di+=1;
+	}
+	dj+=1;
+	xl+=xinc;
+	xr+=xinc;
+    }
+    XDestroyImage(I_in);
+    
+    if(style.magnify!=1.) {
+	item->cols_in=old_cols_in;
+	item->rows_in=old_rows_in;
+    }
+
+
+#ifdef CACHE_BITMAPS
+
+    /* create a bitmap to hold rotated text */
+    item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
+			       item->cols_out, item->rows_out, 1);
+    
+    /* make the text bitmap from XImage */
+    XPutImage(dpy, item->bitmap, font_gc, item->ximage, 0, 0, 0, 0,
+	      item->cols_out, item->rows_out);
+
+    XDestroyImage(item->ximage);
+
+#endif /*CACHE_BITMAPS*/
+
+    XFreeGC(dpy, font_gc);
+    XFreePixmap(dpy, canvas);
+
+    return item;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  Adds a text item to the end of the cache, removing as many items      */
+/*      from the front as required to keep cache size below limit         */
+/**************************************************************************/
+
+static void XRotAddToLinkedList(dpy, item)
+    Display *dpy;
+    RotatedTextItem *item;
+{
+    
+    static long int current_size=0;
+    static RotatedTextItem *last=NULL;
+    RotatedTextItem *i1=first_text_item, *i2=NULL;
+
+#ifdef CACHE_BITMAPS
+
+    /* I don't know how much memory a pixmap takes in the server -
+           probably this + a bit more we can't account for */
+
+    item->size=((item->cols_out-1)/8+1)*item->rows_out;
+
+#else
+
+    /* this is pretty much the size of a RotatedTextItem */
+
+    item->size=((item->cols_out-1)/8+1)*item->rows_out +
+	sizeof(XImage) + strlen(item->text) + 
+	    item->nl*8*sizeof(float) + sizeof(RotatedTextItem);
+
+    if(item->font_name!=NULL)
+	item->size+=strlen(item->font_name);
+    else
+	item->size+=sizeof(Font);
+
+#endif /*CACHE_BITMAPS */
+
+#ifdef DEBUG
+    /* count number of items in cache, for debugging */
+    {
+	int i=0;
+
+	while(i1) {
+	    i++;
+	    i1=i1->next;
+	}
+	DEBUG_PRINT2("Cache has %d items.\n", i);
+	i1=first_text_item;
+    }
+#endif
+
+    DEBUG_PRINT4("current cache size=%ld, new item=%ld, limit=%ld\n",
+		 current_size, item->size, CACHE_SIZE_LIMIT*1024);
+
+    /* if this item is bigger than whole cache, forget it */
+    if(item->size>CACHE_SIZE_LIMIT*1024) {
+	DEBUG_PRINT1("Too big to cache\n\n");
+	item->cached=0;
+	return;
+    }
+
+    /* remove elements from cache as needed */
+    while(i1 && current_size+item->size>CACHE_SIZE_LIMIT*1024) {
+
+	DEBUG_PRINT2("Removed %d bytes\n", i1->size);
+
+	if(i1->font_name!=NULL)
+	    DEBUG_PRINT5("  (`%s'\n   %s\n   angle=%f align=%d)\n",
+			 i1->text, i1->font_name, i1->angle, i1->align);
+
+#ifdef CACHE_FID
+	if(i1->font_name==NULL)
+	    DEBUG_PRINT5("  (`%s'\n  FID=%ld\n   angle=%f align=%d)\n",
+                         i1->text, i1->fid, i1->angle, i1->align);
+#endif /*CACHE_FID*/
+
+	current_size-=i1->size;
+
+	i2=i1->next;
+
+	/* free resources used by the unlucky item */
+	XRotFreeTextItem(dpy, i1);
+
+	/* remove it from linked list */
+	first_text_item=i2;
+	i1=i2;
+    }
+
+    /* add new item to end of linked list */
+    if(first_text_item==NULL) {
+	item->next=NULL;
+	first_text_item=item;
+	last=item;
+    }
+    else {
+	item->next=NULL;
+	last->next=item;
+	last=item;
+    }
+
+    /* new cache size */
+    current_size+=item->size;
+
+    item->cached=1;
+
+    DEBUG_PRINT1("Added item to cache.\n");
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  Free the resources used by a text item                                */
+/**************************************************************************/
+
+static void XRotFreeTextItem(dpy, item)
+    Display *dpy;
+    RotatedTextItem *item;
+{
+    free(item->text);
+
+    if(item->font_name!=NULL)
+	free(item->font_name);
+
+    free((char *)item->corners_x);
+    free((char *)item->corners_y);
+
+#ifdef CACHE_BITMAPS
+    XFreePixmap(dpy, item->bitmap);
+#else
+    XDestroyImage(item->ximage);
+#endif /* CACHE_BITMAPS */
+
+    free((char *)item);
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Magnify an XImage using bilinear interpolation                         */
+/**************************************************************************/
+
+static XImage *XRotMagnifyImage(dpy, ximage)
+    Display *dpy;
+    XImage *ximage;
+{
+    int i, j;
+    float x, y;
+    float u,t;
+    XImage *I_out;
+    int cols_in, rows_in;
+    int cols_out, rows_out;
+    register int i2, j2;
+    float z1, z2, z3, z4;
+    int byte_width_in, byte_width_out;
+    float mag_inv;
+
+    /* size of input image */
+    cols_in=ximage->width;
+    rows_in=ximage->height;
+
+    /* size of final image */
+    cols_out=(float)cols_in*style.magnify;
+    rows_out=(float)rows_in*style.magnify;
+
+    /* this will hold final image */
+    I_out=MakeXImage(dpy, cols_out, rows_out);
+    if(I_out==NULL)
+	return NULL;
+
+    /* width in bytes of input, output images */
+    byte_width_in=(cols_in-1)/8+1;
+    byte_width_out=(cols_out-1)/8+1;
+
+    /* for speed */
+    mag_inv=1./style.magnify;
+
+    y=0.;
+
+    /* loop over magnified image */
+    for(j2=0; j2<rows_out; j2++) {
+	x=0;
+	j=y;
+
+	for(i2=0; i2<cols_out; i2++) {
+	    i=x;
+
+	    /* bilinear interpolation - where are we on bitmap ? */
+	    /* right edge */
+	    if(i==cols_in-1 && j!=rows_in-1) {
+		t=0;
+		u=y-(float)j;
+
+		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
+		z2=z1;
+		z3=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0;
+		z4=z3;
+	    }
+	    /* top edge */
+	    else if(i!=cols_in-1 && j==rows_in-1) {
+		t=x-(float)i;
+		u=0;
+
+		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
+		z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0;
+		z3=z2;
+		z4=z1;
+	    }
+	    /* top right corner */
+	    else if(i==cols_in-1 && j==rows_in-1) {
+		u=0;
+		t=0;
+
+		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
+		z2=z1;
+		z3=z1;
+		z4=z1;
+	    }
+	    /* somewhere `safe' */
+	    else {
+		t=x-(float)i;
+		u=y-(float)j;
+
+		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
+		z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0;
+		z3=(ximage->data[(j+1)*byte_width_in+(i+1)/8] &
+		    128>>((i+1)%8))>0;
+		z4=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0;
+	    }
+
+	    /* if interpolated value is greater than 0.5, set bit */
+	    if(((1-t)*(1-u)*z1 + t*(1-u)*z2 + t*u*z3 + (1-t)*u*z4)>0.5)
+		I_out->data[j2*byte_width_out+i2/8]|=128>>i2%8;
+
+	    x+=mag_inv;
+	}
+	y+=mag_inv;
+    }
+    
+    /* destroy original */
+    XDestroyImage(ximage);
+
+    /* return big image */
+    return I_out;
+}
+
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Calculate the bounding box some text will have when painted            */
+/**************************************************************************/
+
+XPoint *XRotTextExtents(dpy, font, angle, x, y, text, align)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    int x, y;
+    char *text;
+    int align;
+{
+    register int i;
+    char *str1, *str2, *str3;
+    char *str2_a="\0", *str2_b="\n\0";
+    int height;
+    float sin_angle, cos_angle;
+    int nl, max_width;
+    int cols_in, rows_in;
+    float hot_x, hot_y;
+    XPoint *xp_in, *xp_out;
+    int dir, asc, desc;
+    XCharStruct overall;
+    
+    /* manipulate angle to 0<=angle<360 degrees */
+    while(angle<0)
+        angle+=360;
+    
+    while(angle>360)
+        angle-=360;
+    
+    angle*=M_PI/180;
+    
+    /* count number of sections in string */
+    nl=1;
+    if(align!=NONE)
+	for(i=0; i<strlen(text)-1; i++)
+	    if(text[i]=='\n')
+		nl++;
+    
+    /* ignore newline characters if not doing alignment */
+    if(align==NONE)
+	str2=str2_a;
+    else
+	str2=str2_b;
+    
+    /* find width of longest section */
+    str1=my_strdup(text);
+    if(str1==NULL)
+	return NULL;
+    
+    str3=my_strtok(str1, str2);
+
+    XTextExtents(font, str3, (int) strlen(str3), &dir, &asc, &desc,
+		 &overall);
+
+    max_width=overall.rbearing;
+    
+    /* loop through each section */
+    do {
+	str3=my_strtok((char *)NULL, str2);
+
+	if(str3!=NULL) {
+	    XTextExtents(font, str3, (int) strlen(str3), &dir, &asc, &desc,
+			 &overall);
+
+	    if(overall.rbearing>max_width)
+		max_width=overall.rbearing;
+	}
+    }
+    while(str3!=NULL);
+    
+    free(str1);
+    
+    /* overall font height */
+    height=font->ascent+font->descent;
+    
+    /* dimensions horizontal text will have */
+    cols_in=max_width;
+    rows_in=nl*height;
+    
+    /* pre-calculate sin and cos */
+    sin_angle=sin(angle);
+    cos_angle=cos(angle);
+    
+    /* y position */
+    if(align==TLEFT || align==TCENTRE || align==TRIGHT)
+        hot_y=(float)rows_in/2*style.magnify;
+    else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
+	hot_y=0;
+    else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
+	hot_y=-(float)rows_in/2*style.magnify;
+    else
+	hot_y=-((float)rows_in/2-(float)font->descent)*style.magnify;
+    
+    /* x position */
+    if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
+	hot_x=-(float)max_width/2*style.magnify;
+    else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
+	hot_x=0;
+    else
+        hot_x=(float)max_width/2*style.magnify;
+    
+    /* reserve space for XPoints */
+    xp_in=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
+    if(!xp_in)
+	return NULL;
+
+    xp_out=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
+    if(!xp_out)
+	return NULL;
+
+    /* bounding box when horizontal, relative to bitmap centre */
+    xp_in[0].x=-(float)cols_in*style.magnify/2-style.bbx_pad;
+    xp_in[0].y= (float)rows_in*style.magnify/2+style.bbx_pad;
+    xp_in[1].x= (float)cols_in*style.magnify/2+style.bbx_pad;
+    xp_in[1].y= (float)rows_in*style.magnify/2+style.bbx_pad;
+    xp_in[2].x= (float)cols_in*style.magnify/2+style.bbx_pad;
+    xp_in[2].y=-(float)rows_in*style.magnify/2-style.bbx_pad;
+    xp_in[3].x=-(float)cols_in*style.magnify/2-style.bbx_pad;
+    xp_in[3].y=-(float)rows_in*style.magnify/2-style.bbx_pad;
+    xp_in[4].x=xp_in[0].x;
+    xp_in[4].y=xp_in[0].y;
+	
+    /* rotate and translate bounding box */
+    for(i=0; i<5; i++) {
+	xp_out[i].x=(float)x + ( ((float)xp_in[i].x-hot_x)*cos_angle +
+				 ((float)xp_in[i].y+hot_y)*sin_angle);
+	xp_out[i].y=(float)y + (-((float)xp_in[i].x-hot_x)*sin_angle +
+				 ((float)xp_in[i].y+hot_y)*cos_angle);
+    }
+
+    free((char *)xp_in);
+
+    return xp_out;
+}
+
+
diff -Naur tk8.5a5/unix/rotated.h tk8.5a5_with_atext/unix/rotated.h
--- tk8.5a5/unix/rotated.h	1970-01-01 01:00:00.000000000 +0100
+++ tk8.5a5_with_atext/unix/rotated.h	2006-12-07 20:04:19.950209936 +0000
@@ -0,0 +1,79 @@
+/* ************************************************************************ */
+
+
+/* Header file for the `xvertext 5.0' routines.
+
+   Copyright (c) 1993 Alan Richardson ([email protected]) */
+
+
+/* ************************************************************************ */
+
+#ifndef _XVERTEXT_INCLUDED_ 
+#define _XVERTEXT_INCLUDED_
+
+
+#define XV_VERSION      5.0
+#define XV_COPYRIGHT \
+      "xvertext routines Copyright (c) 1993 Alan Richardson"
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* text alignment */
+
+#define NONE             0
+#define TLEFT            1
+#define TCENTRE          2
+#define TRIGHT           3
+#define MLEFT            4
+#define MCENTRE          5
+#define MRIGHT           6
+#define BLEFT            7
+#define BCENTRE          8
+#define BRIGHT           9
+
+
+/* ---------------------------------------------------------------------- */
+
+/* this shoulf be C++ compliant, thanks to 
+     [email protected] (Vasco Lopes Paulo) */
+
+#if defined(__cplusplus) || defined(c_plusplus)
+
+extern "C" {
+float   XRotVersion(char*, int);
+void    XRotSetMagnification(float);
+void    XRotSetBoundingBoxPad(int);
+int     XRotDrawString(Display*, XFontStruct*, float,
+                       Drawable, GC, int, int, char*);
+int     XRotDrawImageString(Display*, XFontStruct*, float,
+                            Drawable, GC, int, int, char*);
+int     XRotDrawAlignedString(Display*, XFontStruct*, float,
+                              Drawable, GC, int, int, char*, int);
+int     XRotDrawAlignedImageString(Display*, XFontStruct*, float,
+                                   Drawable, GC, int, int, char*, int);
+XPoint *XRotTextExtents(Display*, XFontStruct*, float,
+			int, int, char*, int);
+}
+
+#else
+
+extern float   XRotVersion();
+extern void    XRotSetMagnification();
+extern void    XRotSetBoundingBoxPad();
+extern int     XRotDrawString();
+extern int     XRotDrawImageString();
+extern int     XRotDrawAlignedString();
+extern int     XRotDrawAlignedImageString();
+extern XPoint *XRotTextExtents();
+
+#endif /* __cplusplus */
+
+/* ---------------------------------------------------------------------- */
+
+
+#endif /* _XVERTEXT_INCLUDED_ */
+
+
+
diff -Naur tk8.5a5/unix/test_atext.tcl tk8.5a5_with_atext/unix/test_atext.tcl
--- tk8.5a5/unix/test_atext.tcl	1970-01-01 01:00:00.000000000 +0100
+++ tk8.5a5_with_atext/unix/test_atext.tcl	2006-12-07 23:24:56.340297794 +0000
@@ -0,0 +1,18 @@
+# Test proggy for angled text on a canvas
+
+set can [canvas .c -width 500 -height 500 -bg #aaaa88]
+pack $can
+proc mark {can coords} {
+    set rad 3
+    $can create oval [expr {[lindex $coords 0]-$rad}] [expr {[lindex $coords 1]-$rad}] [expr {[lindex $coords 0]+$rad}] [expr {[lindex $coords 1]+$rad}] -fill magenta
+}
+
+$can create text 290 50 -text ".1 Angled at\n.2 0 degrees\n.3 with >\t<tab\n.4 last line" -font {veranda 15} -fill cyan -tags t4
+$can create text 150 190 -text ".1 Angled at\n.2 45 degrees\n.3 with >\t<tab\n.4 last line" -angle 45 -font {veranda 20} -fill cyan -tags t1
+$can create text 250 385 -text "Single line at 0" -fill green4 -font {veranda 20} -tags t2
+$can create text 300 350 -text "Single line at 30" -fill green4 -font {veranda 20} -angle 30 -tags t5
+$can create text 130 370 -text ".1 Angled at\n.2 90 degrees\n.3 with >\t<tab\n.4 last line" -angle 90 -fill yellow -font {veranda 18}  -tags t3
+foreach t {t1 t2 t3  t4 t5} {
+    $can create rectangle [$can bbox $t] -outline blue
+    mark $can [$can coords $t]
+}
diff -Naur tk8.5a5/unix/tkUnixButton.c tk8.5a5_with_atext/unix/tkUnixButton.c
--- tk8.5a5/unix/tkUnixButton.c	2005-11-27 02:36:16.000000000 +0000
+++ tk8.5a5_with_atext/unix/tkUnixButton.c	2006-12-07 20:19:00.920460265 +0000
@@ -581,7 +581,7 @@
 	}
 
 	Tk_DrawTextLayout(butPtr->display, pixmap, gc,
-		butPtr->textLayout, x + textXOffset, y + textYOffset, 0, -1);
+			  butPtr->textLayout, x + textXOffset, y + textYOffset, 0, -1, 0.0);
 	Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
 		butPtr->textLayout, x + textXOffset, y + textYOffset,
 		butPtr->underline);
@@ -664,7 +664,7 @@
 		y += offset;
 	    }
 	    Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
-		    x, y, 0, -1);
+		    x, y, 0, -1, 0.0);
 	    Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
 		    butPtr->textLayout, x, y, butPtr->underline);
 	    y += butPtr->textHeight/2;
@@ -887,7 +887,7 @@
 
 	butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
 		Tcl_GetString(butPtr->textPtr), -1, butPtr->wrapLength,
-		butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight);
+						  butPtr->justify, 0, 0.0, &butPtr->textWidth, &butPtr->textHeight);
 
 	txtWidth = butPtr->textWidth;
 	txtHeight = butPtr->textHeight;
diff -Naur tk8.5a5/unix/tkUnixFont.c tk8.5a5_with_atext/unix/tkUnixFont.c
--- tk8.5a5/unix/tkUnixFont.c	2006-10-05 22:27:43.000000000 +0100
+++ tk8.5a5_with_atext/unix/tkUnixFont.c	2006-12-07 23:14:34.896778036 +0000
@@ -1230,8 +1230,9 @@
 				 * 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
+    int x, int y,		/* Coordinates at which to place origin of
 				 * string when drawing. */
+    double angle)               /* The angle at which to orientate the text string */
 {
     UnixFont *fontPtr;
     SubFont *thisSubFontPtr, *lastSubFontPtr;
@@ -1299,9 +1300,8 @@
 				Tcl_DStringLength(&runString) / 2);
 		    }
 		} else {
-		    XDrawString(display, drawable, gc, x, y,
-			    Tcl_DStringValue(&runString),
-			    Tcl_DStringLength(&runString));
+		    XRotDrawString(display,  lastSubFontPtr->fontStructPtr, (float) angle, drawable, gc, x, y,
+			    Tcl_DStringValue(&runString));
 		    if (do_width) {
 			x += XTextWidth(lastSubFontPtr->fontStructPtr,
 				Tcl_DStringValue(&runString),
@@ -1356,7 +1356,7 @@
 
 void
 TkpDrawCharsInContext(display, drawable, gc, tkfont, source, numBytes,
-	rangeStart, rangeLength, x, y)
+		      rangeStart, rangeLength, x, y, angle)
     Display * display;	    /* Display on which to draw. */
     Drawable drawable;	    /* Window or pixmap in which to draw. */
     GC gc;		    /* Graphics context for drawing characters. */
@@ -1375,10 +1375,11 @@
     int x; int y;	    /* Coordinates at which to place origin of the
 			     * whole (not just the range) string when
 			     * drawing. */
+    double angle;           /* The angle at which to draw the text */
 {
     (void) numBytes; /*unused*/
     Tk_DrawChars(display, drawable, gc, tkfont,
-	    source + rangeStart, rangeLength, x, y);
+		 source + rangeStart, rangeLength, x, y, angle);
 }
 
 /*
diff -Naur tk8.5a5/unix/tkUnixMenubu.c tk8.5a5_with_atext/unix/tkUnixMenubu.c
--- tk8.5a5/unix/tkUnixMenubu.c	2005-11-13 21:00:17.000000000 +0000
+++ tk8.5a5_with_atext/unix/tkUnixMenubu.c	2006-12-07 20:12:02.749063524 +0000
@@ -201,7 +201,7 @@
 	}
 
 	Tk_DrawTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout,
-		x + textXOffset, y + textYOffset, 0, -1);
+			  x + textXOffset, y + textYOffset, 0, -1, 0.0);
 	Tk_UnderlineTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout,
 		x + textXOffset, y + textYOffset, mbPtr->underline);
     } else if (haveImage) {
@@ -224,7 +224,7 @@
 		mbPtr->textWidth + mbPtr->indicatorWidth,
 		mbPtr->textHeight, &x, &y);
 	Tk_DrawTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout,
-		x + textXOffset, y + textYOffset, 0, -1);
+			  x + textXOffset, y + textYOffset, 0, -1, 0.0);
 	Tk_UnderlineTextLayout(mbPtr->display, pixmap, gc,
 		mbPtr->textLayout, x + textXOffset, y + textYOffset,
 		mbPtr->underline);
@@ -379,7 +379,7 @@
 	Tk_FreeTextLayout(mbPtr->textLayout);
 
 	mbPtr->textLayout = Tk_ComputeTextLayout(mbPtr->tkfont, mbPtr->text,
-		-1, mbPtr->wrapLength, mbPtr->justify, 0, &mbPtr->textWidth,
+		-1, mbPtr->wrapLength, mbPtr->justify, 0, 0.0, &mbPtr->textWidth,
 		&mbPtr->textHeight);
 	txtWidth = mbPtr->textWidth;
 	txtHeight = mbPtr->textHeight;
diff -Naur tk8.5a5/unix/tkUnixMenu.c tk8.5a5_with_atext/unix/tkUnixMenu.c
--- tk8.5a5/unix/tkUnixMenu.c	2005-11-27 02:36:16.000000000 +0000
+++ tk8.5a5_with_atext/unix/tkUnixMenu.c	2006-12-07 20:09:58.235516726 +0000
@@ -534,7 +534,7 @@
 	}
     	Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel,
 		mePtr->accelLength, left,
-		(y + (height + fmPtr->ascent - fmPtr->descent) / 2));
+		(y + (height + fmPtr->ascent - fmPtr->descent) / 2), 0.0);
     }
 }
 
@@ -814,7 +814,7 @@
 	    char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
 	    Tk_DrawChars(menuPtr->display, d, gc, tkfont, label,
 		    mePtr->labelLength, leftEdge + textXOffset,
-		    baseline + textYOffset);
+			 baseline + textYOffset, 0.0);
 	    DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr,
 		    x + textXOffset, y + textYOffset,
 		    width, height);
diff -Naur tk8.5a5/unix/tkUnixPort.h tk8.5a5_with_atext/unix/tkUnixPort.h
--- tk8.5a5/unix/tkUnixPort.h	2006-09-06 23:39:28.000000000 +0100
+++ tk8.5a5_with_atext/unix/tkUnixPort.h	2006-12-07 20:04:20.009193229 +0000
@@ -33,7 +33,7 @@
 #       define VOID char
 #   endif
 #endif
-
+#include "../unix/rotated.h"
 #include <stdio.h>
 #include <ctype.h>
 #include <fcntl.h>
diff -Naur tk8.5a5/unix/tkUnixScale.c tk8.5a5_with_atext/unix/tkUnixScale.c
--- tk8.5a5/unix/tkUnixScale.c	2005-11-14 11:54:21.000000000 +0000
+++ tk8.5a5_with_atext/unix/tkUnixScale.c	2006-12-07 20:08:17.649258458 +0000
@@ -227,7 +227,7 @@
 	Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC,
 		scalePtr->tkfont, scalePtr->label,
                 scalePtr->labelLength, scalePtr->vertLabelX,
-                scalePtr->inset + (3*fm.ascent)/2);
+                scalePtr->inset + (3*fm.ascent)/2, 0.0);
     }
 }
 
@@ -285,7 +285,7 @@
 	y = Tk_Height(tkwin) - scalePtr->inset - SPACING - fm.descent;
     }
     Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC,
-	    scalePtr->tkfont, valueString, length, rightEdge - width, y);
+	    scalePtr->tkfont, valueString, length, rightEdge - width, y, 0.0);
 }
 
 /*
@@ -443,7 +443,7 @@
 	Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC,
 		scalePtr->tkfont, scalePtr->label,
                 scalePtr->labelLength, scalePtr->inset + fm.ascent/2,
-                scalePtr->horizLabelY + fm.ascent);
+		     scalePtr->horizLabelY + fm.ascent, 0.0);
     }
 }
 
@@ -508,7 +508,7 @@
 	x = Tk_Width(tkwin) - scalePtr->inset - SPACING - width;
     }
     Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC,
-	    scalePtr->tkfont, valueString, length, x, y);
+	    scalePtr->tkfont, valueString, length, x, y, 0.0);
 }
 
 /*
diff -Naur tk8.5a5/xlib/rotated.c tk8.5a5_with_atext/xlib/rotated.c
--- tk8.5a5/xlib/rotated.c	1970-01-01 01:00:00.000000000 +0100
+++ tk8.5a5_with_atext/xlib/rotated.c	2006-12-07 20:04:20.060178788 +0000
@@ -0,0 +1,1577 @@
+/* ********************************************************************** */
+
+/* xvertext 5.0, Copyright (c) 1993 Alan Richardson ([email protected])
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both the
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  All work developed as a consequence of the use of
+ * this program should duly acknowledge such use. No representations are
+ * made about the suitability of this software for any purpose.  It is
+ * provided "as is" without express or implied warranty.
+ */
+
+/* ********************************************************************** */
+
+
+/* BETTER: xvertext now does rotation at any angle!!
+ *
+ * BEWARE: function arguments have CHANGED since version 2.0!!
+ */
+
+/* ********************************************************************** */
+
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <stdio.h>
+#include <math.h>
+#include "rotated.h"
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* Make sure cache size is set */
+
+#ifndef CACHE_SIZE_LIMIT
+#define CACHE_SIZE_LIMIT 0
+#endif /*CACHE_SIZE_LIMIT */
+    
+/* Make sure a cache method is specified */
+
+#ifndef CACHE_XIMAGES
+#ifndef CACHE_BITMAPS
+#define CACHE_BITMAPS
+#endif /*CACHE_BITMAPS*/
+#endif /*CACHE_XIMAGES*/
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* Debugging macros */
+
+#ifdef DEBUG
+static int debug=1;
+#else
+static int debug=0;
+#endif /*DEBUG*/
+
+#define DEBUG_PRINT1(a) if (debug) printf (a)
+#define DEBUG_PRINT2(a, b) if (debug) printf (a, b)
+#define DEBUG_PRINT3(a, b, c) if (debug) printf (a, b, c)
+#define DEBUG_PRINT4(a, b, c, d) if (debug) printf (a, b, c, d)
+#define DEBUG_PRINT5(a, b, c, d, e) if (debug) printf (a, b, c, d, e)
+
+
+/* ---------------------------------------------------------------------- */
+
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* A structure holding everything needed for a rotated string */
+
+typedef struct rotated_text_item_template {
+    Pixmap bitmap;
+    XImage *ximage;
+    
+    char *text;
+    char *font_name;
+    Font fid;
+    float angle;
+    int align;
+    float magnify;
+    
+    int cols_in;
+    int rows_in;
+    int cols_out;
+    int rows_out;
+    
+    int nl;
+    int max_width;
+    float *corners_x;
+    float *corners_y;
+    
+    long int size;
+    int cached;
+
+    struct rotated_text_item_template *next;
+} RotatedTextItem;
+
+RotatedTextItem *first_text_item=NULL;
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* A structure holding current magnification and bounding box padding */
+
+static struct style_template {
+    float magnify;
+    int bbx_pad;
+} style={
+    1.,
+    0
+    };
+
+
+/* ---------------------------------------------------------------------- */
+
+
+static char            *my_strdup();
+static char            *my_strtok();
+
+float                   XRotVersion();
+void                    XRotSetMagnification();
+void                    XRotSetBoundingBoxPad();
+int                     XRotDrawString();
+int                     XRotDrawImageString();
+int                     XRotDrawAlignedString();
+int                     XRotDrawAlignedImageString();
+XPoint                 *XRotTextExtents();
+
+static XImage          *MakeXImage();
+static int              XRotPaintAlignedString();
+static int              XRotDrawHorizontalString();
+static RotatedTextItem *XRotRetrieveFromCache();
+static RotatedTextItem *XRotCreateTextItem();
+static void             XRotAddToLinkedList();
+static void             XRotFreeTextItem();
+static XImage          *XRotMagnifyImage();
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Routine to mimic `strdup()' (some machines don't have it)              */
+/**************************************************************************/
+
+static char *my_strdup(str)
+    char *str;
+{
+    char *s;
+    
+    if(str==NULL)
+	return NULL;
+    
+    s=(char *)malloc((unsigned)(strlen(str)+1));
+    if(s!=NULL) 
+	strcpy(s, str);
+    
+    return s;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Routine to replace `strtok' : this one returns a zero length string if */
+/* it encounters two consecutive delimiters                               */
+/**************************************************************************/
+
+static char *my_strtok(str1, str2)
+    char *str1, *str2;
+{
+    char *ret;
+    int i, j, stop;
+    static int start, len;
+    static char *stext;
+    
+    if(str2==NULL)
+	return NULL;
+    
+    /* initialise if str1 not NULL */
+    if(str1!=NULL) {
+	start=0;
+	stext=str1;
+	len=strlen(str1);
+    }
+    
+    /* run out of tokens ? */
+    if(start>=len)
+	return NULL;
+    
+    /* loop through characters */
+    for(i=start; i<len; i++) {
+	/* loop through delimiters */
+	stop=0;
+	for(j=0; j<strlen(str2); j++)
+	    if(stext[i]==str2[j])
+		stop=1;
+	
+	if(stop)
+	    break;
+    }
+    
+    stext[i]='\0';
+    
+    ret=stext+start;
+    
+    start=i+1;
+    
+    return ret;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Return version/copyright information                                   */
+/**************************************************************************/
+
+float XRotVersion(str, n)
+    char *str;
+    int n;
+{
+    if(str!=NULL)
+	strncpy(str, XV_COPYRIGHT, n);
+    return XV_VERSION;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Set the font magnification factor for all subsequent operations        */
+/**************************************************************************/
+
+void XRotSetMagnification(m)
+    float m;
+{
+    if(m>0.)
+	style.magnify=m;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Set the padding used when calculating bounding boxes                   */
+/**************************************************************************/
+
+void XRotSetBoundingBoxPad(p)
+    int p;
+{
+    if(p>=0)
+	style.bbx_pad=p;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  Create an XImage structure and allocate memory for it                 */
+/**************************************************************************/
+
+static XImage *MakeXImage(dpy, w, h)
+    Display *dpy;
+    int w, h;
+{
+    XImage *I;
+    char *data;
+    
+    /* reserve memory for image */
+    data=(char *)calloc((unsigned)(((w-1)/8+1)*h), 1);
+    if(data==NULL)
+	return NULL;
+    
+    /* create the XImage */
+    I=XCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 1, XYBitmap,
+                   0, data, w, h, 8, 0);
+    if(I==NULL)
+	return NULL;
+    
+    I->byte_order=I->bitmap_bit_order=MSBFirst;
+    return I;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  A front end to XRotPaintAlignedString:                                */
+/*      -no alignment, no background                                      */
+/**************************************************************************/
+
+int XRotDrawString(dpy, font, angle, drawable, gc, x, y, str)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    char *str;
+{
+    return (XRotPaintAlignedString(dpy, font, angle, drawable, gc,
+				   x, y, str, NONE, 0));
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  A front end to XRotPaintAlignedString:                                */
+/*      -no alignment, paints background                                  */
+/**************************************************************************/
+
+int XRotDrawImageString(dpy, font, angle, drawable, gc, x, y, str)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    char *str;
+{
+    return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
+				  x, y, str, NONE, 1));
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  A front end to XRotPaintAlignedString:                                */
+/*      -does alignment, no background                                    */
+/**************************************************************************/
+
+int XRotDrawAlignedString(dpy, font, angle, drawable, gc, x, y, text, align)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    char *text;
+    int align;
+{
+    return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
+				  x, y, text, align, 0));
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  A front end to XRotPaintAlignedString:                                */
+/*      -does alignment, paints background                                */
+/**************************************************************************/
+
+int XRotDrawAlignedImageString(dpy, font, angle, drawable, gc, x, y, text,
+			       align)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    char *text;
+    int align;
+{
+    return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
+				  x, y, text, align, 1));
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  Aligns and paints a rotated string                                    */
+/**************************************************************************/
+
+static int XRotPaintAlignedString(dpy, font, angle, drawable, gc, x, y, text,
+				  align, bg)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    char *text;
+    int align;
+    int bg;
+{
+    int i;
+    GC my_gc;
+    int xp, yp;
+    float hot_x, hot_y;
+    float hot_xp, hot_yp;
+    float sin_angle, cos_angle;
+    RotatedTextItem *item;
+    Pixmap bitmap_to_paint;
+    
+    /* return early for NULL/empty strings */
+    if(text==NULL)
+        return 0;
+    
+    if(strlen(text)==0)
+	return 0;
+
+    /* manipulate angle to 0<=angle<360 degrees */
+    while(angle<0)
+        angle+=360;
+    
+    while(angle>=360)
+        angle-=360;
+    
+    angle*=M_PI/180;
+    
+    /* horizontal text made easy */
+    if(angle==0. && style.magnify==1.) 
+	return(XRotDrawHorizontalString(dpy, font, drawable, gc, x, y,
+					text, align, bg));
+    
+    /* get a rotated bitmap */
+    item=XRotRetrieveFromCache(dpy, font, angle, text, align);
+    if(item==NULL)
+	return NULL;
+    
+    /* this gc has similar properties to the user's gc */
+    my_gc=XCreateGC(dpy, drawable, NULL, 0);
+    XCopyGC(dpy, gc, GCForeground|GCBackground|GCFunction|GCPlaneMask,
+	    my_gc);
+
+    /* alignment : which point (hot_x, hot_y) relative to bitmap centre
+       coincides with user's specified point? */
+    
+    /* y position */
+    if(align==TLEFT || align==TCENTRE || align==TRIGHT)
+        hot_y=(float)item->rows_in/2*style.magnify;
+    else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
+	hot_y=0;
+    else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
+	hot_y=-(float)item->rows_in/2*style.magnify;
+    else
+	hot_y=-((float)item->rows_in/2-(float)font->descent)*style.magnify;
+    
+    /* x position */
+    if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
+	hot_x=-(float)item->max_width/2*style.magnify;
+    else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
+	hot_x=0;
+    else
+        hot_x=(float)item->max_width/2*style.magnify;
+    
+    /* pre-calculate sin and cos */
+    sin_angle=sin(angle);
+    cos_angle=cos(angle);
+    
+    /* rotate hot_x and hot_y around bitmap centre */
+    hot_xp= hot_x*cos_angle - hot_y*sin_angle;
+    hot_yp= hot_x*sin_angle + hot_y*cos_angle;
+    
+    /* text background will be drawn using XFillPolygon */
+    if(bg) {
+	GC depth_one_gc;
+	XPoint *xpoints;
+	Pixmap empty_stipple;
+	
+	/* reserve space for XPoints */
+	xpoints=(XPoint *)malloc((unsigned)(4*item->nl*sizeof(XPoint)));
+	if(!xpoints)
+	    return 1;
+	
+	/* rotate corner positions */
+	for(i=0; i<4*item->nl; i++) {
+	    xpoints[i].x=(float)x + ( (item->corners_x[i]-hot_x)*cos_angle + 
+				      (item->corners_y[i]+hot_y)*sin_angle);
+	    xpoints[i].y=(float)y + (-(item->corners_x[i]-hot_x)*sin_angle + 
+				      (item->corners_y[i]+hot_y)*cos_angle);
+	}
+	
+	/* we want to swap foreground and background colors here;
+	   XGetGCValues() is only available in R4+ */
+	
+	empty_stipple=XCreatePixmap(dpy, drawable, 1, 1, 1);
+	
+	depth_one_gc=XCreateGC(dpy, empty_stipple, NULL, 0);
+	XSetForeground(dpy, depth_one_gc, 0);
+	XFillRectangle(dpy, empty_stipple, depth_one_gc, 0, 0, 2, 2);
+
+	XSetStipple(dpy, my_gc, empty_stipple);
+	XSetFillStyle(dpy, my_gc, FillOpaqueStippled);
+	
+	XFillPolygon(dpy, drawable, my_gc, xpoints, 4*item->nl, Nonconvex,
+		     CoordModeOrigin);
+	
+	/* free our resources */
+	free((char *)xpoints);
+	XFreeGC(dpy, depth_one_gc);
+	XFreePixmap(dpy, empty_stipple);
+    }
+    
+    /* where should top left corner of bitmap go ? */
+    xp=(float)x-((float)item->cols_out/2 +hot_xp);
+    yp=(float)y-((float)item->rows_out/2 -hot_yp);
+    
+    /* by default we draw the rotated bitmap, solid */
+    bitmap_to_paint=item->bitmap;
+
+    /* handle user stippling */
+#ifndef X11R3
+    {
+	GC depth_one_gc;
+	XGCValues values;
+	Pixmap new_bitmap, inverse;
+	
+	/* try and get some GC properties */
+	if(XGetGCValues(dpy, gc, 
+			GCStipple|GCFillStyle|GCForeground|GCBackground|
+			GCTileStipXOrigin|GCTileStipYOrigin,
+			&values)) {
+
+	    /* only do this if stippling requested */
+	    if((values.fill_style==FillStippled ||
+		values.fill_style==FillOpaqueStippled) && !bg) {
+
+		/* opaque stipple: draw rotated text in background colour */
+		if(values.fill_style==FillOpaqueStippled) {
+		    XSetForeground(dpy, my_gc, values.background);
+		    XSetFillStyle(dpy, my_gc, FillStippled);
+		    XSetStipple(dpy, my_gc, item->bitmap);
+		    XSetTSOrigin(dpy, my_gc, xp, yp);
+		    XFillRectangle(dpy, drawable, my_gc, xp, yp,
+				   item->cols_out, item->rows_out);
+		    XSetForeground(dpy, my_gc, values.foreground);
+		}
+
+		/* this will merge the rotated text and the user's stipple */
+		new_bitmap=XCreatePixmap(dpy, drawable,
+					 item->cols_out, item->rows_out, 1);
+
+		/* create a GC */
+		depth_one_gc=XCreateGC(dpy, new_bitmap, NULL, 0);
+		XSetForeground(dpy, depth_one_gc, 1);
+		XSetBackground(dpy, depth_one_gc, 0);
+
+		/* set the relative stipple origin */
+		XSetTSOrigin(dpy, depth_one_gc, 
+			     values.ts_x_origin-xp, values.ts_y_origin-yp);
+
+		/* fill the whole bitmap with the user's stipple */
+		XSetStipple(dpy, depth_one_gc, values.stipple);
+		XSetFillStyle(dpy, depth_one_gc, FillOpaqueStippled);
+		XFillRectangle(dpy, new_bitmap, depth_one_gc,
+			       0, 0, item->cols_out, item->rows_out);
+
+		/* set stipple origin back to normal */
+		XSetTSOrigin(dpy, depth_one_gc, 0, 0);
+
+		/* this will contain an inverse copy of the rotated text */
+		inverse=XCreatePixmap(dpy, drawable,
+				      item->cols_out, item->rows_out, 1);
+
+		/* invert text */
+		XSetFillStyle(dpy, depth_one_gc, FillSolid);
+		XSetFunction(dpy, depth_one_gc, GXcopyInverted);
+		XCopyArea(dpy, item->bitmap, inverse, depth_one_gc,
+			  0, 0, item->cols_out, item->rows_out, 0, 0);
+
+		/* now delete user's stipple everywhere EXCEPT on text */
+                XSetForeground(dpy, depth_one_gc, 0);
+                XSetBackground(dpy, depth_one_gc, 1);
+		XSetStipple(dpy, depth_one_gc, inverse);
+		XSetFillStyle(dpy, depth_one_gc, FillStippled);
+		XSetFunction(dpy, depth_one_gc, GXcopy);
+		XFillRectangle(dpy, new_bitmap, depth_one_gc,
+                               0, 0, item->cols_out, item->rows_out);
+
+		/* free resources */
+		XFreePixmap(dpy, inverse);
+		XFreeGC(dpy, depth_one_gc);
+
+		/* this is the new bitmap */
+		bitmap_to_paint=new_bitmap;
+	    }
+	}
+    }
+#endif /*X11R3*/
+
+    /* paint text using stipple technique */
+    XSetFillStyle(dpy, my_gc, FillStippled);
+    XSetStipple(dpy, my_gc, bitmap_to_paint);
+    XSetTSOrigin(dpy, my_gc, xp, yp);
+    XFillRectangle(dpy, drawable, my_gc, xp, yp, 
+		   item->cols_out, item->rows_out);
+    
+    /* free our resources */
+    XFreeGC(dpy, my_gc);
+
+    /* stippled bitmap no longer needed */
+    if(bitmap_to_paint!=item->bitmap)
+	XFreePixmap(dpy, bitmap_to_paint);
+
+#ifdef CACHE_XIMAGES
+    XFreePixmap(dpy, item->bitmap);
+#endif /*CACHE_XIMAGES*/
+
+    /* if item isn't cached, destroy it completely */
+    if(!item->cached) 
+	XRotFreeTextItem(dpy,item);
+
+    /* we got to the end OK! */
+    return 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  Draw a horizontal string in a quick fashion                           */
+/**************************************************************************/
+
+static int XRotDrawHorizontalString(dpy, font, drawable, gc, x, y, text, 
+			     align, bg)
+    Display *dpy;
+    XFontStruct *font;
+    Drawable drawable;
+    GC gc;
+    int x, y;
+    char *text;
+    int align;
+    int bg;
+{
+    GC my_gc;
+    int nl=1, i;
+    int height;
+    int xp, yp;
+    char *str1, *str2, *str3;
+    char *str2_a="\0", *str2_b="\n\0";
+    int dir, asc, desc;
+    XCharStruct overall;
+
+    DEBUG_PRINT1("**\nHorizontal text.\n");
+
+    /* this gc has similar properties to the user's gc (including stipple) */
+    my_gc=XCreateGC(dpy, drawable, NULL, 0);
+    XCopyGC(dpy, gc,
+	    GCForeground|GCBackground|GCFunction|GCStipple|GCFillStyle|
+	    GCTileStipXOrigin|GCTileStipYOrigin|GCPlaneMask, my_gc);
+    XSetFont(dpy, my_gc, font->fid);
+	
+    /* count number of sections in string */
+    if(align!=NONE)
+	for(i=0; i<strlen(text)-1; i++)
+	    if(text[i]=='\n')
+		nl++;
+    
+    /* ignore newline characters if not doing alignment */
+    if(align==NONE)
+	str2=str2_a;
+    else
+	str2=str2_b;
+    
+    /* overall font height */
+    height=font->ascent+font->descent;
+    
+    /* y position */
+    if(align==TLEFT || align==TCENTRE || align==TRIGHT)
+	yp=y+font->ascent;
+    else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
+	yp=y-nl*height/2+font->ascent;
+    else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
+	yp=y-nl*height+font->ascent;
+    else
+	yp=y;
+    
+    str1=my_strdup(text);
+    if(str1==NULL)
+	return 1;
+    
+    str3=my_strtok(str1, str2);
+    
+    /* loop through each section in the string */
+    do {
+        XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
+                     &overall);
+
+	/* where to draw section in x ? */
+	if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
+	    xp=x;
+	else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
+	    xp=x-overall.rbearing/2;
+	else
+	    xp=x-overall.rbearing;
+	
+	/* draw string onto bitmap */
+	if(!bg)
+	    XDrawString(dpy, drawable, my_gc, xp, yp, str3, strlen(str3));
+	else
+	    XDrawImageString(dpy, drawable, my_gc, xp, yp, str3, strlen(str3));
+	
+	/* move to next line */
+	yp+=height;
+	
+	str3=my_strtok((char *)NULL, str2);
+    }
+    while(str3!=NULL);
+    
+    free(str1);
+    XFreeGC(dpy, my_gc);
+
+    return 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*   Query cache for a match with this font/text/angle/alignment          */
+/*       request, otherwise arrange for its creation                      */
+/**************************************************************************/
+
+static RotatedTextItem *XRotRetrieveFromCache(dpy, font, angle, text, align)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    char *text;
+    int align;
+{
+    Font fid;
+    char *font_name=NULL;
+    unsigned long name_value;
+    RotatedTextItem *item=NULL;
+    RotatedTextItem *i1=first_text_item;
+    
+    /* get font name, if it exists */
+    if(XGetFontProperty(font, XA_FONT, &name_value)) {
+	DEBUG_PRINT1("got font name OK\n");
+	font_name=XGetAtomName(dpy, name_value);
+	fid=0;
+    }
+#ifdef CACHE_FID
+    /* otherwise rely (unreliably?) on font ID */
+    else {
+	DEBUG_PRINT1("can't get fontname, caching FID\n");
+	font_name=NULL;
+	fid=font->fid;
+    }
+#else
+    /* not allowed to cache font ID's */
+    else {
+	DEBUG_PRINT1("can't get fontname, can't cache\n");
+	font_name=NULL;
+	fid=0;
+    }
+#endif /*CACHE_FID*/
+    
+    /* look for a match in cache */
+
+    /* matching formula:
+       identical text;
+       identical fontname (if defined, font ID's if not);
+       angles close enough (<0.00001 here, could be smaller);
+       HORIZONTAL alignment matches, OR it's a one line string;
+       magnifications the same */
+
+    while(i1 && !item) {
+	/* match everything EXCEPT fontname/ID */
+	if(strcmp(text, i1->text)==0 &&
+	   fabs(angle-i1->angle)<0.00001 &&
+	   style.magnify==i1->magnify &&
+	   (i1->nl==1 ||
+	    ((align==0)?9:(align-1))%3==
+	      ((i1->align==0)?9:(i1->align-1))%3)) {
+
+	    /* now match fontname/ID */
+	    if(font_name!=NULL && i1->font_name!=NULL) {
+		if(strcmp(font_name, i1->font_name)==0) {
+		    item=i1;
+		    DEBUG_PRINT1("Matched against font names\n");
+		}
+		else
+		    i1=i1->next;
+	    }
+#ifdef CACHE_FID
+	    else if(font_name==NULL && i1->font_name==NULL) {
+		if(fid==i1->fid) {
+		    item=i1;
+		    DEBUG_PRINT1("Matched against FID's\n");
+                }
+		else
+                    i1=i1->next;
+	    }
+#endif /*CACHE_FID*/
+	    else
+		i1=i1->next;
+	}
+	else
+	    i1=i1->next;
+    }
+    
+    if(item)
+	DEBUG_PRINT1("**\nFound target in cache.\n");
+    if(!item)
+	DEBUG_PRINT1("**\nNo match in cache.\n");
+
+    /* no match */
+    if(!item) {
+	/* create new item */
+	item=XRotCreateTextItem(dpy, font, angle, text, align);
+	if(!item)
+	    return NULL;
+
+	/* record what it shows */
+	item->text=my_strdup(text);
+
+	/* fontname or ID */
+	if(font_name!=NULL) {
+	    item->font_name=my_strdup(font_name);
+	    item->fid=0;
+	}
+	else {
+	    item->font_name=NULL;
+	    item->fid=fid;
+	}
+
+	item->angle=angle;
+	item->align=align;
+	item->magnify=style.magnify;
+
+	/* cache it */
+	XRotAddToLinkedList(dpy, item);
+    }
+
+    if(font_name)
+	XFree(font_name);
+
+    /* if XImage is cached, need to recreate the bitmap */
+
+#ifdef CACHE_XIMAGES
+    {
+	GC depth_one_gc;
+
+	/* create bitmap to hold rotated text */
+	item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
+				   item->cols_out, item->rows_out, 1);
+	
+	/* depth one gc */
+	depth_one_gc=XCreateGC(dpy, item->bitmap, NULL, 0);
+	XSetBackground(dpy, depth_one_gc, 0);
+	XSetForeground(dpy, depth_one_gc, 1);
+
+	/* make the text bitmap from XImage */
+	XPutImage(dpy, item->bitmap, depth_one_gc, item->ximage, 0, 0, 0, 0,
+		  item->cols_out, item->rows_out);
+
+	XFreeGC(dpy, depth_one_gc);
+    }
+#endif /*CACHE_XIMAGES*/
+    
+    return item;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  Create a rotated text item                                            */
+/**************************************************************************/
+
+static RotatedTextItem *XRotCreateTextItem(dpy, font, angle, text, align)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    char *text;
+    int align;
+{
+    RotatedTextItem *item=NULL;
+    Pixmap canvas;
+    GC font_gc;
+    XImage *I_in;
+    register int i, j;
+    char *str1, *str2, *str3;
+    char *str2_a="\0", *str2_b="\n\0";
+    int height;
+    int byte_w_in, byte_w_out;
+    int xp, yp;
+    float sin_angle, cos_angle;
+    int it, jt;
+    float di, dj;
+    int ic=0;
+    float xl, xr, xinc;
+    int byte_out;
+    int dir, asc, desc;
+    XCharStruct overall;
+    int old_cols_in=0, old_rows_in=0;
+    
+    /* allocate memory */
+    item=(RotatedTextItem *)malloc((unsigned)sizeof(RotatedTextItem));
+    if(!item)
+	return NULL;
+	
+    /* count number of sections in string */
+    item->nl=1;
+    if(align!=NONE)
+	for(i=0; i<strlen(text)-1; i++)
+	    if(text[i]=='\n')
+		item->nl++;
+    
+    /* ignore newline characters if not doing alignment */
+    if(align==NONE)
+	str2=str2_a;
+    else
+	str2=str2_b;
+    
+    /* find width of longest section */
+    str1=my_strdup(text);
+    if(str1==NULL)
+	return NULL;
+    
+    str3=my_strtok(str1, str2);
+
+    XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
+		 &overall);
+    
+    item->max_width=overall.rbearing;
+    
+    /* loop through each section */
+    do {
+	str3=my_strtok((char *)NULL, str2);
+
+	if(str3!=NULL) {
+	    XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
+			 &overall);
+
+	    if(overall.rbearing>item->max_width)
+		item->max_width=overall.rbearing;
+	}
+    }
+    while(str3!=NULL);
+    
+    free(str1);
+    
+    /* overall font height */
+    height=font->ascent+font->descent;
+    
+    /* dimensions horizontal text will have */
+    item->cols_in=item->max_width;
+    item->rows_in=item->nl*height;
+    
+    /* bitmap for drawing on */
+    canvas=XCreatePixmap(dpy, DefaultRootWindow(dpy),
+			 item->cols_in, item->rows_in, 1);
+    
+    /* create a GC for the bitmap */
+    font_gc=XCreateGC(dpy, canvas, NULL, 0);
+    XSetBackground(dpy, font_gc, 0);
+    XSetFont(dpy, font_gc, font->fid);
+    
+    /* make sure the bitmap is blank */
+    XSetForeground(dpy, font_gc, 0);
+    XFillRectangle(dpy, canvas, font_gc, 0, 0, 
+		   item->cols_in+1, item->rows_in+1);
+    XSetForeground(dpy, font_gc, 1);
+    
+    /* pre-calculate sin and cos */
+    sin_angle=sin(angle);
+    cos_angle=cos(angle);
+    
+    /* text background will be drawn using XFillPolygon */
+    item->corners_x=
+	(float *)malloc((unsigned)(4*item->nl*sizeof(float)));
+    if(!item->corners_x)
+	return NULL;
+    
+    item->corners_y=
+	(float *)malloc((unsigned)(4*item->nl*sizeof(float)));
+    if(!item->corners_y)
+	return NULL;
+    
+    /* draw text horizontally */
+    
+    /* start at top of bitmap */
+    yp=font->ascent;
+    
+    str1=my_strdup(text);
+    if(str1==NULL)
+	return NULL;
+    
+    str3=my_strtok(str1, str2);
+    
+    /* loop through each section in the string */
+    do {
+	XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
+		&overall);
+
+	/* where to draw section in x ? */
+	if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
+	    xp=0;
+	else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
+	    xp=(item->max_width-overall.rbearing)/2;
+	else
+            xp=item->max_width-overall.rbearing;
+
+	/* draw string onto bitmap */
+	XDrawString(dpy, canvas, font_gc, xp, yp, str3, strlen(str3));
+	
+	/* keep a note of corner positions of this string */
+	item->corners_x[ic]=((float)xp-(float)item->cols_in/2)*style.magnify;
+	item->corners_y[ic]=((float)(yp-font->ascent)-(float)item->rows_in/2)
+	    *style.magnify;
+	item->corners_x[ic+1]=item->corners_x[ic];
+	item->corners_y[ic+1]=item->corners_y[ic]+(float)height*style.magnify;
+	item->corners_x[item->nl*4-1-ic]=item->corners_x[ic]+
+	    (float)overall.rbearing*style.magnify;
+	item->corners_y[item->nl*4-1-ic]=item->corners_y[ic];
+	item->corners_x[item->nl*4-2-ic]=
+	    item->corners_x[item->nl*4-1-ic];
+	item->corners_y[item->nl*4-2-ic]=item->corners_y[ic+1];
+	
+	ic+=2;
+	
+	/* move to next line */
+	yp+=height;
+	
+	str3=my_strtok((char *)NULL, str2);
+    }
+    while(str3!=NULL);
+    
+    free(str1);
+    
+    /* create image to hold horizontal text */
+    I_in=MakeXImage(dpy, item->cols_in, item->rows_in);
+    if(I_in==NULL)
+	return NULL;
+    
+    /* extract horizontal text */
+    XGetSubImage(dpy, canvas, 0, 0, item->cols_in, item->rows_in,
+		 1, XYPixmap, I_in, 0, 0);
+    I_in->format=XYBitmap;
+    
+    /* magnify horizontal text */
+    if(style.magnify!=1.) {
+	I_in=XRotMagnifyImage(dpy, I_in);
+
+	old_cols_in=item->cols_in;
+	old_rows_in=item->rows_in;
+	item->cols_in=(float)item->cols_in*style.magnify;
+	item->rows_in=(float)item->rows_in*style.magnify;
+    }
+
+    /* how big will rotated text be ? */
+    item->cols_out=fabs((float)item->rows_in*sin_angle) +
+	fabs((float)item->cols_in*cos_angle) +0.99999 +2;
+
+    item->rows_out=fabs((float)item->rows_in*cos_angle) +
+	fabs((float)item->cols_in*sin_angle) +0.99999 +2;
+
+    if(item->cols_out%2==0)
+	item->cols_out++;
+    
+    if(item->rows_out%2==0)
+	item->rows_out++;
+    
+    /* create image to hold rotated text */
+    item->ximage=MakeXImage(dpy, item->cols_out, item->rows_out);
+    if(item->ximage==NULL)
+	return NULL;
+    
+    byte_w_in=(item->cols_in-1)/8+1;
+    byte_w_out=(item->cols_out-1)/8+1;
+    
+    /* we try to make this bit as fast as possible - which is why it looks
+       a bit over-the-top */
+    
+    /* vertical distance from centre */
+    dj=0.5-(float)item->rows_out/2;
+
+    /* where abouts does text actually lie in rotated image? */
+    if(angle==0 || angle==M_PI/2 || 
+       angle==M_PI || angle==3*M_PI/2) {
+	xl=0;
+	xr=(float)item->cols_out;
+	xinc=0;
+    }
+    else if(angle<M_PI) {
+	xl=(float)item->cols_out/2+
+	    (dj-(float)item->rows_in/(2*cos_angle))/
+		tan(angle)-2;
+	xr=(float)item->cols_out/2+
+	    (dj+(float)item->rows_in/(2*cos_angle))/
+		tan(angle)+2;
+	xinc=1./tan(angle);
+    }
+    else {
+	xl=(float)item->cols_out/2+
+	    (dj+(float)item->rows_in/(2*cos_angle))/
+		tan(angle)-2;
+	xr=(float)item->cols_out/2+
+	    (dj-(float)item->rows_in/(2*cos_angle))/
+		tan(angle)+2;
+	
+	xinc=1./tan(angle);
+    }
+
+    /* loop through all relevent bits in rotated image */
+    for(j=0; j<item->rows_out; j++) {
+	
+	/* no point re-calculating these every pass */
+	di=(float)((xl<0)?0:(int)xl)+0.5-(float)item->cols_out/2;
+	byte_out=(item->rows_out-j-1)*byte_w_out;
+	
+	/* loop through meaningful columns */
+	for(i=((xl<0)?0:(int)xl); 
+	    i<((xr>=item->cols_out)?item->cols_out:(int)xr); i++) {
+	    
+	    /* rotate coordinates */
+	    it=(float)item->cols_in/2 + ( di*cos_angle + dj*sin_angle);
+	    jt=(float)item->rows_in/2 - (-di*sin_angle + dj*cos_angle);
+	    
+            /* set pixel if required */
+            if(it>=0 && it<item->cols_in && jt>=0 && jt<item->rows_in)
+                if((I_in->data[jt*byte_w_in+it/8] & 128>>(it%8))>0)
+                    item->ximage->data[byte_out+i/8]|=128>>i%8;
+	    
+	    di+=1;
+	}
+	dj+=1;
+	xl+=xinc;
+	xr+=xinc;
+    }
+    XDestroyImage(I_in);
+    
+    if(style.magnify!=1.) {
+	item->cols_in=old_cols_in;
+	item->rows_in=old_rows_in;
+    }
+
+
+#ifdef CACHE_BITMAPS
+
+    /* create a bitmap to hold rotated text */
+    item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
+			       item->cols_out, item->rows_out, 1);
+    
+    /* make the text bitmap from XImage */
+    XPutImage(dpy, item->bitmap, font_gc, item->ximage, 0, 0, 0, 0,
+	      item->cols_out, item->rows_out);
+
+    XDestroyImage(item->ximage);
+
+#endif /*CACHE_BITMAPS*/
+
+    XFreeGC(dpy, font_gc);
+    XFreePixmap(dpy, canvas);
+
+    return item;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  Adds a text item to the end of the cache, removing as many items      */
+/*      from the front as required to keep cache size below limit         */
+/**************************************************************************/
+
+static void XRotAddToLinkedList(dpy, item)
+    Display *dpy;
+    RotatedTextItem *item;
+{
+    
+    static long int current_size=0;
+    static RotatedTextItem *last=NULL;
+    RotatedTextItem *i1=first_text_item, *i2=NULL;
+
+#ifdef CACHE_BITMAPS
+
+    /* I don't know how much memory a pixmap takes in the server -
+           probably this + a bit more we can't account for */
+
+    item->size=((item->cols_out-1)/8+1)*item->rows_out;
+
+#else
+
+    /* this is pretty much the size of a RotatedTextItem */
+
+    item->size=((item->cols_out-1)/8+1)*item->rows_out +
+	sizeof(XImage) + strlen(item->text) + 
+	    item->nl*8*sizeof(float) + sizeof(RotatedTextItem);
+
+    if(item->font_name!=NULL)
+	item->size+=strlen(item->font_name);
+    else
+	item->size+=sizeof(Font);
+
+#endif /*CACHE_BITMAPS */
+
+#ifdef DEBUG
+    /* count number of items in cache, for debugging */
+    {
+	int i=0;
+
+	while(i1) {
+	    i++;
+	    i1=i1->next;
+	}
+	DEBUG_PRINT2("Cache has %d items.\n", i);
+	i1=first_text_item;
+    }
+#endif
+
+    DEBUG_PRINT4("current cache size=%ld, new item=%ld, limit=%ld\n",
+		 current_size, item->size, CACHE_SIZE_LIMIT*1024);
+
+    /* if this item is bigger than whole cache, forget it */
+    if(item->size>CACHE_SIZE_LIMIT*1024) {
+	DEBUG_PRINT1("Too big to cache\n\n");
+	item->cached=0;
+	return;
+    }
+
+    /* remove elements from cache as needed */
+    while(i1 && current_size+item->size>CACHE_SIZE_LIMIT*1024) {
+
+	DEBUG_PRINT2("Removed %d bytes\n", i1->size);
+
+	if(i1->font_name!=NULL)
+	    DEBUG_PRINT5("  (`%s'\n   %s\n   angle=%f align=%d)\n",
+			 i1->text, i1->font_name, i1->angle, i1->align);
+
+#ifdef CACHE_FID
+	if(i1->font_name==NULL)
+	    DEBUG_PRINT5("  (`%s'\n  FID=%ld\n   angle=%f align=%d)\n",
+                         i1->text, i1->fid, i1->angle, i1->align);
+#endif /*CACHE_FID*/
+
+	current_size-=i1->size;
+
+	i2=i1->next;
+
+	/* free resources used by the unlucky item */
+	XRotFreeTextItem(dpy, i1);
+
+	/* remove it from linked list */
+	first_text_item=i2;
+	i1=i2;
+    }
+
+    /* add new item to end of linked list */
+    if(first_text_item==NULL) {
+	item->next=NULL;
+	first_text_item=item;
+	last=item;
+    }
+    else {
+	item->next=NULL;
+	last->next=item;
+	last=item;
+    }
+
+    /* new cache size */
+    current_size+=item->size;
+
+    item->cached=1;
+
+    DEBUG_PRINT1("Added item to cache.\n");
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/*  Free the resources used by a text item                                */
+/**************************************************************************/
+
+static void XRotFreeTextItem(dpy, item)
+    Display *dpy;
+    RotatedTextItem *item;
+{
+    free(item->text);
+
+    if(item->font_name!=NULL)
+	free(item->font_name);
+
+    free((char *)item->corners_x);
+    free((char *)item->corners_y);
+
+#ifdef CACHE_BITMAPS
+    XFreePixmap(dpy, item->bitmap);
+#else
+    XDestroyImage(item->ximage);
+#endif /* CACHE_BITMAPS */
+
+    free((char *)item);
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Magnify an XImage using bilinear interpolation                         */
+/**************************************************************************/
+
+static XImage *XRotMagnifyImage(dpy, ximage)
+    Display *dpy;
+    XImage *ximage;
+{
+    int i, j;
+    float x, y;
+    float u,t;
+    XImage *I_out;
+    int cols_in, rows_in;
+    int cols_out, rows_out;
+    register int i2, j2;
+    float z1, z2, z3, z4;
+    int byte_width_in, byte_width_out;
+    float mag_inv;
+
+    /* size of input image */
+    cols_in=ximage->width;
+    rows_in=ximage->height;
+
+    /* size of final image */
+    cols_out=(float)cols_in*style.magnify;
+    rows_out=(float)rows_in*style.magnify;
+
+    /* this will hold final image */
+    I_out=MakeXImage(dpy, cols_out, rows_out);
+    if(I_out==NULL)
+	return NULL;
+
+    /* width in bytes of input, output images */
+    byte_width_in=(cols_in-1)/8+1;
+    byte_width_out=(cols_out-1)/8+1;
+
+    /* for speed */
+    mag_inv=1./style.magnify;
+
+    y=0.;
+
+    /* loop over magnified image */
+    for(j2=0; j2<rows_out; j2++) {
+	x=0;
+	j=y;
+
+	for(i2=0; i2<cols_out; i2++) {
+	    i=x;
+
+	    /* bilinear interpolation - where are we on bitmap ? */
+	    /* right edge */
+	    if(i==cols_in-1 && j!=rows_in-1) {
+		t=0;
+		u=y-(float)j;
+
+		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
+		z2=z1;
+		z3=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0;
+		z4=z3;
+	    }
+	    /* top edge */
+	    else if(i!=cols_in-1 && j==rows_in-1) {
+		t=x-(float)i;
+		u=0;
+
+		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
+		z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0;
+		z3=z2;
+		z4=z1;
+	    }
+	    /* top right corner */
+	    else if(i==cols_in-1 && j==rows_in-1) {
+		u=0;
+		t=0;
+
+		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
+		z2=z1;
+		z3=z1;
+		z4=z1;
+	    }
+	    /* somewhere `safe' */
+	    else {
+		t=x-(float)i;
+		u=y-(float)j;
+
+		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
+		z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0;
+		z3=(ximage->data[(j+1)*byte_width_in+(i+1)/8] &
+		    128>>((i+1)%8))>0;
+		z4=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0;
+	    }
+
+	    /* if interpolated value is greater than 0.5, set bit */
+	    if(((1-t)*(1-u)*z1 + t*(1-u)*z2 + t*u*z3 + (1-t)*u*z4)>0.5)
+		I_out->data[j2*byte_width_out+i2/8]|=128>>i2%8;
+
+	    x+=mag_inv;
+	}
+	y+=mag_inv;
+    }
+    
+    /* destroy original */
+    XDestroyImage(ximage);
+
+    /* return big image */
+    return I_out;
+}
+
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/**************************************************************************/
+/* Calculate the bounding box some text will have when painted            */
+/**************************************************************************/
+
+XPoint *XRotTextExtents(dpy, font, angle, x, y, text, align)
+    Display *dpy;
+    XFontStruct *font;
+    float angle;
+    int x, y;
+    char *text;
+    int align;
+{
+    register int i;
+    char *str1, *str2, *str3;
+    char *str2_a="\0", *str2_b="\n\0";
+    int height;
+    float sin_angle, cos_angle;
+    int nl, max_width;
+    int cols_in, rows_in;
+    float hot_x, hot_y;
+    XPoint *xp_in, *xp_out;
+    int dir, asc, desc;
+    XCharStruct overall;
+    
+    /* manipulate angle to 0<=angle<360 degrees */
+    while(angle<0)
+        angle+=360;
+    
+    while(angle>360)
+        angle-=360;
+    
+    angle*=M_PI/180;
+    
+    /* count number of sections in string */
+    nl=1;
+    if(align!=NONE)
+	for(i=0; i<strlen(text)-1; i++)
+	    if(text[i]=='\n')
+		nl++;
+    
+    /* ignore newline characters if not doing alignment */
+    if(align==NONE)
+	str2=str2_a;
+    else
+	str2=str2_b;
+    
+    /* find width of longest section */
+    str1=my_strdup(text);
+    if(str1==NULL)
+	return NULL;
+    
+    str3=my_strtok(str1, str2);
+
+    XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
+		 &overall);
+
+    max_width=overall.rbearing;
+    
+    /* loop through each section */
+    do {
+	str3=my_strtok((char *)NULL, str2);
+
+	if(str3!=NULL) {
+	    XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
+			 &overall);
+
+	    if(overall.rbearing>max_width)
+		max_width=overall.rbearing;
+	}
+    }
+    while(str3!=NULL);
+    
+    free(str1);
+    
+    /* overall font height */
+    height=font->ascent+font->descent;
+    
+    /* dimensions horizontal text will have */
+    cols_in=max_width;
+    rows_in=nl*height;
+    
+    /* pre-calculate sin and cos */
+    sin_angle=sin(angle);
+    cos_angle=cos(angle);
+    
+    /* y position */
+    if(align==TLEFT || align==TCENTRE || align==TRIGHT)
+        hot_y=(float)rows_in/2*style.magnify;
+    else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
+	hot_y=0;
+    else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
+	hot_y=-(float)rows_in/2*style.magnify;
+    else
+	hot_y=-((float)rows_in/2-(float)font->descent)*style.magnify;
+    
+    /* x position */
+    if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
+	hot_x=-(float)max_width/2*style.magnify;
+    else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
+	hot_x=0;
+    else
+        hot_x=(float)max_width/2*style.magnify;
+    
+    /* reserve space for XPoints */
+    xp_in=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
+    if(!xp_in)
+	return NULL;
+
+    xp_out=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
+    if(!xp_out)
+	return NULL;
+
+    /* bounding box when horizontal, relative to bitmap centre */
+    xp_in[0].x=-(float)cols_in*style.magnify/2-style.bbx_pad;
+    xp_in[0].y= (float)rows_in*style.magnify/2+style.bbx_pad;
+    xp_in[1].x= (float)cols_in*style.magnify/2+style.bbx_pad;
+    xp_in[1].y= (float)rows_in*style.magnify/2+style.bbx_pad;
+    xp_in[2].x= (float)cols_in*style.magnify/2+style.bbx_pad;
+    xp_in[2].y=-(float)rows_in*style.magnify/2-style.bbx_pad;
+    xp_in[3].x=-(float)cols_in*style.magnify/2-style.bbx_pad;
+    xp_in[3].y=-(float)rows_in*style.magnify/2-style.bbx_pad;
+    xp_in[4].x=xp_in[0].x;
+    xp_in[4].y=xp_in[0].y;
+	
+    /* rotate and translate bounding box */
+    for(i=0; i<5; i++) {
+	xp_out[i].x=(float)x + ( ((float)xp_in[i].x-hot_x)*cos_angle +
+				 ((float)xp_in[i].y+hot_y)*sin_angle);
+	xp_out[i].y=(float)y + (-((float)xp_in[i].x-hot_x)*sin_angle +
+				 ((float)xp_in[i].y+hot_y)*cos_angle);
+    }
+
+    free((char *)xp_in);
+
+    return xp_out;
+}
+
+
diff -Naur tk8.5a5/xlib/rotated.h tk8.5a5_with_atext/xlib/rotated.h
--- tk8.5a5/xlib/rotated.h	1970-01-01 01:00:00.000000000 +0100
+++ tk8.5a5_with_atext/xlib/rotated.h	2006-12-07 20:04:20.061178505 +0000
@@ -0,0 +1,79 @@
+/* ************************************************************************ */
+
+
+/* Header file for the `xvertext 5.0' routines.
+
+   Copyright (c) 1993 Alan Richardson ([email protected]) */
+
+
+/* ************************************************************************ */
+
+#ifndef _XVERTEXT_INCLUDED_ 
+#define _XVERTEXT_INCLUDED_
+
+
+#define XV_VERSION      5.0
+#define XV_COPYRIGHT \
+      "xvertext routines Copyright (c) 1993 Alan Richardson"
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* text alignment */
+
+#define NONE             0
+#define TLEFT            1
+#define TCENTRE          2
+#define TRIGHT           3
+#define MLEFT            4
+#define MCENTRE          5
+#define MRIGHT           6
+#define BLEFT            7
+#define BCENTRE          8
+#define BRIGHT           9
+
+
+/* ---------------------------------------------------------------------- */
+
+/* this shoulf be C++ compliant, thanks to 
+     [email protected] (Vasco Lopes Paulo) */
+
+#if defined(__cplusplus) || defined(c_plusplus)
+
+extern "C" {
+float   XRotVersion(char*, int);
+void    XRotSetMagnification(float);
+void    XRotSetBoundingBoxPad(int);
+int     XRotDrawString(Display*, XFontStruct*, float,
+                       Drawable, GC, int, int, char*);
+int     XRotDrawImageString(Display*, XFontStruct*, float,
+                            Drawable, GC, int, int, char*);
+int     XRotDrawAlignedString(Display*, XFontStruct*, float,
+                              Drawable, GC, int, int, char*, int);
+int     XRotDrawAlignedImageString(Display*, XFontStruct*, float,
+                                   Drawable, GC, int, int, char*, int);
+XPoint *XRotTextExtents(Display*, XFontStruct*, float,
+			int, int, char*, int);
+}
+
+#else
+
+extern float   XRotVersion();
+extern void    XRotSetMagnification();
+extern void    XRotSetBoundingBoxPad();
+extern int     XRotDrawString();
+extern int     XRotDrawImageString();
+extern int     XRotDrawAlignedString();
+extern int     XRotDrawAlignedImageString();
+extern XPoint *XRotTextExtents();
+
+#endif /* __cplusplus */
+
+/* ---------------------------------------------------------------------- */
+
+
+#endif /* _XVERTEXT_INCLUDED_ */
+
+
+