1 /* ********************************************************************** */
2 
3 /* xvertext 5.0, Copyright (c) 1993 Alan Richardson (mppa3@uk.ac.sussex.syma)
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation for any purpose and without fee is hereby granted, provided
7  * that the above copyright notice appear in all copies and that both the
8  * copyright notice and this permission notice appear in supporting
9  * documentation.  All work developed as a consequence of the use of
10  * this program should duly acknowledge such use. No representations are
11  * made about the suitability of this software for any purpose.  It is
12  * provided "as is" without express or implied warranty.
13  */
14 
15 /* ********************************************************************** */
16 
17 
18 /* BETTER: xvertext now does rotation at any angle!!
19  *
20  * BEWARE: function arguments have CHANGED since version 2.0!!
21  */
22 
23 /* ********************************************************************** */
24 
25 
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <X11/Xatom.h>
29 #include <stdio.h>
30 #include <math.h>
31 #include <string.h>
32 #include "rotated.h"
33 
34 
35 /* ---------------------------------------------------------------------- */
36 
37 /* FJ 4/96 */
38 #undef NULL             /* in case <stdio.h> has defined it. */
39 #define     NULL    0
40 /* Fin FJ 4/96 */
41 
42 /* Make sure cache size is set */
43 
44 #ifndef CACHE_SIZE_LIMIT
45 #define CACHE_SIZE_LIMIT 0
46 #endif /*CACHE_SIZE_LIMIT */
47 
48 /* Make sure a cache method is specified */
49 
50 #ifndef CACHE_XIMAGES
51 #ifndef CACHE_BITMAPS
52 #define CACHE_BITMAPS
53 #endif /*CACHE_BITMAPS*/
54 #endif /*CACHE_XIMAGES*/
55 
56 
57 /* ---------------------------------------------------------------------- */
58 
59 
60 /* Debugging macros */
61 
62 #ifdef DEBUG
63 static int debug=1;
64 #else
65 static int debug=0;
66 #endif /*DEBUG*/
67 
68 #define DEBUG_PRINT1(a) if (debug) printf (a)
69 #define DEBUG_PRINT2(a, b) if (debug) printf (a, b)
70 #define DEBUG_PRINT3(a, b, c) if (debug) printf (a, b, c)
71 #define DEBUG_PRINT4(a, b, c, d) if (debug) printf (a, b, c, d)
72 #define DEBUG_PRINT5(a, b, c, d, e) if (debug) printf (a, b, c, d, e)
73 
74 
75 /* ---------------------------------------------------------------------- */
76 
77 
78 #ifndef M_PI
79 #define M_PI 3.14159265358979323846
80 #endif
81 
82 
83 /* ---------------------------------------------------------------------- */
84 
85 
86 /* A structure holding everything needed for a rotated string */
87 
88 typedef struct rotated_text_item_template {
89     Pixmap bitmap;
90     XImage *ximage;
91 
92     char *text;
93     char *font_name;
94     Font fid;
95     float angle;
96     int align;
97     float magnify;
98 
99     int cols_in;
100     int rows_in;
101     int cols_out;
102     int rows_out;
103 
104     int nl;
105     int max_width;
106     float *corners_x;
107     float *corners_y;
108 
109     long int size;
110     int cached;
111 
112     struct rotated_text_item_template *next;
113 } RotatedTextItem;
114 
115 RotatedTextItem *first_text_item=NULL;
116 
117 
118 /* ---------------------------------------------------------------------- */
119 
120 
121 /* A structure holding current magnification and bounding box padding */
122 
123 static struct style_template {
124     float magnify;
125     int bbx_pad;
126 } style={
127     1.,
128     0
129     };
130 
131 
132 /* ---------------------------------------------------------------------- */
133 
134 
135 static char            *my_strdup();
136 static char            *my_strtok();
137 
138 float                   XRotVersion();
139 void                    XRotSetMagnification();
140 void                    XRotSetBoundingBoxPad();
141 int                     XRotDrawString();
142 int                     XRotDrawImageString();
143 int                     XRotDrawAlignedString();
144 int                     XRotDrawAlignedImageString();
145 XPoint                 *XRotTextExtents();
146 
147 static XImage          *MakeXImage();
148 static int              XRotPaintAlignedString();
149 static int              XRotDrawHorizontalString();
150 static RotatedTextItem *XRotRetrieveFromCache();
151 static RotatedTextItem *XRotCreateTextItem();
152 static void             XRotAddToLinkedList();
153 static void             XRotFreeTextItem();
154 static XImage          *XRotMagnifyImage();
155 
156 
157 /* ---------------------------------------------------------------------- */
158 
159 
160 /**************************************************************************/
161 /* Routine to mimic `strdup()' (some machines don't have it)              */
162 /**************************************************************************/
163 
my_strdup(str)164 static char *my_strdup(str)
165     char *str;
166 {
167     char *s;
168 
169     if(str==NULL)
170 	return NULL;
171 
172     s=(char *)malloc((unsigned)(strlen(str)+1));
173     if(s!=NULL)
174 	strcpy(s, str);
175 
176     return s;
177 }
178 
179 
180 /* ---------------------------------------------------------------------- */
181 
182 
183 /**************************************************************************/
184 /* Routine to replace `strtok' : this one returns a zero length string if */
185 /* it encounters two consecutive delimiters                               */
186 /**************************************************************************/
187 
my_strtok(str1,str2)188 static char *my_strtok(str1, str2)
189     char *str1, *str2;
190 {
191     char *ret;
192     int i, j, stop;
193     static int start, len;
194     static char *stext;
195 
196     if(str2==NULL)
197 	return NULL;
198 
199     /* initialise if str1 not NULL */
200     if(str1!=NULL) {
201 	start=0;
202 	stext=str1;
203 	len=strlen(str1);
204     }
205 
206     /* run out of tokens ? */
207     if(start>=len)
208 	return NULL;
209 
210     /* loop through characters */
211     for(i=start; i<len; i++) {
212 	/* loop through delimiters */
213 	stop=0;
214 	for(j=0; j<strlen(str2); j++)
215 	    if(stext[i]==str2[j])
216 		stop=1;
217 
218 	if(stop)
219 	    break;
220     }
221 
222     stext[i]='\0';
223 
224     ret=stext+start;
225 
226     start=i+1;
227 
228     return ret;
229 }
230 
231 
232 /* ---------------------------------------------------------------------- */
233 
234 
235 /**************************************************************************/
236 /* Return version/copyright information                                   */
237 /**************************************************************************/
238 
XRotVersion(str,n)239 float XRotVersion(str, n)
240     char *str;
241     int n;
242 {
243     if(str!=NULL)
244 	strncpy(str, XV_COPYRIGHT, n);
245     return XV_VERSION;
246 }
247 
248 
249 /* ---------------------------------------------------------------------- */
250 
251 
252 /**************************************************************************/
253 /* Set the font magnification factor for all subsequent operations        */
254 /**************************************************************************/
255 
XRotSetMagnification(m)256 void XRotSetMagnification(m)
257     float m;
258 {
259     if(m>0.)
260 	style.magnify=m;
261 }
262 
263 
264 /* ---------------------------------------------------------------------- */
265 
266 
267 /**************************************************************************/
268 /* Set the padding used when calculating bounding boxes                   */
269 /**************************************************************************/
270 
XRotSetBoundingBoxPad(p)271 void XRotSetBoundingBoxPad(p)
272     int p;
273 {
274     if(p>=0)
275 	style.bbx_pad=p;
276 }
277 
278 
279 /* ---------------------------------------------------------------------- */
280 
281 
282 /**************************************************************************/
283 /*  Create an XImage structure and allocate memory for it                 */
284 /**************************************************************************/
285 
MakeXImage(dpy,w,h)286 static XImage *MakeXImage(dpy, w, h)
287     Display *dpy;
288     int w, h;
289 {
290     XImage *I;
291     char *data;
292 
293     /* reserve memory for image */
294     data=(char *)calloc((unsigned)(((w-1)/8+1)*h), 1);
295     if(data==NULL)
296 	return NULL;
297 
298     /* create the XImage */
299     I=XCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 1, XYBitmap,
300                    0, data, w, h, 8, 0);
301     if(I==NULL)
302 	return NULL;
303 
304     I->byte_order=I->bitmap_bit_order=MSBFirst;
305     return I;
306 }
307 
308 
309 /* ---------------------------------------------------------------------- */
310 
311 
312 /**************************************************************************/
313 /*  A front end to XRotPaintAlignedString:                                */
314 /*      -no alignment, no background                                      */
315 /**************************************************************************/
316 
XRotDrawString(dpy,font,angle,drawable,gc,x,y,str)317 int XRotDrawString(dpy, font, angle, drawable, gc, x, y, str)
318     Display *dpy;
319     XFontStruct *font;
320     float angle;
321     Drawable drawable;
322     GC gc;
323     int x, y;
324     char *str;
325 {
326     return (XRotPaintAlignedString(dpy, font, angle, drawable, gc,
327 				   x, y, str, NONE, 0));
328 }
329 
330 
331 /* ---------------------------------------------------------------------- */
332 
333 
334 /**************************************************************************/
335 /*  A front end to XRotPaintAlignedString:                                */
336 /*      -no alignment, paints background                                  */
337 /**************************************************************************/
338 
XRotDrawImageString(dpy,font,angle,drawable,gc,x,y,str)339 int XRotDrawImageString(dpy, font, angle, drawable, gc, x, y, str)
340     Display *dpy;
341     XFontStruct *font;
342     float angle;
343     Drawable drawable;
344     GC gc;
345     int x, y;
346     char *str;
347 {
348     return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
349 				  x, y, str, NONE, 1));
350 }
351 
352 
353 /* ---------------------------------------------------------------------- */
354 
355 
356 /**************************************************************************/
357 /*  A front end to XRotPaintAlignedString:                                */
358 /*      -does alignment, no background                                    */
359 /**************************************************************************/
360 
XRotDrawAlignedString(dpy,font,angle,drawable,gc,x,y,text,align)361 int XRotDrawAlignedString(dpy, font, angle, drawable, gc, x, y, text, align)
362     Display *dpy;
363     XFontStruct *font;
364     float angle;
365     Drawable drawable;
366     GC gc;
367     int x, y;
368     char *text;
369     int align;
370 {
371     return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
372 				  x, y, text, align, 0));
373 }
374 
375 
376 /* ---------------------------------------------------------------------- */
377 
378 
379 /**************************************************************************/
380 /*  A front end to XRotPaintAlignedString:                                */
381 /*      -does alignment, paints background                                */
382 /**************************************************************************/
383 
XRotDrawAlignedImageString(dpy,font,angle,drawable,gc,x,y,text,align)384 int XRotDrawAlignedImageString(dpy, font, angle, drawable, gc, x, y, text,
385 			       align)
386     Display *dpy;
387     XFontStruct *font;
388     float angle;
389     Drawable drawable;
390     GC gc;
391     int x, y;
392     char *text;
393     int align;
394 {
395     return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
396 				  x, y, text, align, 1));
397 }
398 
399 
400 /* ---------------------------------------------------------------------- */
401 
402 
403 /**************************************************************************/
404 /*  Aligns and paints a rotated string                                    */
405 /**************************************************************************/
406 
XRotPaintAlignedString(dpy,font,angle,drawable,gc,x,y,text,align,bg)407 static int XRotPaintAlignedString(dpy, font, angle, drawable, gc, x, y, text,
408 				  align, bg)
409     Display *dpy;
410     XFontStruct *font;
411     float angle;
412     Drawable drawable;
413     GC gc;
414     int x, y;
415     char *text;
416     int align;
417     int bg;
418 {
419     int i;
420     GC my_gc;
421     int xp, yp;
422     float hot_x, hot_y;
423     float hot_xp, hot_yp;
424     float sin_angle, cos_angle;
425     RotatedTextItem *item;
426     Pixmap bitmap_to_paint;
427 
428     /* return early for NULL/empty strings */
429     if(text==NULL)
430         return 0;
431 
432     if(strlen(text)==0)
433 	return 0;
434 
435     /* manipulate angle to 0<=angle<360 degrees */
436     while(angle<0)
437         angle+=360;
438 
439     while(angle>=360)
440         angle-=360;
441 
442     angle*=M_PI/180;
443 
444     /* horizontal text made easy */
445     if(angle==0. && style.magnify==1.)
446 	return(XRotDrawHorizontalString(dpy, font, drawable, gc, x, y,
447 					text, align, bg));
448 
449     /* get a rotated bitmap */
450     item=XRotRetrieveFromCache(dpy, font, angle, text, align);
451     if(item==NULL)
452 	return NULL;
453 
454     /* this gc has similar properties to the user's gc */
455     my_gc=XCreateGC(dpy, drawable, NULL, 0);
456     XCopyGC(dpy, gc, GCForeground|GCBackground|GCFunction|GCPlaneMask,
457 	    my_gc);
458 
459     /* alignment : which point (hot_x, hot_y) relative to bitmap centre
460        coincides with user's specified point? */
461 
462     /* y position */
463     if(align==TLEFT || align==TCENTRE || align==TRIGHT)
464         hot_y=(float)item->rows_in/2*style.magnify;
465     else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
466 	hot_y=0;
467     else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
468 	hot_y=-(float)item->rows_in/2*style.magnify;
469     else
470 	hot_y=-((float)item->rows_in/2-(float)font->descent)*style.magnify;
471 
472     /* x position */
473     if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
474 	hot_x=-(float)item->max_width/2*style.magnify;
475     else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
476 	hot_x=0;
477     else
478         hot_x=(float)item->max_width/2*style.magnify;
479 
480     /* pre-calculate sin and cos */
481     sin_angle=sin(angle);
482     cos_angle=cos(angle);
483 
484     /* rotate hot_x and hot_y around bitmap centre */
485     hot_xp= hot_x*cos_angle - hot_y*sin_angle;
486     hot_yp= hot_x*sin_angle + hot_y*cos_angle;
487 
488     /* text background will be drawn using XFillPolygon */
489     if(bg) {
490 	GC depth_one_gc;
491 	XPoint *xpoints;
492 	Pixmap empty_stipple;
493 
494 	/* reserve space for XPoints */
495 	xpoints=(XPoint *)malloc((unsigned)(4*item->nl*sizeof(XPoint)));
496 	if(!xpoints)
497 	    return 1;
498 
499 	/* rotate corner positions */
500 	for(i=0; i<4*item->nl; i++) {
501 	    xpoints[i].x=(float)x + ( (item->corners_x[i]-hot_x)*cos_angle +
502 				      (item->corners_y[i]+hot_y)*sin_angle);
503 	    xpoints[i].y=(float)y + (-(item->corners_x[i]-hot_x)*sin_angle +
504 				      (item->corners_y[i]+hot_y)*cos_angle);
505 	}
506 
507 	/* we want to swap foreground and background colors here;
508 	   XGetGCValues() is only available in R4+ */
509 
510 	empty_stipple=XCreatePixmap(dpy, drawable, 1, 1, 1);
511 
512 	depth_one_gc=XCreateGC(dpy, empty_stipple, NULL, 0);
513 	XSetForeground(dpy, depth_one_gc, 0);
514 	XFillRectangle(dpy, empty_stipple, depth_one_gc, 0, 0, 2, 2);
515 
516 	XSetStipple(dpy, my_gc, empty_stipple);
517 	XSetFillStyle(dpy, my_gc, FillOpaqueStippled);
518 
519 	XFillPolygon(dpy, drawable, my_gc, xpoints, 4*item->nl, Nonconvex,
520 		     CoordModeOrigin);
521 
522 	/* free our resources */
523 	free((char *)xpoints);
524 	XFreeGC(dpy, depth_one_gc);
525 	XFreePixmap(dpy, empty_stipple);
526     }
527 
528     /* where should top left corner of bitmap go ? */
529     xp=(float)x-((float)item->cols_out/2 +hot_xp);
530     yp=(float)y-((float)item->rows_out/2 -hot_yp);
531 
532     /* by default we draw the rotated bitmap, solid */
533     bitmap_to_paint=item->bitmap;
534 
535     /* handle user stippling */
536 #ifndef X11R3
537     {
538 	GC depth_one_gc;
539 	XGCValues values;
540 	Pixmap new_bitmap, inverse;
541 
542 	/* try and get some GC properties */
543 	if(XGetGCValues(dpy, gc,
544 			GCStipple|GCFillStyle|GCForeground|GCBackground|
545 			GCTileStipXOrigin|GCTileStipYOrigin,
546 			&values)) {
547 
548 	    /* only do this if stippling requested */
549 	    if((values.fill_style==FillStippled ||
550 		values.fill_style==FillOpaqueStippled) && !bg) {
551 
552 		/* opaque stipple: draw rotated text in background colour */
553 		if(values.fill_style==FillOpaqueStippled) {
554 		    XSetForeground(dpy, my_gc, values.background);
555 		    XSetFillStyle(dpy, my_gc, FillStippled);
556 		    XSetStipple(dpy, my_gc, item->bitmap);
557 		    XSetTSOrigin(dpy, my_gc, xp, yp);
558 		    XFillRectangle(dpy, drawable, my_gc, xp, yp,
559 				   item->cols_out, item->rows_out);
560 		    XSetForeground(dpy, my_gc, values.foreground);
561 		}
562 
563 		/* this will merge the rotated text and the user's stipple */
564 		new_bitmap=XCreatePixmap(dpy, drawable,
565 					 item->cols_out, item->rows_out, 1);
566 
567 		/* create a GC */
568 		depth_one_gc=XCreateGC(dpy, new_bitmap, NULL, 0);
569 		XSetForeground(dpy, depth_one_gc, 1);
570 		XSetBackground(dpy, depth_one_gc, 0);
571 
572 		/* set the relative stipple origin */
573 		XSetTSOrigin(dpy, depth_one_gc,
574 			     values.ts_x_origin-xp, values.ts_y_origin-yp);
575 
576 		/* fill the whole bitmap with the user's stipple */
577 		XSetStipple(dpy, depth_one_gc, values.stipple);
578 		XSetFillStyle(dpy, depth_one_gc, FillOpaqueStippled);
579 		XFillRectangle(dpy, new_bitmap, depth_one_gc,
580 			       0, 0, item->cols_out, item->rows_out);
581 
582 		/* set stipple origin back to normal */
583 		XSetTSOrigin(dpy, depth_one_gc, 0, 0);
584 
585 		/* this will contain an inverse copy of the rotated text */
586 		inverse=XCreatePixmap(dpy, drawable,
587 				      item->cols_out, item->rows_out, 1);
588 
589 		/* invert text */
590 		XSetFillStyle(dpy, depth_one_gc, FillSolid);
591 		XSetFunction(dpy, depth_one_gc, GXcopyInverted);
592 		XCopyArea(dpy, item->bitmap, inverse, depth_one_gc,
593 			  0, 0, item->cols_out, item->rows_out, 0, 0);
594 
595 		/* now delete user's stipple everywhere EXCEPT on text */
596                 XSetForeground(dpy, depth_one_gc, 0);
597                 XSetBackground(dpy, depth_one_gc, 1);
598 		XSetStipple(dpy, depth_one_gc, inverse);
599 		XSetFillStyle(dpy, depth_one_gc, FillStippled);
600 		XSetFunction(dpy, depth_one_gc, GXcopy);
601 		XFillRectangle(dpy, new_bitmap, depth_one_gc,
602                                0, 0, item->cols_out, item->rows_out);
603 
604 		/* free resources */
605 		XFreePixmap(dpy, inverse);
606 		XFreeGC(dpy, depth_one_gc);
607 
608 		/* this is the new bitmap */
609 		bitmap_to_paint=new_bitmap;
610 	    }
611 	}
612     }
613 #endif /*X11R3*/
614 
615     /* paint text using stipple technique */
616     XSetFillStyle(dpy, my_gc, FillStippled);
617     XSetStipple(dpy, my_gc, bitmap_to_paint);
618     XSetTSOrigin(dpy, my_gc, xp, yp);
619     XFillRectangle(dpy, drawable, my_gc, xp, yp,
620 		   item->cols_out, item->rows_out);
621 
622     /* free our resources */
623     XFreeGC(dpy, my_gc);
624 
625     /* stippled bitmap no longer needed */
626     if(bitmap_to_paint!=item->bitmap)
627 	XFreePixmap(dpy, bitmap_to_paint);
628 
629 #ifdef CACHE_XIMAGES
630     XFreePixmap(dpy, item->bitmap);
631 #endif /*CACHE_XIMAGES*/
632 
633     /* if item isn't cached, destroy it completely */
634     if(!item->cached)
635 	XRotFreeTextItem(dpy,item);
636 
637     /* we got to the end OK! */
638     return 0;
639 }
640 
641 
642 /* ---------------------------------------------------------------------- */
643 
644 
645 /**************************************************************************/
646 /*  Draw a horizontal string in a quick fashion                           */
647 /**************************************************************************/
648 
XRotDrawHorizontalString(dpy,font,drawable,gc,x,y,text,align,bg)649 static int XRotDrawHorizontalString(dpy, font, drawable, gc, x, y, text,
650 			     align, bg)
651     Display *dpy;
652     XFontStruct *font;
653     Drawable drawable;
654     GC gc;
655     int x, y;
656     char *text;
657     int align;
658     int bg;
659 {
660     GC my_gc;
661     int nl=1, i;
662     int height;
663     int xp, yp;
664     char *str1, *str2, *str3;
665     char *str2_a="\0", *str2_b="\n\0";
666     int dir, asc, desc;
667     XCharStruct overall;
668 
669     DEBUG_PRINT1("**\nHorizontal text.\n");
670 
671     /* this gc has similar properties to the user's gc (including stipple) */
672     my_gc=XCreateGC(dpy, drawable, NULL, 0);
673     XCopyGC(dpy, gc,
674 	    GCForeground|GCBackground|GCFunction|GCStipple|GCFillStyle|
675 	    GCTileStipXOrigin|GCTileStipYOrigin|GCPlaneMask, my_gc);
676     XSetFont(dpy, my_gc, font->fid);
677 
678     /* count number of sections in string */
679     if(align!=NONE)
680 	for(i=0; i<strlen(text)-1; i++)
681 	    if(text[i]=='\n')
682 		nl++;
683 
684     /* ignore newline characters if not doing alignment */
685     if(align==NONE)
686 	str2=str2_a;
687     else
688 	str2=str2_b;
689 
690     /* overall font height */
691     height=font->ascent+font->descent;
692 
693     /* y position */
694     if(align==TLEFT || align==TCENTRE || align==TRIGHT)
695 	yp=y+font->ascent;
696     else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
697 	yp=y-nl*height/2+font->ascent;
698     else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
699 	yp=y-nl*height+font->ascent;
700     else
701 	yp=y;
702 
703     str1=my_strdup(text);
704     if(str1==NULL)
705 	return 1;
706 
707     str3=my_strtok(str1, str2);
708 
709     /* loop through each section in the string */
710     do {
711         XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
712                      &overall);
713 
714 	/* where to draw section in x ? */
715 	if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
716 	    xp=x;
717 	else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
718 	    xp=x-overall.rbearing/2;
719 	else
720 	    xp=x-overall.rbearing;
721 
722 	/* draw string onto bitmap */
723 	if(!bg)
724 	    XDrawString(dpy, drawable, my_gc, xp, yp, str3, strlen(str3));
725 	else
726 	    XDrawImageString(dpy, drawable, my_gc, xp, yp, str3, strlen(str3));
727 
728 	/* move to next line */
729 	yp+=height;
730 
731 	str3=my_strtok((char *)NULL, str2);
732     }
733     while(str3!=NULL);
734 
735     free(str1);
736     XFreeGC(dpy, my_gc);
737 
738     return 0;
739 }
740 
741 
742 /* ---------------------------------------------------------------------- */
743 
744 
745 /**************************************************************************/
746 /*   Query cache for a match with this font/text/angle/alignment          */
747 /*       request, otherwise arrange for its creation                      */
748 /**************************************************************************/
749 
XRotRetrieveFromCache(dpy,font,angle,text,align)750 static RotatedTextItem *XRotRetrieveFromCache(dpy, font, angle, text, align)
751     Display *dpy;
752     XFontStruct *font;
753     float angle;
754     char *text;
755     int align;
756 {
757     Font fid;
758     char *font_name=NULL;
759     unsigned long name_value;
760     RotatedTextItem *item=NULL;
761     RotatedTextItem *i1=first_text_item;
762 
763     /* get font name, if it exists */
764     if(XGetFontProperty(font, XA_FONT, &name_value)) {
765 	DEBUG_PRINT1("got font name OK\n");
766 	font_name=XGetAtomName(dpy, name_value);
767 	fid=0;
768     }
769 #ifdef CACHE_FID
770     /* otherwise rely (unreliably?) on font ID */
771     else {
772 	DEBUG_PRINT1("can't get fontname, caching FID\n");
773 	font_name=NULL;
774 	fid=font->fid;
775     }
776 #else
777     /* not allowed to cache font ID's */
778     else {
779 	DEBUG_PRINT1("can't get fontname, can't cache\n");
780 	font_name=NULL;
781 	fid=0;
782     }
783 #endif /*CACHE_FID*/
784 
785     /* look for a match in cache */
786 
787     /* matching formula:
788        identical text;
789        identical fontname (if defined, font ID's if not);
790        angles close enough (<0.00001 here, could be smaller);
791        HORIZONTAL alignment matches, OR it's a one line string;
792        magnifications the same */
793 
794     while(i1 && !item) {
795 	/* match everything EXCEPT fontname/ID */
796 	if(strcmp(text, i1->text)==0 &&
797 	   fabs(angle-i1->angle)<0.00001 &&
798 	   style.magnify==i1->magnify &&
799 	   (i1->nl==1 ||
800 	    ((align==0)?9:(align-1))%3==
801 	      ((i1->align==0)?9:(i1->align-1))%3)) {
802 
803 	    /* now match fontname/ID */
804 	    if(font_name!=NULL && i1->font_name!=NULL) {
805 		if(strcmp(font_name, i1->font_name)==0) {
806 		    item=i1;
807 		    DEBUG_PRINT1("Matched against font names\n");
808 		}
809 		else
810 		    i1=i1->next;
811 	    }
812 #ifdef CACHE_FID
813 	    else if(font_name==NULL && i1->font_name==NULL) {
814 		if(fid==i1->fid) {
815 		    item=i1;
816 		    DEBUG_PRINT1("Matched against FID's\n");
817                 }
818 		else
819                     i1=i1->next;
820 	    }
821 #endif /*CACHE_FID*/
822 	    else
823 		i1=i1->next;
824 	}
825 	else
826 	    i1=i1->next;
827     }
828 
829     if(item)
830 	DEBUG_PRINT1("**\nFound target in cache.\n");
831     if(!item)
832 	DEBUG_PRINT1("**\nNo match in cache.\n");
833 
834     /* no match */
835     if(!item) {
836 	/* create new item */
837 	item=XRotCreateTextItem(dpy, font, angle, text, align);
838 	if(!item)
839 	    return NULL;
840 
841 	/* record what it shows */
842 	item->text=my_strdup(text);
843 
844 	/* fontname or ID */
845 	if(font_name!=NULL) {
846 	    item->font_name=my_strdup(font_name);
847 	    item->fid=0;
848 	}
849 	else {
850 	    item->font_name=NULL;
851 	    item->fid=fid;
852 	}
853 
854 	item->angle=angle;
855 	item->align=align;
856 	item->magnify=style.magnify;
857 
858 	/* cache it */
859 	XRotAddToLinkedList(dpy, item);
860     }
861 
862     if(font_name)
863 	XFree(font_name);
864 
865     /* if XImage is cached, need to recreate the bitmap */
866 
867 #ifdef CACHE_XIMAGES
868     {
869 	GC depth_one_gc;
870 
871 	/* create bitmap to hold rotated text */
872 	item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
873 				   item->cols_out, item->rows_out, 1);
874 
875 	/* depth one gc */
876 	depth_one_gc=XCreateGC(dpy, item->bitmap, NULL, 0);
877 	XSetBackground(dpy, depth_one_gc, 0);
878 	XSetForeground(dpy, depth_one_gc, 1);
879 
880 	/* make the text bitmap from XImage */
881 	XPutImage(dpy, item->bitmap, depth_one_gc, item->ximage, 0, 0, 0, 0,
882 		  item->cols_out, item->rows_out);
883 
884 	XFreeGC(dpy, depth_one_gc);
885     }
886 #endif /*CACHE_XIMAGES*/
887 
888     return item;
889 }
890 
891 
892 /* ---------------------------------------------------------------------- */
893 
894 
895 /**************************************************************************/
896 /*  Create a rotated text item                                            */
897 /**************************************************************************/
898 
XRotCreateTextItem(dpy,font,angle,text,align)899 static RotatedTextItem *XRotCreateTextItem(dpy, font, angle, text, align)
900     Display *dpy;
901     XFontStruct *font;
902     float angle;
903     char *text;
904     int align;
905 {
906     RotatedTextItem *item=NULL;
907     Pixmap canvas;
908     GC font_gc;
909     XImage *I_in;
910     register int i, j;
911     char *str1, *str2, *str3;
912     char *str2_a="\0", *str2_b="\n\0";
913     int height;
914     int byte_w_in, byte_w_out;
915     int xp, yp;
916     float sin_angle, cos_angle;
917     int it, jt;
918     float di, dj;
919     int ic=0;
920     float xl, xr, xinc;
921     int byte_out;
922     int dir, asc, desc;
923     XCharStruct overall;
924     int old_cols_in=0, old_rows_in=0;
925 
926     /* allocate memory */
927     item=(RotatedTextItem *)malloc((unsigned)sizeof(RotatedTextItem));
928     if(!item)
929 	return NULL;
930 
931     /* count number of sections in string */
932     item->nl=1;
933     if(align!=NONE)
934 	for(i=0; i<strlen(text)-1; i++)
935 	    if(text[i]=='\n')
936 		item->nl++;
937 
938     /* ignore newline characters if not doing alignment */
939     if(align==NONE)
940 	str2=str2_a;
941     else
942 	str2=str2_b;
943 
944     /* find width of longest section */
945     str1=my_strdup(text);
946     if(str1==NULL)
947 	return NULL;
948 
949     str3=my_strtok(str1, str2);
950 
951     XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
952 		 &overall);
953 
954     item->max_width=overall.rbearing;
955 
956     /* loop through each section */
957     do {
958 	str3=my_strtok((char *)NULL, str2);
959 
960 	if(str3!=NULL) {
961 	    XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
962 			 &overall);
963 
964 	    if(overall.rbearing>item->max_width)
965 		item->max_width=overall.rbearing;
966 	}
967     }
968     while(str3!=NULL);
969 
970     free(str1);
971 
972     /* overall font height */
973     height=font->ascent+font->descent;
974 
975     /* dimensions horizontal text will have */
976     item->cols_in=item->max_width;
977     item->rows_in=item->nl*height;
978 
979     /* bitmap for drawing on */
980     canvas=XCreatePixmap(dpy, DefaultRootWindow(dpy),
981 			 item->cols_in, item->rows_in, 1);
982 
983     /* create a GC for the bitmap */
984     font_gc=XCreateGC(dpy, canvas, NULL, 0);
985     XSetBackground(dpy, font_gc, 0);
986     XSetFont(dpy, font_gc, font->fid);
987 
988     /* make sure the bitmap is blank */
989     XSetForeground(dpy, font_gc, 0);
990     XFillRectangle(dpy, canvas, font_gc, 0, 0,
991 		   item->cols_in+1, item->rows_in+1);
992     XSetForeground(dpy, font_gc, 1);
993 
994     /* pre-calculate sin and cos */
995     sin_angle=sin(angle);
996     cos_angle=cos(angle);
997 
998     /* text background will be drawn using XFillPolygon */
999     item->corners_x=
1000 	(float *)malloc((unsigned)(4*item->nl*sizeof(float)));
1001     if(!item->corners_x)
1002 	return NULL;
1003 
1004     item->corners_y=
1005 	(float *)malloc((unsigned)(4*item->nl*sizeof(float)));
1006     if(!item->corners_y)
1007 	return NULL;
1008 
1009     /* draw text horizontally */
1010 
1011     /* start at top of bitmap */
1012     yp=font->ascent;
1013 
1014     str1=my_strdup(text);
1015     if(str1==NULL)
1016 	return NULL;
1017 
1018     str3=my_strtok(str1, str2);
1019 
1020     /* loop through each section in the string */
1021     do {
1022 	XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
1023 		&overall);
1024 
1025 	/* where to draw section in x ? */
1026 	if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
1027 	    xp=0;
1028 	else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
1029 	    xp=(item->max_width-overall.rbearing)/2;
1030 	else
1031             xp=item->max_width-overall.rbearing;
1032 
1033 	/* draw string onto bitmap */
1034 	XDrawString(dpy, canvas, font_gc, xp, yp, str3, strlen(str3));
1035 
1036 	/* keep a note of corner positions of this string */
1037 	item->corners_x[ic]=((float)xp-(float)item->cols_in/2)*style.magnify;
1038 	item->corners_y[ic]=((float)(yp-font->ascent)-(float)item->rows_in/2)
1039 	    *style.magnify;
1040 	item->corners_x[ic+1]=item->corners_x[ic];
1041 	item->corners_y[ic+1]=item->corners_y[ic]+(float)height*style.magnify;
1042 	item->corners_x[item->nl*4-1-ic]=item->corners_x[ic]+
1043 	    (float)overall.rbearing*style.magnify;
1044 	item->corners_y[item->nl*4-1-ic]=item->corners_y[ic];
1045 	item->corners_x[item->nl*4-2-ic]=
1046 	    item->corners_x[item->nl*4-1-ic];
1047 	item->corners_y[item->nl*4-2-ic]=item->corners_y[ic+1];
1048 
1049 	ic+=2;
1050 
1051 	/* move to next line */
1052 	yp+=height;
1053 
1054 	str3=my_strtok((char *)NULL, str2);
1055     }
1056     while(str3!=NULL);
1057 
1058     free(str1);
1059 
1060     /* create image to hold horizontal text */
1061     I_in=MakeXImage(dpy, item->cols_in, item->rows_in);
1062     if(I_in==NULL)
1063 	return NULL;
1064 
1065     /* extract horizontal text */
1066     XGetSubImage(dpy, canvas, 0, 0, item->cols_in, item->rows_in,
1067 		 1, XYPixmap, I_in, 0, 0);
1068     I_in->format=XYBitmap;
1069 
1070     /* magnify horizontal text */
1071     if(style.magnify!=1.) {
1072 	I_in=XRotMagnifyImage(dpy, I_in);
1073 
1074 	old_cols_in=item->cols_in;
1075 	old_rows_in=item->rows_in;
1076 	item->cols_in=(float)item->cols_in*style.magnify;
1077 	item->rows_in=(float)item->rows_in*style.magnify;
1078     }
1079 
1080     /* how big will rotated text be ? */
1081     item->cols_out=fabs((float)item->rows_in*sin_angle) +
1082 	fabs((float)item->cols_in*cos_angle) +0.99999 +2;
1083 
1084     item->rows_out=fabs((float)item->rows_in*cos_angle) +
1085 	fabs((float)item->cols_in*sin_angle) +0.99999 +2;
1086 
1087     if(item->cols_out%2==0)
1088 	item->cols_out++;
1089 
1090     if(item->rows_out%2==0)
1091 	item->rows_out++;
1092 
1093     /* create image to hold rotated text */
1094     item->ximage=MakeXImage(dpy, item->cols_out, item->rows_out);
1095     if(item->ximage==NULL)
1096 	return NULL;
1097 
1098     byte_w_in=(item->cols_in-1)/8+1;
1099     byte_w_out=(item->cols_out-1)/8+1;
1100 
1101     /* we try to make this bit as fast as possible - which is why it looks
1102        a bit over-the-top */
1103 
1104     /* vertical distance from centre */
1105     dj=0.5-(float)item->rows_out/2;
1106 
1107     /* where abouts does text actually lie in rotated image? */
1108     if(angle==0 || angle==M_PI/2 ||
1109        angle==M_PI || angle==3*M_PI/2) {
1110 	xl=0;
1111 	xr=(float)item->cols_out;
1112 	xinc=0;
1113     }
1114     else if(angle<M_PI) {
1115 	xl=(float)item->cols_out/2+
1116 	    (dj-(float)item->rows_in/(2*cos_angle))/
1117 		tan(angle)-2;
1118 	xr=(float)item->cols_out/2+
1119 	    (dj+(float)item->rows_in/(2*cos_angle))/
1120 		tan(angle)+2;
1121 	xinc=1./tan(angle);
1122     }
1123     else {
1124 	xl=(float)item->cols_out/2+
1125 	    (dj+(float)item->rows_in/(2*cos_angle))/
1126 		tan(angle)-2;
1127 	xr=(float)item->cols_out/2+
1128 	    (dj-(float)item->rows_in/(2*cos_angle))/
1129 		tan(angle)+2;
1130 
1131 	xinc=1./tan(angle);
1132     }
1133 
1134     /* loop through all relevent bits in rotated image */
1135     for(j=0; j<item->rows_out; j++) {
1136 
1137 	/* no point re-calculating these every pass */
1138 	di=(float)((xl<0)?0:(int)xl)+0.5-(float)item->cols_out/2;
1139 	byte_out=(item->rows_out-j-1)*byte_w_out;
1140 
1141 	/* loop through meaningful columns */
1142 	for(i=((xl<0)?0:(int)xl);
1143 	    i<((xr>=item->cols_out)?item->cols_out:(int)xr); i++) {
1144 
1145 	    /* rotate coordinates */
1146 	    it=(float)item->cols_in/2 + ( di*cos_angle + dj*sin_angle);
1147 	    jt=(float)item->rows_in/2 - (-di*sin_angle + dj*cos_angle);
1148 
1149             /* set pixel if required */
1150             if(it>=0 && it<item->cols_in && jt>=0 && jt<item->rows_in)
1151                 if((I_in->data[jt*byte_w_in+it/8] & 128>>(it%8))>0)
1152                     item->ximage->data[byte_out+i/8]|=128>>i%8;
1153 
1154 	    di+=1;
1155 	}
1156 	dj+=1;
1157 	xl+=xinc;
1158 	xr+=xinc;
1159     }
1160     XDestroyImage(I_in);
1161 
1162     if(style.magnify!=1.) {
1163 	item->cols_in=old_cols_in;
1164 	item->rows_in=old_rows_in;
1165     }
1166 
1167 
1168 #ifdef CACHE_BITMAPS
1169 
1170     /* create a bitmap to hold rotated text */
1171     item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
1172 			       item->cols_out, item->rows_out, 1);
1173 
1174     /* make the text bitmap from XImage */
1175     XPutImage(dpy, item->bitmap, font_gc, item->ximage, 0, 0, 0, 0,
1176 	      item->cols_out, item->rows_out);
1177 
1178     XDestroyImage(item->ximage);
1179 
1180 #endif /*CACHE_BITMAPS*/
1181 
1182     XFreeGC(dpy, font_gc);
1183     XFreePixmap(dpy, canvas);
1184 
1185     return item;
1186 }
1187 
1188 
1189 /* ---------------------------------------------------------------------- */
1190 
1191 
1192 /**************************************************************************/
1193 /*  Adds a text item to the end of the cache, removing as many items      */
1194 /*      from the front as required to keep cache size below limit         */
1195 /**************************************************************************/
1196 
XRotAddToLinkedList(dpy,item)1197 static void XRotAddToLinkedList(dpy, item)
1198     Display *dpy;
1199     RotatedTextItem *item;
1200 {
1201 
1202     static long int current_size=0;
1203     static RotatedTextItem *last=NULL;
1204     RotatedTextItem *i1=first_text_item, *i2=NULL;
1205 
1206 #ifdef CACHE_BITMAPS
1207 
1208     /* I don't know how much memory a pixmap takes in the server -
1209            probably this + a bit more we can't account for */
1210 
1211     item->size=((item->cols_out-1)/8+1)*item->rows_out;
1212 
1213 #else
1214 
1215     /* this is pretty much the size of a RotatedTextItem */
1216 
1217     item->size=((item->cols_out-1)/8+1)*item->rows_out +
1218 	sizeof(XImage) + strlen(item->text) +
1219 	    item->nl*8*sizeof(float) + sizeof(RotatedTextItem);
1220 
1221     if(item->font_name!=NULL)
1222 	item->size+=strlen(item->font_name);
1223     else
1224 	item->size+=sizeof(Font);
1225 
1226 #endif /*CACHE_BITMAPS */
1227 
1228 #ifdef DEBUG
1229     /* count number of items in cache, for debugging */
1230     {
1231 	int i=0;
1232 
1233 	while(i1) {
1234 	    i++;
1235 	    i1=i1->next;
1236 	}
1237 	DEBUG_PRINT2("Cache has %d items.\n", i);
1238 	i1=first_text_item;
1239     }
1240 #endif
1241 
1242     DEBUG_PRINT4("current cache size=%ld, new item=%ld, limit=%ld\n",
1243 		 current_size, item->size, CACHE_SIZE_LIMIT*1024);
1244 
1245     /* if this item is bigger than whole cache, forget it */
1246     if(item->size>CACHE_SIZE_LIMIT*1024) {
1247 	DEBUG_PRINT1("Too big to cache\n\n");
1248 	item->cached=0;
1249 	return;
1250     }
1251 
1252     /* remove elements from cache as needed */
1253     while(i1 && current_size+item->size>CACHE_SIZE_LIMIT*1024) {
1254 
1255 	DEBUG_PRINT2("Removed %d bytes\n", i1->size);
1256 
1257 	if(i1->font_name!=NULL)
1258 	    DEBUG_PRINT5("  (`%s'\n   %s\n   angle=%f align=%d)\n",
1259 			 i1->text, i1->font_name, i1->angle, i1->align);
1260 
1261 #ifdef CACHE_FID
1262 	if(i1->font_name==NULL)
1263 	    DEBUG_PRINT5("  (`%s'\n  FID=%ld\n   angle=%f align=%d)\n",
1264                          i1->text, i1->fid, i1->angle, i1->align);
1265 #endif /*CACHE_FID*/
1266 
1267 	current_size-=i1->size;
1268 
1269 	i2=i1->next;
1270 
1271 	/* free resources used by the unlucky item */
1272 	XRotFreeTextItem(dpy, i1);
1273 
1274 	/* remove it from linked list */
1275 	first_text_item=i2;
1276 	i1=i2;
1277     }
1278 
1279     /* add new item to end of linked list */
1280     if(first_text_item==NULL) {
1281 	item->next=NULL;
1282 	first_text_item=item;
1283 	last=item;
1284     }
1285     else {
1286 	item->next=NULL;
1287 	last->next=item;
1288 	last=item;
1289     }
1290 
1291     /* new cache size */
1292     current_size+=item->size;
1293 
1294     item->cached=1;
1295 
1296     DEBUG_PRINT1("Added item to cache.\n");
1297 }
1298 
1299 
1300 /* ---------------------------------------------------------------------- */
1301 
1302 
1303 /**************************************************************************/
1304 /*  Free the resources used by a text item                                */
1305 /**************************************************************************/
1306 
XRotFreeTextItem(dpy,item)1307 static void XRotFreeTextItem(dpy, item)
1308     Display *dpy;
1309     RotatedTextItem *item;
1310 {
1311     free(item->text);
1312 
1313     if(item->font_name!=NULL)
1314 	free(item->font_name);
1315 
1316     free((char *)item->corners_x);
1317     free((char *)item->corners_y);
1318 
1319 #ifdef CACHE_BITMAPS
1320     XFreePixmap(dpy, item->bitmap);
1321 #else
1322     XDestroyImage(item->ximage);
1323 #endif /* CACHE_BITMAPS */
1324 
1325     free((char *)item);
1326 }
1327 
1328 
1329 /* ---------------------------------------------------------------------- */
1330 
1331 
1332 /**************************************************************************/
1333 /* Magnify an XImage using bilinear interpolation                         */
1334 /**************************************************************************/
1335 
XRotMagnifyImage(dpy,ximage)1336 static XImage *XRotMagnifyImage(dpy, ximage)
1337     Display *dpy;
1338     XImage *ximage;
1339 {
1340     int i, j;
1341     float x, y;
1342     float u,t;
1343     XImage *I_out;
1344     int cols_in, rows_in;
1345     int cols_out, rows_out;
1346     register int i2, j2;
1347     float z1, z2, z3, z4;
1348     int byte_width_in, byte_width_out;
1349     float mag_inv;
1350 
1351     /* size of input image */
1352     cols_in=ximage->width;
1353     rows_in=ximage->height;
1354 
1355     /* size of final image */
1356     cols_out=(float)cols_in*style.magnify;
1357     rows_out=(float)rows_in*style.magnify;
1358 
1359     /* this will hold final image */
1360     I_out=MakeXImage(dpy, cols_out, rows_out);
1361     if(I_out==NULL)
1362 	return NULL;
1363 
1364     /* width in bytes of input, output images */
1365     byte_width_in=(cols_in-1)/8+1;
1366     byte_width_out=(cols_out-1)/8+1;
1367 
1368     /* for speed */
1369     mag_inv=1./style.magnify;
1370 
1371     y=0.;
1372 
1373     /* loop over magnified image */
1374     for(j2=0; j2<rows_out; j2++) {
1375 	x=0;
1376 	j=y;
1377 
1378 	for(i2=0; i2<cols_out; i2++) {
1379 	    i=x;
1380 
1381 	    /* bilinear interpolation - where are we on bitmap ? */
1382 	    /* right edge */
1383 	    if(i==cols_in-1 && j!=rows_in-1) {
1384 		t=0;
1385 		u=y-(float)j;
1386 
1387 		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
1388 		z2=z1;
1389 		z3=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0;
1390 		z4=z3;
1391 	    }
1392 	    /* top edge */
1393 	    else if(i!=cols_in-1 && j==rows_in-1) {
1394 		t=x-(float)i;
1395 		u=0;
1396 
1397 		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
1398 		z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0;
1399 		z3=z2;
1400 		z4=z1;
1401 	    }
1402 	    /* top right corner */
1403 	    else if(i==cols_in-1 && j==rows_in-1) {
1404 		u=0;
1405 		t=0;
1406 
1407 		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
1408 		z2=z1;
1409 		z3=z1;
1410 		z4=z1;
1411 	    }
1412 	    /* somewhere `safe' */
1413 	    else {
1414 		t=x-(float)i;
1415 		u=y-(float)j;
1416 
1417 		z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
1418 		z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0;
1419 		z3=(ximage->data[(j+1)*byte_width_in+(i+1)/8] &
1420 		    128>>((i+1)%8))>0;
1421 		z4=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0;
1422 	    }
1423 
1424 	    /* if interpolated value is greater than 0.5, set bit */
1425 	    if(((1-t)*(1-u)*z1 + t*(1-u)*z2 + t*u*z3 + (1-t)*u*z4)>0.5)
1426 		I_out->data[j2*byte_width_out+i2/8]|=128>>i2%8;
1427 
1428 	    x+=mag_inv;
1429 	}
1430 	y+=mag_inv;
1431     }
1432 
1433     /* destroy original */
1434     XDestroyImage(ximage);
1435 
1436     /* return big image */
1437     return I_out;
1438 }
1439 
1440 
1441 
1442 /* ---------------------------------------------------------------------- */
1443 
1444 
1445 /**************************************************************************/
1446 /* Calculate the bounding box some text will have when painted            */
1447 /**************************************************************************/
1448 
XRotTextExtents(dpy,font,angle,x,y,text,align)1449 XPoint *XRotTextExtents(dpy, font, angle, x, y, text, align)
1450     Display *dpy;
1451     XFontStruct *font;
1452     float angle;
1453     int x, y;
1454     char *text;
1455     int align;
1456 {
1457     register int i;
1458     char *str1, *str2, *str3;
1459     char *str2_a="\0", *str2_b="\n\0";
1460     int height;
1461     float sin_angle, cos_angle;
1462     int nl, max_width;
1463     int cols_in, rows_in;
1464     float hot_x, hot_y;
1465     XPoint *xp_in, *xp_out;
1466     int dir, asc, desc;
1467     XCharStruct overall;
1468 
1469     /* manipulate angle to 0<=angle<360 degrees */
1470     while(angle<0)
1471         angle+=360;
1472 
1473     while(angle>360)
1474         angle-=360;
1475 
1476     angle*=M_PI/180;
1477 
1478     /* count number of sections in string */
1479     nl=1;
1480     if(align!=NONE)
1481 	for(i=0; i<strlen(text)-1; i++)
1482 	    if(text[i]=='\n')
1483 		nl++;
1484 
1485     /* ignore newline characters if not doing alignment */
1486     if(align==NONE)
1487 	str2=str2_a;
1488     else
1489 	str2=str2_b;
1490 
1491     /* find width of longest section */
1492     str1=my_strdup(text);
1493     if(str1==NULL)
1494 	return NULL;
1495 
1496     str3=my_strtok(str1, str2);
1497 
1498     XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
1499 		 &overall);
1500 
1501     max_width=overall.rbearing;
1502 
1503     /* loop through each section */
1504     do {
1505 	str3=my_strtok((char *)NULL, str2);
1506 
1507 	if(str3!=NULL) {
1508 	    XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
1509 			 &overall);
1510 
1511 	    if(overall.rbearing>max_width)
1512 		max_width=overall.rbearing;
1513 	}
1514     }
1515     while(str3!=NULL);
1516 
1517     free(str1);
1518 
1519     /* overall font height */
1520     height=font->ascent+font->descent;
1521 
1522     /* dimensions horizontal text will have */
1523     cols_in=max_width;
1524     rows_in=nl*height;
1525 
1526     /* pre-calculate sin and cos */
1527     sin_angle=sin(angle);
1528     cos_angle=cos(angle);
1529 
1530     /* y position */
1531     if(align==TLEFT || align==TCENTRE || align==TRIGHT)
1532         hot_y=(float)rows_in/2*style.magnify;
1533     else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
1534 	hot_y=0;
1535     else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
1536 	hot_y=-(float)rows_in/2*style.magnify;
1537     else
1538 	hot_y=-((float)rows_in/2-(float)font->descent)*style.magnify;
1539 
1540     /* x position */
1541     if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
1542 	hot_x=-(float)max_width/2*style.magnify;
1543     else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
1544 	hot_x=0;
1545     else
1546         hot_x=(float)max_width/2*style.magnify;
1547 
1548     /* reserve space for XPoints */
1549     xp_in=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
1550     if(!xp_in)
1551 	return NULL;
1552 
1553     xp_out=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
1554     if(!xp_out)
1555 	return NULL;
1556 
1557     /* bounding box when horizontal, relative to bitmap centre */
1558     xp_in[0].x=-(float)cols_in*style.magnify/2-style.bbx_pad;
1559     xp_in[0].y= (float)rows_in*style.magnify/2+style.bbx_pad;
1560     xp_in[1].x= (float)cols_in*style.magnify/2+style.bbx_pad;
1561     xp_in[1].y= (float)rows_in*style.magnify/2+style.bbx_pad;
1562     xp_in[2].x= (float)cols_in*style.magnify/2+style.bbx_pad;
1563     xp_in[2].y=-(float)rows_in*style.magnify/2-style.bbx_pad;
1564     xp_in[3].x=-(float)cols_in*style.magnify/2-style.bbx_pad;
1565     xp_in[3].y=-(float)rows_in*style.magnify/2-style.bbx_pad;
1566     xp_in[4].x=xp_in[0].x;
1567     xp_in[4].y=xp_in[0].y;
1568 
1569     /* rotate and translate bounding box */
1570     for(i=0; i<5; i++) {
1571 	xp_out[i].x=(float)x + ( ((float)xp_in[i].x-hot_x)*cos_angle +
1572 				 ((float)xp_in[i].y+hot_y)*sin_angle);
1573 	xp_out[i].y=(float)y + (-((float)xp_in[i].x-hot_x)*sin_angle +
1574 				 ((float)xp_in[i].y+hot_y)*cos_angle);
1575     }
1576 
1577     free((char *)xp_in);
1578 
1579     return xp_out;
1580 }
1581 
1582 
1583