Canvas Rotated Text Patch for Perl/Tk 800.023

2 views
Skip to first unread message

Terran Melconian

unread,
Sep 23, 2001, 3:24:02 AM9/23/01
to
This is the first release of an experimental patch to add rotating
text to Perl/Tk canvases. I tried Slaven Rezic's perl-only
implementation, and it was almost everything I wanted, but I needed
postscript output, so here's a source patch.

Status:
PostScript: should be completly working, but may have bugs
X11: *Almost* working. alignment is off by a few pixels
sometimes.
Mac/Windows: Not implemented at all, but postscript print should still
be ok

Selection: will break for rotated text

postscript was joyous and took an hour. X11 took three days, and is
not as good. The #1 problem is that you can't properly query the
metrics for a rotated X font, so you have to get another copy of the
font, nonrotated, to query. I "guess" instead.

Anyway, play with it and tell me what you think. Hopefully someone
else can do the other platforms - I certainly can't.


--- ../Tk800-orig/Tk/prolog.ps Sat Dec 30 11:12:37 2000
+++ ./Tk/prolog.ps Sat Sep 22 21:17:25 2001
@@ -207,11 +207,13 @@
% drawn in stippled fashion. If text is stippled,
% procedure StippleText must have been defined to call
% StippleFill in the right way.
+% angle - Rotation angle in degrees, positive CCW
%
% Also, when this procedure is invoked, the color and font must already
% have been set for the text.

/DrawText {
+ /angle exch def
/stipple exch def
/justify exch def
/yoffset exch def
@@ -235,19 +237,47 @@
exch pop exch sub /height exch def pop
newpath

- % Translate coordinates first so that the origin is at the upper-left
- % corner of the text's bounding box. Remember that x and y for
- % positioning are still on the stack.
-
+ % Use x and y for positioning currently on stack
translate
- lineLength xoffset mul
- strings length 1 sub spacing mul height add yoffset mul translate
+
+ % We now need to know the actual width and height of the box, including
+ % any rotations.
+ /basex lineLength def
+ /basey strings length 1 sub spacing mul height add def
+
+ /rx basex angle cos mul abs basey angle sin mul abs add def
+ /ry basex angle sin mul abs basey angle cos mul abs add def
+
+ % Then we need to know how to move the box so that the lower-left-most
+ % part is back to being in the upper-left corner.
+ angle 0 gt angle 90 gt not and {
+ 0
+ basex angle sin mul neg
+ translate } if
+ angle 90 gt angle 180 gt not and {
+ basex angle cos mul neg
+ ry neg
+ translate } if
+ angle 180 gt angle 270 gt not and {
+ rx
+ basey angle cos mul
+ translate } if
+ angle 270 gt angle 360 gt not and {
+ basey angle sin mul neg
+ 0
+ translate } if
+
+ % Now move based on the anchor
+ rx xoffset mul
+ ry yoffset mul translate

% Now use the baseline and justification information to translate so
% that the origin is at the baseline and positioning point for the
% first line of text.

- justify lineLength mul baseline neg translate
+ justify lineLength mul angle cos mul baseline angle sin mul add
+ justify lineLength mul angle sin mul baseline angle cos mul add neg
+ translate

% Iterate over each of the lines to output it. For each line,
% compute its width again so it can be properly justified, then
@@ -276,7 +306,7 @@
moveto
} forall
grestore
- } {show} ifelse
+ } {show} angle rotate ifelse
0 spacing neg translate
} forall
} bind def
diff -r -u -b -B ../Tk800-orig/pTk/mTk/generic/tk.h ./pTk/mTk/generic/tk.h
--- ../Tk800-orig/pTk/mTk/generic/tk.h Sat Dec 30 11:12:37 2000
+++ ./pTk/mTk/generic/tk.h Sat Sep 22 15:49:11 2001
@@ -1934,6 +1934,8 @@
ClientData clientData, Tk_Window tkwin,
char *widgRec, int offset,
Tcl_FreeProc **freeProcPtr));
+EXTERN Tk_Font Tk_RotateFont _ANSI_ARGS_((Tk_Font oldfnt, double angle));
+

EXTERN Tcl_Command Lang_CreateWidget _ANSI_ARGS_((Tcl_Interp *interp,
Tk_Window, Tcl_CmdProc *proc,
diff -r -u -b -B ../Tk800-orig/pTk/mTk/generic/tkCanvPs.c ./pTk/mTk/generic/tkCanvPs.c
--- ../Tk800-orig/pTk/mTk/generic/tkCanvPs.c Sat Dec 30 11:12:37 2000
+++ ./pTk/mTk/generic/tkCanvPs.c Sat Sep 22 21:16:00 2001
@@ -290,7 +290,7 @@
% acr",
/* End of part 3 */

- /* Start of part 4 (2000 characters) */
+ /* Start of part 4 (many characters) */
"oss rows, blasting out a stipple-pattern-sized rectangle at\n\
% each position\n\
\n\
@@ -341,11 +341,13 @@
% drawn in stippled fashion. If text is stippled,\n\
% procedure StippleText must have been defined to call\n\
% StippleFill in the right way.\n\
+% angle - Rotation angle in degrees, positive ccw \n\
%\n\
% Also, when this procedure is invoked, the color and font must already\n\
% have been set for the text.\n\
\n\
/DrawText {\n\
+ /angle exch def\n\
/stipple exch def\n\
/justify exch def\n\
/yoffset exch def\n\
@@ -358,11 +360,7 @@
/lineLength 0 def\n\
strings {\n\
stringwidth pop\n\
- dup lineLength gt {/lineLength exch def}",
- /* End of part 4 */
-
- /* Start of part 5 (1546 characters) */
- " {pop} ifelse\n\
+ dup lineLength gt {/lineLength exch def} {pop} ifelse\n\
newpath\n\
} forall\n\
\n\
@@ -373,19 +371,47 @@
exch pop exch sub /height exch def pop\n\
newpath\n\
\n\
- % Translate coordinates first so that the origin is at the upper-left\n\
- % corner of the text's bounding box. Remember that x and y for\n\
- % positioning are still on the stack.\n\
-\n\
+ % Use x and y for positioning currently on stack\n\
translate\n\
- lineLength xoffset mul\n\
- strings length 1 sub spacing mul height add yoffset mul translate\n\
+\n\
+ % We now need to know the actual width and height of the box, including\n\
+ % any rotations.\n\
+ /basex lineLength def\n\
+ /basey strings length 1 sub spacing mul height add def\n\
+\n\
+ /rx basex angle cos mul abs basey angle sin mul abs add def\n\
+ /ry basex angle sin mul abs basey angle cos mul abs add def\n\
+\n\
+ % Then we need to know how to move the box so that the lower-left-most\n\
+ % part is back to being in the upper-left corner.\n\
+ angle 0 gt angle 90 gt not and {\n\
+ 0\n\
+ basex angle sin mul neg\n\
+ translate } if\n\
+ angle 90 gt angle 180 gt not and {\n\
+ basex angle cos mul neg\n\
+ ry neg\n\
+ translate } if\n\
+ angle 180 gt angle 270 gt not and {\n\
+ rx\n\
+ basey angle cos mul\n\
+ translate } if\n\
+ angle 270 gt angle 360 gt not and {\n\
+ basey angle sin mul neg\n\
+ 0\n\
+ translate } if\n\
+\n\
+ % Now move based on the anchor\n\
+ rx xoffset mul\n\
+ ry yoffset mul translate\n\
\n\
% Now use the baseline and justification information to translate so\n\
% that the origin is at the baseline and positioning point for the\n\
% first line of text.\n\
\n\
- justify lineLength mul baseline neg translate\n\
+ justify lineLength mul angle cos mul baseline angle sin mul add\n\
+ justify lineLength mul angle sin mul baseline angle cos mul add neg\n\
+ translate\n\
\n\
% Iterate over each of the lines to output it. For each line,\n\
% compute its width again so it can be properly justified, then\n\
@@ -414,7 +440,7 @@
moveto\n\
} forall\n\
grestore\n\
- } {show} ifelse\n\
+ } {show} angle rotate ifelse\n\
0 spacing neg translate\n\
} forall\n\
} bind def\n\
diff -r -u -b -B ../Tk800-orig/pTk/mTk/generic/tkCanvText.c ./pTk/mTk/generic/tkCanvText.c
--- ../Tk800-orig/pTk/mTk/generic/tkCanvText.c Sat Dec 30 11:12:37 2000
+++ ./pTk/mTk/generic/tkCanvText.c Sun Sep 23 02:52:12 2001
@@ -72,12 +72,16 @@
int rightEdge; /* Pixel just to right of right edge of
* area of text item. Used for selecting up
* to end of line. */
+ int insertionx, insertiony; /* Insertion point, which is not necessarily
+ in the corner anymore now that we rotate */
GC gc; /* Graphics context for drawing text. */
GC selTextGC; /* Graphics context for selected text. */
GC cursorOffGC; /* If not None, this gives a graphics context
* to use to draw the insertion cursor when
* it's off. Used if the selection and
* insertion cursor colors are the same. */
+ double angle; /* rotation angle, degrees; positive ccw */
+
} TextItem;

/*
@@ -145,6 +149,8 @@
(char *) NULL, Tk_Offset(Tk_Item, updateCmd), TK_CONFIG_NULL_OK},
{TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL,
"0", Tk_Offset(TextItem, width), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_DOUBLE, "-angle", (char *) NULL, (char *) NULL,
+ 0, Tk_Offset(TextItem, angle), 0},
{TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
(char *) NULL, 0, 0}
};
@@ -302,6 +308,7 @@
textPtr->disabledStipple = None;
textPtr->text = NULL;
textPtr->width = 0;
+ textPtr->angle = 0;

textPtr->numChars = 0;
textPtr->textLayout = NULL;
@@ -322,6 +329,16 @@
return TCL_OK;
}

+ /* If we have a nonzero angle, we must rotate the font accordingly */
+ if (textPtr->angle)
+ {
+ while(textPtr->angle >= 360)
+ textPtr->angle -= 360;
+ while(textPtr->angle < 0)
+ textPtr->angle += 360;
+ textPtr->tkfont=Tk_RotateFont(textPtr->tkfont, textPtr->angle);
+ }
+
error:
DeleteText(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
return TCL_ERROR;
@@ -444,6 +461,15 @@
* graphics contexts.
*/

+ if (textPtr->angle)
+ {
+ while(textPtr->angle >= 360)
+ textPtr->angle -= 360;
+ while(textPtr->angle < 0)
+ textPtr->angle += 360;
+ textPtr->tkfont=Tk_RotateFont(textPtr->tkfont, textPtr->angle);
+ }
+
state = Tk_GetItemState(canvas, itemPtr);

if (textPtr->activeColor != NULL ||
@@ -680,8 +706,47 @@
* of the bounding box for the text item.
*/

+ /*ROTATEDEBUG*/
+/* printf ("angle: %g width: %d height %d\n", textPtr->angle, width, height);
+ */
+
+ if (textPtr->angle == 0)
+ {
+ leftX = (int) (textPtr->x + 0.5);
+ topY = (int) (textPtr->y + 0.5);
+
+ textPtr->insertionx=leftX;
+ textPtr->insertiony=topY;
+ }
+ else
+ /* Now rotate. We rotate around x,y. insertion[xy] just move,
+ * as desired. leftx, lefty, width, and height change to
+ * describe an imaginary box bounding the new rotated shape */
+
+ {
+ double cs=cos(textPtr->angle*M_PI/180);
+ double sn=sin(textPtr->angle*M_PI/180);
+ int cenx=textPtr->x+width/2;
+ int ceny=textPtr->y+height/2;
+ int delix=textPtr->x-cenx;
+ int deliy=-(textPtr->y-ceny); /* positive towards *top* */
+ int origw=width;
+ int origh=height;
+
leftX = (int) (textPtr->x + 0.5);
topY = (int) (textPtr->y + 0.5);
+
+ /* width and height are easy */
+ width=abs(cs*origw)+abs(sn*origh);
+ height=abs(sn*origw)+abs(cs*origh);
+
+ /* this has to adjust to be relative to the new center */
+ textPtr->insertionx=cenx+(delix*cs-deliy*sn);
+ textPtr->insertionx+=(width-origw)/2;
+ textPtr->insertiony=ceny+(-delix*sn-deliy*cs);
+ textPtr->insertiony+=(height-origh)/2;
+
+ }
switch (textPtr->anchor) {
case TK_ANCHOR_NW:
case TK_ANCHOR_N:
@@ -692,12 +757,14 @@
case TK_ANCHOR_CENTER:
case TK_ANCHOR_E:
topY -= height / 2;
+ textPtr->insertiony -= height/2;
break;

case TK_ANCHOR_SW:
case TK_ANCHOR_S:
case TK_ANCHOR_SE:
topY -= height;
+ textPtr->insertiony -= height;
break;
}
switch (textPtr->anchor) {
@@ -710,14 +777,23 @@
case TK_ANCHOR_CENTER:
case TK_ANCHOR_S:
leftX -= width / 2;
+ textPtr->insertionx -= width/2;
break;

case TK_ANCHOR_NE:
case TK_ANCHOR_E:
case TK_ANCHOR_SE:
leftX -= width;
+ textPtr->insertionx -= width;
break;
}
+ /*ROTATEDEBUG */
+/*
+ printf ("%g: %d %d %d %d %d %d\n", textPtr->angle, leftX, topY,
+ width, height, textPtr->insertionx, textPtr->insertiony);
+*/
+
+

textPtr->leftEdge = leftX;
textPtr->rightEdge = leftX + width;
@@ -909,8 +985,11 @@
* as foreground color) than regular text.
*/

- Tk_CanvasDrawableCoords(canvas, (double) textPtr->leftEdge,
+/* Tk_CanvasDrawableCoords(canvas, (double) textPtr->leftEdge,
(double) textPtr->header.y1, &drawableX, &drawableY);
+*/
+ Tk_CanvasDrawableCoords(canvas, (double) textPtr->insertionx,
+ (double) textPtr->insertiony, &drawableX, &drawableY);
Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
drawableX, drawableY, 0, -1);

@@ -1526,9 +1605,9 @@
}

Tk_GetFontMetrics(textPtr->tkfont, &fm);
- sprintf(buffer, "] %d %g %g %s %s DrawText\n",
+ sprintf(buffer, "] %d %g %g %s %s %g DrawText\n",
fm.linespace, x / -2.0, y / 2.0, justify,
- ((stipple == None) ? "false" : "true"));
+ ((stipple == None) ? "false" : "true"), textPtr->angle);
Tcl_AppendResult(interp, buffer, (char *) NULL);

return TCL_OK;
diff -r -u -b -B ../Tk800-orig/pTk/mTk/generic/tkFont.c ./pTk/mTk/generic/tkFont.c
--- ../Tk800-orig/pTk/mTk/generic/tkFont.c Sat Dec 30 11:12:37 2000
+++ ./pTk/mTk/generic/tkFont.c Sun Sep 23 02:26:09 2001
@@ -14,6 +14,7 @@
* RCS: @(#) $Id: tkFont.c,v 1.2 1998/09/14 18:23:10 stanton Exp $
*/

+#include <math.h>
#include "tkInt.h"
#include "tkFont.h"

@@ -818,6 +819,69 @@
/*
*---------------------------------------------------------------------------
*
+ * Tk_RotateFont --
+ *
+ * Given a Tk_Font and an angle, generate a new font which is rotated.
+ * Angle is absolute, not relative to any current rotation.
+ *
+ * Results:
+ * If possible, a new Tk_Font which is rotated is generated.
+ *
+ * Side effects:
+ * Tk_FreeFont() is called on the font passed in.
+ *
+ *---------------------------------------------------------------------------
+ */
+Tk_Font
+Tk_RotateFont(oldfnt, angle)
+ Tk_Font oldfnt;
+ double angle;
+{
+ TkFont *newfont;
+ TkFont *oldfont=(TkFont *)oldfnt;
+ Tcl_HashEntry *cacheHashPtr;
+ char *oldkey, newkey[100];
+ int new;
+ TkFontAttributes fa;
+ Tcl_HashTable *cacheTable =
+ &(((TkWindow *)(oldfont->tkwin))->mainPtr->fontInfoPtr->fontCache);
+
+ oldkey=Tcl_GetHashKey(cacheTable, oldfont->cacheHashPtr);
+
+ /* Generate a new key from the old one and the rotation angle */
+ sprintf(newkey, "%g:", angle);
+ strncat(newkey, oldkey, 100);
+
+ /* Insert the new entry into the hash */
+ cacheHashPtr = Tcl_CreateHashEntry(cacheTable, newkey, &new);
+ if (!new)
+ {
+ newfont=(TkFont *)Tcl_GetHashValue(cacheHashPtr);
+ Tk_FreeFont((Tk_Font)oldfont);
+ return (Tk_Font)newfont;
+ }
+
+ fa=oldfont->fa;
+ fa.angle=angle;
+
+ /* GetFontFromAttributes for Unix has been modified to DTRT with the
+ * angle field. Other platforms will silently fail. */
+ newfont= TkpGetFontFromAttributes(NULL, oldfont->tkwin, &fa);
+
+ Tcl_SetHashValue(cacheHashPtr, newfont);
+ newfont->namedHashPtr=0;
+ newfont->cacheHashPtr=cacheHashPtr;
+ newfont->tkwin=oldfont->tkwin;
+ newfont->refCount=1;
+
+ Tk_FreeFont((Tk_Font)oldfont);
+ return (Tk_Font)newfont;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
* Tk_GetFontFromObj --
*
* Given a string description of a font, map the description to a
@@ -907,6 +971,7 @@
Tcl_SetHashValue(cacheHashPtr, fontPtr);

fontPtr->refCount = 1;
+ fontPtr->tkwin = tkwin;
fontPtr->cacheHashPtr = cacheHashPtr;
fontPtr->namedHashPtr = namedHashPtr;

@@ -1496,7 +1561,10 @@
flags &= ~TK_AT_LEAST_ONE;
if (charsThisChunk > 0) {
chunkPtr = NewChunk(&layoutPtr, &maxChunks, start,
- charsThisChunk, curX, newX, baseline);
+ charsThisChunk,
+ curX+baseline*sin(fontPtr->fa.angle*M_PI/180),
+ newX+baseline*sin(fontPtr->fa.angle*M_PI/180),
+ baseline*cos(fontPtr->fa.angle*M_PI/180));

start += charsThisChunk;
curX = newX;
@@ -1512,8 +1580,11 @@
if (*special == '\t') {
newX = curX + fontPtr->tabWidth;
newX -= newX % fontPtr->tabWidth;
- NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX,
- baseline)->numDisplayChars = -1;
+ NewChunk(&layoutPtr, &maxChunks, start, 1,
+ curX+baseline*sin(fontPtr->fa.angle*M_PI/180),
+ newX+baseline*sin(fontPtr->fa.angle*M_PI/180),
+ baseline*cos(fontPtr->fa.angle*M_PI/180))
+ ->numDisplayChars = -1;
start++;
if ((start < end) &&
((wrapLength <= 0) || (newX <= wrapLength))) {
@@ -2507,6 +2578,7 @@
break;
}
}
+ faPtr->angle=0;
return TCL_OK;
}

@@ -2685,6 +2757,7 @@
result = TkParseXLFD(string, &xa);
if (result == TCL_OK) {
*faPtr = xa.fa;
+ faPtr->angle=0;
return result;
}
}
@@ -2750,6 +2823,8 @@
(char *) NULL);
return TCL_ERROR;
}
+ faPtr->angle=0;
+
return TCL_OK;
}

@@ -2785,6 +2860,8 @@
Tcl_DString ds;

memset(field, '\0', sizeof(field));
+ /* ROTATEDEBUG */
+/* printf("String: %s\n", string); */

str = string;
if (*str == '-') {
@@ -2870,19 +2947,33 @@
* Pointsize in tenths of a point, but treat it as tenths of a pixel.
*/

+ /* Default to 0; set later if necessary */
+ xaPtr->fa.angle=0;
+
if (FieldSpecified(field[XLFD_POINT_SIZE])) {
if (field[XLFD_POINT_SIZE][0] == '[') {
+ double a,b,c,d;
+ int i;
/*
* Some X fonts have the point size specified as follows:
*
* [ N1 N2 N3 N4 ]
*
- * where N1 is the point size (in points, not decipoints!), and
- * N2, N3, and N4 are some additional numbers that I don't know
- * the purpose of, so I ignore them.
+ * These are coordinate transforms, wherein the point size
+ * of the font will be sqrt(N1*N1 + N2*N2). If this is not
+ * equal to sqrt(N3*N3 + N4*N4), the font is stretched, but
+ * we ignore that possibility for now and assume it's not.
+ * See XFLD doc for more information.
*/

- xaPtr->fa.pointsize = atoi(field[XLFD_POINT_SIZE] + 1);
+ /* Undo the translation hack so we can scan */
+ for (i=1; field[XLFD_POINT_SIZE][i] != ']'; i++)
+ if( field[XLFD_POINT_SIZE][i] == '~')
+ field[XLFD_POINT_SIZE][i] = '-';
+ sscanf(field[XLFD_POINT_SIZE]+1, "%lg %lg %lg %lg",
+ &a, &b, &c, &d);
+ xaPtr->fa.pointsize = pow(c*c+d*d, 0.5)+.5;
+ xaPtr->fa.angle = atan2(b,a)/M_PI*180;
} else if (Lang_GetStrInt(NULL, field[XLFD_POINT_SIZE],
&xaPtr->fa.pointsize) == TCL_OK) {
xaPtr->fa.pointsize /= 10;
@@ -2897,24 +2988,29 @@

if (FieldSpecified(field[XLFD_PIXEL_SIZE])) {
if (field[XLFD_PIXEL_SIZE][0] == '[') {
- /*
- * Some X fonts have the pixel size specified as follows:
- *
- * [ N1 N2 N3 N4 ]
- *
- * where N1 is the pixel size, and where N2, N3, and N4
- * are some additional numbers that I don't know
- * the purpose of, so I ignore them.
- */
+ /* See point size for explanation */

- xaPtr->fa.pointsize = atoi(field[XLFD_PIXEL_SIZE] + 1);
+ double a,b,c,d;
+ int i;
+
+ for (i=1; field[XLFD_PIXEL_SIZE][i] != ']'; i++)
+ if( field[XLFD_PIXEL_SIZE][i] == '~')
+ field[XLFD_PIXEL_SIZE][i] = '-';
+ sscanf(field[XLFD_PIXEL_SIZE]+1, "%lg %lg %lg %lg",
+ &a, &b, &c, &d);
+ /* ROTATEDEBUG */
+/* printf ("From %s, pixelsize %d\n", field[XLFD_PIXEL_SIZE],
+ xaPtr->fa.pointsize); */
+
+ xaPtr->fa.pointsize = pow(c*c+d*d, 0.5)+.5;
+ xaPtr->fa.angle = atan2(b,a)/M_PI*180;
} else if (Lang_GetStrInt(NULL, field[XLFD_PIXEL_SIZE],
&xaPtr->fa.pointsize) != TCL_OK) {
return TCL_ERROR;
}
- }

xaPtr->fa.pointsize = -xaPtr->fa.pointsize;
+ }

/* XLFD_RESOLUTION_X ignored. */

diff -r -u -b -B ../Tk800-orig/pTk/mTk/generic/tkFont.h ./pTk/mTk/generic/tkFont.h
--- ../Tk800-orig/pTk/mTk/generic/tkFont.h Sat Dec 30 11:12:37 2000
+++ ./pTk/mTk/generic/tkFont.h Sun Sep 23 01:55:11 2001
@@ -35,6 +35,7 @@
int slant; /* Slant flag; see below for def'n. */
int underline; /* Non-zero for underline font. */
int overstrike; /* Non-zero for overstrike font. */
+ double angle; /* in degrees, ccw positive */
} TkFontAttributes;

/*
@@ -92,6 +93,8 @@
*/

int refCount; /* Number of users of the TkFont. */
+ Tk_Window tkwin; /* needed to fork the font - you cant get
+ from a cachePtr back to the table */
Tcl_HashEntry *cacheHashPtr;/* Entry in font cache for this structure,
* used when deleting it. */
Tcl_HashEntry *namedHashPtr;/* Pointer to hash table entry that
@@ -105,6 +108,9 @@
int underlineHeight; /* Height of underline bar (used for drawing
* underlines on a non-underlined font). */

+ struct TkFont *nonrotated; /* Used to point to the nonrotated version
+ of a rotated font */
+
/*
* Fields in the generic font structure that are filled in by
* platform-specific code.
diff -r -u -b -B ../Tk800-orig/pTk/mTk/unix/tkUnixFont.c ./pTk/mTk/unix/tkUnixFont.c
--- ../Tk800-orig/pTk/mTk/unix/tkUnixFont.c Sat Dec 30 11:12:37 2000
+++ ./pTk/mTk/unix/tkUnixFont.c Sun Sep 23 02:51:20 2001
@@ -11,6 +11,7 @@
*
* RCS: @(#) $Id: tkUnixFont.c,v 1.4 1998/11/25 01:48:54 stanton Exp $
*/
+#include <math.h>

#include "tkPort.h"
#include "tkInt.h"
@@ -337,6 +339,10 @@
} else {
score += (pixelsize - xaPixelsize) * 100;
}
+
+ /* If the font is to be rotated, bitmapped won't do at all. */
+ if (faPtr->angle)
+ score+=5000;
}

score += ABS(xa.fa.weight - faPtr->weight) * 30;
@@ -412,8 +418,43 @@
rest = strchr(rest + 1, '-');
}
*str = '\0';
+
+ /* We fill in rotated fonts using the transformation matrix.
+ * See the XLFD documentation for details. */
+ if (faPtr->angle==0)
sprintf(buf, "%.240s-*-%d-*-*-*-*-*%s", nameList[bestScaleableIdx],
pixelsize, rest);
+ else
+ {
+ char val[4][50];
+ double x,y;
+ int i;
+
+ /* We want to round off to low precision here, so we don't get
+ * 23434e-16 where we want 0 */
+ x=rint(pixelsize*cos(faPtr->angle/180*M_PI)*1000)/1000;
+ y=rint(pixelsize*sin(faPtr->angle/180*M_PI)*1000)/1000;
+
+ sprintf(val[0], "%g",x);
+ sprintf(val[1], "%g",y);
+ sprintf(val[2], "%g",-y);
+ sprintf(val[3], "%g",x);
+
+ /* X uses this horrible hack because the meaning of - is taken */
+ /* We know there is no - in the exponent because the rounding
+ * above guarantees there is no exponent at all */
+ for (i=0; i<4; i++)
+ if (val[i][0]=='-')
+ val[i][0]='~';
+
+ sprintf(buf, "%.240s-*-[%s %s %s %s]-0-*-*-*-*%s",
+ nameList[bestScaleableIdx], val[0], val[1], val[2], val[3],
+ rest);
+ }
+
+ /* ROTATEDEBUG */
+/* printf ("Using scalable font %s\n", buf); */
+
*str = '-';
fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), buf);
bestScaleableScore = INT_MAX;
@@ -771,7 +812,27 @@
0x7fff - x, 0, &length);
}

+ if (!fontPtr->font.fa.angle)
XDrawString(display, drawable, gc, x, y, source, numChars);
+ else
+ {
+ int i;
+ int rx,ry;
+ rx=x;
+ ry=y;
+
+ /* ROTATEDEBUG */
+/* printf ("Drawing at angle %g\n", fontPtr->font.fa.angle); */
+
+ for (i=0; i<numChars; i++)
+ {
+ XDrawString(display, drawable, gc, rx, ry, source+i, 1);
+ rx+=fontPtr->widths[UCHAR(source[i])]*
+ cos(fontPtr->font.fa.angle*M_PI/180);
+ ry-=fontPtr->widths[UCHAR(source[i])]*
+ sin(fontPtr->font.fa.angle*M_PI/180);
+ }
+ }

if (fontPtr->font.fa.underline != 0) {
XFillRectangle(display, drawable, gc, x,
@@ -874,6 +935,12 @@
fontPtr->display = Tk_Display(tkwin);
fontPtr->fontStructPtr = fontStructPtr;

+ if (fontPtr->font.fa.angle)
+ {
+ /* and now we have to kluge the ascent and descent too */
+ fontPtr->font.fm.ascent = fontPtr->font.fa.pointsize;
+ fontPtr->font.fm.descent = fontPtr->font.fa.pointsize/6;
+ }
/*
* Classify the characters.
*/
@@ -902,6 +969,21 @@
} else if (fontStructPtr->per_char == NULL) {
n = fontStructPtr->max_bounds.width;
} else {
+ if (fontPtr->font.fa.angle)
+ {
+ fontPtr->font.fm.fixed = 0;
+
+ /* This is a horrible kluge because I can't find any way to
+ * get the proper width! */
+ if (fontStructPtr->per_char[i - firstChar].width == 0)
+ n=(fontStructPtr->per_char[i-firstChar].ascent+
+ fontStructPtr->per_char[i-firstChar].descent+1);
+// *sin(fontPtr->font.fa.angle/180*M_PI);
+ else
+ n=fontStructPtr->per_char[i - firstChar].width
+ /cos(fontPtr->font.fa.angle/180*M_PI)+.5;
+ }
+ else
n = fontStructPtr->per_char[i - firstChar].width;
}
fontPtr->widths[i] = n;
diff -r -u -b -B ../Tk800-orig/pTk/tk.m ./pTk/tk.m
--- ../Tk800-orig/pTk/tk.m Sat Dec 30 11:12:37 2000
+++ ./pTk/tk.m Sun Sep 23 00:47:51 2001
@@ -658,6 +658,10 @@
# define Tk_RestrictEvents (*TkVptr->V_Tk_RestrictEvents)
#endif

+#ifndef Tk_RotateFont
+# define Tk_RotateFont (*TkVptr->V_Tk_RotateFont)
+#endif
+
#ifndef Tk_SetAppName
# define Tk_SetAppName (*TkVptr->V_Tk_SetAppName)
#endif
diff -r -u -b -B ../Tk800-orig/pTk/tk.t ./pTk/tk.t
--- ../Tk800-orig/pTk/tk.t Sat Dec 30 11:12:37 2000
+++ ./pTk/tk.t Sun Sep 23 00:47:51 2001
@@ -870,6 +870,10 @@
ClientData arg, ClientData *prevArgPtr)))
#endif

+#ifndef Tk_RotateFont
+VFUNC(Tk_Font,Tk_RotateFont,V_Tk_RotateFont,_ANSI_ARGS_((Tk_Font oldfnt, double angle)))
+#endif
+
#ifndef Tk_SetAppName
VFUNC(char *,Tk_SetAppName,V_Tk_SetAppName,_ANSI_ARGS_((Tk_Window tkwin,
char *name)))

Terran Melconian

unread,
Sep 23, 2001, 3:27:15 AM9/23/01
to
In article <9ok2mi$9qg$1...@decurion.mit.edu>,

Terran Melconian <ter...@mit.edu> wrote:
>This is the first release of an experimental patch to add rotating
>text to Perl/Tk canvases. I tried Slaven Rezic's perl-only
>implementation, and it was almost everything I wanted, but I needed
>postscript output, so here's a source patch.

I forgot to say how it works. You give the text the -angle option, in
degrees CCW.
Here's a demo:

#!/usr/bin/perl

#use lib '/usr/local/src/Tk800.023/blib/lib';
#use lib '/usr/local/src/Tk800.023/blib/arch/';

use Tk;
use POSIX;

$mw=new MainWindow;

$canv=$mw->Canvas(-background => "#ffffff", -width => 620, -height => 800);
$canv->pack(-expand=>1, -fill=>"both");

for ($i=0; $i<=360; $i+=30)
{
$canv->createLine(100, $i*2+15, 105, $i*2+15);
$canv->createText(100, $i*2+15, -text=> 'IFOOOO',
anchor => 'e', -angle => $i);
}

for ($i=0; $i<=360; $i+=30)
{
$canv->createLine(120, $i*2+15, 115, $i*2+15);
$canv->createText(120, $i*2+15, -text=> 'IFOOOO',
anchor => 'w', -angle => $i);
}

$canv->postscript(-file => "/tmp/foo.ps", -width => 620, -height => 800);
MainLoop;

Reply all
Reply to author
Forward
0 new messages