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