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_ */
+
+
+