1 /* ********************************************************************** */
2 
3 /* xvertext, Copyright (c) 1992 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 #include <X11/Xlib.h>
19 #include <X11/Xutil.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include "Rotated.h"
24 
25 
26 /* ---------------------------------------------------------------------- */
27 
28 
29 int xv_errno;
30 
31 static char *my_strdup(char *);
32 static char *my_strtok(char *, char *);
33 
34 
35 /* ---------------------------------------------------------------------- */
36 
37 
38 /* *** Routine to mimic `strdup()' (some machines don't have it) *** */
39 
my_strdup(char * str)40 static char *my_strdup(char *str)
41 {
42   char *s;
43 
44   if (str == NULL) return NULL;
45 
46   s = (char *)malloc((unsigned)(strlen(str)+1));
47   /* this error is highly unlikely ... */
48   if (s == NULL) {
49     fprintf(stderr, "Fatal error: my_strdup(): Couldn't do malloc (gulp!)\n");
50     exit(1);
51   }
52 
53   strcpy(s, str);
54   return s;
55 }
56 
57 
58 /* ---------------------------------------------------------------------- */
59 
60 
61 /* *** Routine to replace `strtok' : this one returns a zero
62        length string if it encounters two consecutive delimiters *** */
63 
my_strtok(char * str1,char * str2)64 static char *my_strtok(char *str1, char *str2)
65 {
66   char *ret;
67   int i, j, stop;
68   static int start, len;
69   static char *stext;
70 
71   /* this error should never occur ... */
72   if (str2 == NULL) {
73     fprintf(stderr,
74 	    "Fatal error: my_strtok(): recieved null delimiter string\n");
75     exit(1);
76   }
77 
78   /* initialise if str1 not NULL ... */
79   if (str1 != NULL) {
80     start = 0;
81     stext = str1;
82     len = strlen(str1);
83   }
84 
85   /* run out of tokens ? ... */
86   if (start >= len) return NULL;
87 
88   /* loop through characters ... */
89   for (i = start; i < len; i++) {
90 
91     /* loop through delimiters ... */
92     stop = 0;
93     for (j = 0; j < strlen(str2); j++)
94       if (stext[i] == str2[j]) stop = 1;
95 
96     if (stop) break;
97   }
98 
99   stext[i] = '\0';
100   ret = stext + start;
101   start = i+1;
102 
103   return ret;
104 }
105 
106 
107 /* ---------------------------------------------------------------------- */
108 
109 
110 /* *** Routine to return version/copyright information *** */
111 
XRotVersion(char * str,int n)112 float XRotVersion(char *str, int n)
113 {
114   if (str != NULL) strncpy(str, XV_COPYRIGHT, n);
115   return XV_VERSION;
116 }
117 
118 
119 /* ---------------------------------------------------------------------- */
120 
121 
122 /* *** Load the rotated version of a given font *** */
123 
XRotLoadFont(Display * dpy,char * fontname,float angle)124 XRotFontStruct *XRotLoadFont(Display *dpy, char *fontname, float angle)
125 {
126   char val;
127   XImage *I1, *I2;
128   Pixmap canvas;
129   Window root;
130   int screen;
131   GC font_gc;
132   char text[3];/*, errstr[300];*/
133   XFontStruct *fontstruct;
134   XRotFontStruct *rotfont;
135   int ichar, i, j, index, boxlen = 60, dir;
136   int vert_w, vert_h, vert_len, bit_w, bit_h, bit_len;
137   int min_char, max_char;
138   unsigned char *vertdata, *bitdata;
139   int ascent, descent, lbearing, rbearing;
140   int on = 1, off = 0;
141 
142   /* make angle positive ... */
143   if (angle < 0) do angle += 360; while (angle < 0);
144 
145   /* get nearest vertical or horizontal direction ... */
146   dir = (int)((angle+45.)/90.)%4;
147 
148   /* useful macros ... */
149   screen = DefaultScreen(dpy);
150   root = DefaultRootWindow(dpy);
151 
152   /* create the depth 1 canvas bitmap ... */
153   canvas = XCreatePixmap(dpy, root, boxlen, boxlen, 1);
154 
155   /* create a GC ... */
156   font_gc = XCreateGC(dpy, canvas, 0, 0);
157   XSetBackground(dpy, font_gc, off);
158 
159   /* load the font ... */
160   fontstruct = XLoadQueryFont(dpy, fontname);
161   if (fontstruct == NULL) {
162     xv_errno = XV_NOFONT;
163     return NULL;
164   }
165 
166   XSetFont(dpy, font_gc, fontstruct->fid);
167 
168   /* allocate space for rotated font ... */
169   rotfont = (XRotFontStruct *)malloc((unsigned)sizeof(XRotFontStruct));
170   if (rotfont == NULL) {
171     xv_errno = XV_NOMEM;
172     return NULL;
173   }
174 
175   /* determine which characters are defined in font ... */
176   min_char = fontstruct->min_char_or_byte2;
177   max_char = fontstruct->max_char_or_byte2;
178 
179   /* we only want printing characters ... */
180   if (min_char<32)  min_char = 32;
181   if (max_char>126) max_char = 126;
182 
183   /* some overall font data ... */
184   rotfont->name = my_strdup(fontname);
185   rotfont->dir = dir;
186   rotfont->min_char = min_char;
187   rotfont->max_char = max_char;
188   rotfont->max_ascent = fontstruct->max_bounds.ascent;
189   rotfont->max_descent = fontstruct->max_bounds.descent;
190   rotfont->height = rotfont->max_ascent+rotfont->max_descent;
191 
192   /* remember xfontstruct for `normal' text ... */
193   if (dir == 0) rotfont->xfontstruct = fontstruct;
194 
195   else {
196     /* font needs rotation ... */
197     /* loop through each character ... */
198     for (ichar = min_char; ichar <= max_char; ichar++) {
199 
200       index = ichar-fontstruct->min_char_or_byte2;
201 
202       /* per char dimensions ... */
203       ascent =   rotfont->per_char[ichar-32].ascent =
204 	fontstruct->per_char[index].ascent;
205       descent =  rotfont->per_char[ichar-32].descent =
206 	fontstruct->per_char[index].descent;
207       lbearing = rotfont->per_char[ichar-32].lbearing =
208 	fontstruct->per_char[index].lbearing;
209       rbearing = rotfont->per_char[ichar-32].rbearing =
210 	fontstruct->per_char[index].rbearing;
211       rotfont->per_char[ichar-32].width =
212 	fontstruct->per_char[index].width;
213 
214       /* some space chars have zero body, but a bitmap can't have ... */
215       if (!ascent && !descent)
216 	ascent =   rotfont->per_char[ichar-32].ascent =   1;
217       if (!lbearing && !rbearing)
218 	rbearing = rotfont->per_char[ichar-32].rbearing = 1;
219 
220       /* glyph width and height when vertical ... */
221       vert_w = rbearing-lbearing;
222       vert_h = ascent+descent;
223 
224       /* width in bytes ... */
225       vert_len = (vert_w-1)/8+1;
226 
227       XSetForeground(dpy, font_gc, off);
228       XFillRectangle(dpy, canvas, font_gc, 0, 0, boxlen, boxlen);
229 
230       /* draw the character centre top right on canvas ... */
231       sprintf(text, "%c", ichar);
232       XSetForeground(dpy, font_gc, on);
233       XDrawImageString(dpy, canvas, font_gc, boxlen/2 - lbearing,
234 		       boxlen/2 - descent, text, 1);
235 
236       /* reserve memory for first XImage ... */
237       vertdata = (unsigned char *) malloc((unsigned)(vert_len*vert_h));
238       if (vertdata == NULL) {
239 	xv_errno = XV_NOMEM;
240 	return NULL;
241       }
242 
243       /* create the XImage ... */
244       I1 = XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap,
245 			0, (char *)vertdata, vert_w, vert_h, 8, 0);
246 
247       if (I1 == NULL) {
248 	xv_errno = XV_NOXIMAGE;
249 	return NULL;
250       }
251 
252       I1->byte_order = I1->bitmap_bit_order = MSBFirst;
253 
254       /* extract character from canvas ... */
255       XGetSubImage(dpy, canvas, boxlen/2, boxlen/2-vert_h,
256 		   vert_w, vert_h, 1, XYPixmap, I1, 0, 0);
257       I1->format = XYBitmap;
258 
259       /* width, height of rotated character ... */
260       if (dir == 2) {
261 	bit_w = vert_w;
262 	bit_h = vert_h;
263       } else {
264 	bit_w = vert_h;
265 	bit_h = vert_w;
266       }
267 
268       /* width in bytes ... */
269       bit_len = (bit_w-1)/8 + 1;
270 
271       rotfont->per_char[ichar-32].glyph.bit_w = bit_w;
272       rotfont->per_char[ichar-32].glyph.bit_h = bit_h;
273 
274       /* reserve memory for the rotated image ... */
275       bitdata = (unsigned char *)calloc((unsigned)(bit_h*bit_len), 1);
276       if (bitdata == NULL) {
277 	xv_errno = XV_NOMEM;
278 	return NULL;
279       }
280 
281       /* create the image ... */
282       I2 = XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap, 0,
283 			(char *)bitdata, bit_w, bit_h, 8, 0);
284 
285       if (I2 == NULL) {
286 	xv_errno = XV_NOXIMAGE;
287 	return NULL;
288       }
289 
290       I2->byte_order = I2->bitmap_bit_order = MSBFirst;
291 
292       /* map vertical data to rotated character ... */
293       for (j = 0; j < bit_h; j++) {
294 	for (i = 0; i < bit_w; i++) {
295 	  /* map bits ... */
296 	  if (dir == 1)
297 	    val = vertdata[i*vert_len + (vert_w-j-1)/8] &
298 	      (128>>((vert_w-j-1)%8));
299 
300 	  else if (dir == 2)
301 	    val = vertdata[(vert_h-j-1)*vert_len + (vert_w-i-1)/8] &
302 	      (128>>((vert_w-i-1)%8));
303 
304 	  else
305 	    val = vertdata[(vert_h-i-1)*vert_len + j/8] &
306 	      (128>>(j%8));
307 
308 	  if (val)
309 	    bitdata[j*bit_len + i/8] = bitdata[j*bit_len + i/8] |
310 	      (128>>(i%8));
311 	}
312       }
313 
314       /* create this character's bitmap ... */
315       rotfont->per_char[ichar-32].glyph.bm =
316 	XCreatePixmap(dpy, root, bit_w, bit_h, 1);
317 
318       /* put the image into the bitmap ... */
319       XPutImage(dpy, rotfont->per_char[ichar-32].glyph.bm,
320 		font_gc, I2, 0, 0, 0, 0, bit_w, bit_h);
321 
322       /* free the image and data ... */
323       XDestroyImage(I1);
324       XDestroyImage(I2);
325       /*      free((char *)bitdata);  -- XDestroyImage does this
326 	      free((char *)vertdata);*/
327     }
328 
329     XFreeFont(dpy, fontstruct);
330   }
331 
332   /* free pixmap and GC ... */
333   XFreePixmap(dpy, canvas);
334   XFreeGC(dpy, font_gc);
335 
336   return rotfont;
337 }
338 
339 
340 /* ---------------------------------------------------------------------- */
341 
342 
343 /* *** Free the resources associated with a rotated font *** */
344 
XRotUnloadFont(Display * dpy,XRotFontStruct * rotfont)345 void XRotUnloadFont(Display *dpy, XRotFontStruct *rotfont)
346 {
347   int ichar;
348 
349   if (rotfont->dir == 0) XFreeFont(dpy, rotfont->xfontstruct);
350 
351   else
352     /* loop through each character, freeing its pixmap ... */
353     for (ichar = rotfont->min_char-32; ichar <= rotfont->max_char-32; ichar++)
354       XFreePixmap(dpy, rotfont->per_char[ichar].glyph.bm);
355 
356   /* rotfont should never be referenced again ... */
357   free((char *)rotfont->name);
358   free((char *)rotfont);
359 }
360 
361 
362 /* ---------------------------------------------------------------------- */
363 
364 
365 /* *** Return the width of a string *** */
366 
XRotTextWidth(XRotFontStruct * rotfont,char * str,int len)367 int XRotTextWidth(XRotFontStruct *rotfont, char *str, int len)
368 {
369   int i, width = 0, ichar;
370 
371   if (str == NULL) return 0;
372 
373   if (rotfont->dir == 0)
374     width = XTextWidth(rotfont->xfontstruct, str, strlen(str));
375 
376   else
377     for (i = 0; i<len; i++) {
378       ichar = str[i]-32;
379 
380       /* make sure it's a printing character ... */
381       if (ichar >= 0 && ichar<95)
382 	width += rotfont->per_char[ichar].width;
383     }
384 
385   return width;
386 }
387 
388 
389 /* ---------------------------------------------------------------------- */
390 
391 
392 /* *** A front end to XRotPaintString : mimics XDrawString *** */
393 
XRotDrawString(Display * dpy,XRotFontStruct * rotfont,Drawable drawable,GC gc,int x,int y,char * str,int len)394 void XRotDrawString(Display *dpy, XRotFontStruct *rotfont, Drawable drawable,
395 		    GC gc, int x, int y, char *str, int len)
396 {
397   static GC my_gc = 0;
398   int i, xp, yp, dir, ichar;
399 
400   if (str == NULL || len<1) return;
401 
402   dir = rotfont->dir;
403   if (my_gc == 0) my_gc = XCreateGC(dpy, drawable, 0, 0);
404 
405   XCopyGC(dpy, gc, GCForeground|GCBackground, my_gc);
406 
407   /* a horizontal string is easy ... */
408   if (dir == 0) {
409     XSetFillStyle(dpy, my_gc, FillSolid);
410     XSetFont(dpy, my_gc, rotfont->xfontstruct->fid);
411     XDrawString(dpy, drawable, my_gc, x, y, str, len);
412     return;
413   }
414 
415   /* vertical or upside down ... */
416 
417   XSetFillStyle(dpy, my_gc, FillStippled);
418 
419   /* loop through each character in string ... */
420   for (i = 0; i<len; i++) {
421     ichar = str[i]-32;
422 
423     /* make sure it's a printing character ... */
424     if (ichar >= 0 && ichar<95) {
425       /* suitable offset ... */
426       if (dir == 1) {
427 	xp = x-rotfont->per_char[ichar].ascent;
428 	yp = y-rotfont->per_char[ichar].rbearing;
429       }
430       else if (dir == 2) {
431 	xp = x-rotfont->per_char[ichar].rbearing;
432 	yp = y-rotfont->per_char[ichar].descent+1;
433       }
434       else {
435 	xp = x-rotfont->per_char[ichar].descent+1;
436 	yp = y+rotfont->per_char[ichar].lbearing;
437       }
438 
439       /* draw the glyph ... */
440       XSetStipple(dpy, my_gc, rotfont->per_char[ichar].glyph.bm);
441 
442       XSetTSOrigin(dpy, my_gc, xp, yp);
443 
444       XFillRectangle(dpy, drawable, my_gc, xp, yp,
445 		     rotfont->per_char[ichar].glyph.bit_w,
446 		     rotfont->per_char[ichar].glyph.bit_h);
447 
448       /* advance position ... */
449       if (dir == 1)
450 	y -= rotfont->per_char[ichar].width;
451       else if (dir == 2)
452 	x -= rotfont->per_char[ichar].width;
453       else
454 	y += rotfont->per_char[ichar].width;
455     }
456   }
457 }
458 
459 
460 
461 /* ---------------------------------------------------------------------- */
462 
463 
464 /* *** A front end to XRotPaintAlignedString : uses XRotDrawString *** */
465 
XRotDrawAlignedString(Display * dpy,XRotFontStruct * rotfont,Drawable drawable,GC gc,int x,int y,char * text,int align)466 void XRotDrawAlignedString(Display *dpy, XRotFontStruct *rotfont,
467 			   Drawable drawable, GC gc, int x, int y,
468 			   char *text, int align)
469 {
470   int xp = 0, yp = 0, dir;
471   int i, nl = 1, max_width = 0, this_width;
472   char *str1, *str2 = "\n\0", *str3;
473 
474   if (text == NULL)
475     return;
476 
477   dir = rotfont->dir;
478 
479   /* count number of sections in string ... */
480   for (i = 0; i<strlen(text); i++)
481     if (text[i] == '\n')
482       nl++;
483 
484   /* find width of longest section ... */
485   str1 = my_strdup(text);
486   str3 = my_strtok(str1, str2);
487   max_width = XRotTextWidth(rotfont, str3, strlen(str3));
488 
489   do {
490     str3 = my_strtok((char *)NULL, str2);
491     if (str3 != NULL)
492       if (XRotTextWidth(rotfont, str3, strlen(str3))>max_width)
493 	max_width = XRotTextWidth(rotfont, str3, strlen(str3));
494   }
495   while (str3 != NULL);
496 
497   /* calculate vertical starting point according to alignment policy and
498      rotation angle ... */
499   if (dir == 0) {
500     if (align == TLEFT || align == TCENTRE || align == TRIGHT)
501       yp = y+rotfont->max_ascent;
502 
503     else if (align == BLEFT || align == BCENTRE || align == BRIGHT)
504       yp = y-(nl-1)*rotfont->height - rotfont->max_descent;
505 
506     else
507       yp = y-(nl-1)/2*rotfont->height + rotfont->max_ascent -
508 	rotfont->height/2 - ((nl%2 == 0)?rotfont->height/2:0);
509   }
510 
511   else if (dir == 1) {
512     if (align == TLEFT || align == TCENTRE || align == TRIGHT)
513       xp = x+rotfont->max_ascent;
514 
515     else if (align == BLEFT || align == BCENTRE || align == BRIGHT)
516       xp = x-(nl-1)*rotfont->height - rotfont->max_descent;
517 
518     else
519       xp = x-(nl-1)/2*rotfont->height + rotfont->max_ascent -
520 	rotfont->height/2 - ((nl%2 == 0)?rotfont->height/2:0);
521   }
522 
523   else if (dir == 2) {
524     if (align == TLEFT || align == TCENTRE || align == TRIGHT)
525       yp = y-rotfont->max_ascent;
526 
527     else if (align == BLEFT || align == BCENTRE || align == BRIGHT)
528       yp = y+(nl-1)*rotfont->height + rotfont->max_descent;
529 
530     else
531       yp = y+(nl-1)/2*rotfont->height - rotfont->max_ascent +
532 	rotfont->height/2 + ((nl%2 == 0)?rotfont->height/2:0);
533   }
534 
535   else {
536     if (align == TLEFT || align == TCENTRE || align == TRIGHT)
537       xp = x-rotfont->max_ascent;
538 
539     else if (align == BLEFT || align == BCENTRE || align == BRIGHT)
540       xp = x+(nl-1)*rotfont->height + rotfont->max_descent;
541 
542     else
543       xp = x+(nl-1)/2*rotfont->height - rotfont->max_ascent +
544 	rotfont->height/2 + ((nl%2 == 0)?rotfont->height/2:0);
545   }
546 
547   free(str1);
548   str1 = my_strdup(text);
549   str3 = my_strtok(str1, str2);
550 
551   /* loop through each section in the string ... */
552   do {
553     /* width of this section ... */
554     this_width = XRotTextWidth(rotfont, str3, strlen(str3));
555 
556     /* horizontal alignment ... */
557     if (dir == 0) {
558       if (align == TLEFT || align == MLEFT || align == BLEFT)
559 	xp = x;
560 
561       else if (align == TCENTRE || align == MCENTRE || align == BCENTRE)
562 	xp = x-this_width/2;
563 
564       else
565 	xp = x-max_width;
566     }
567 
568     else if (dir == 1) {
569       if (align == TLEFT || align == MLEFT || align == BLEFT)
570 	yp = y;
571 
572       else if (align == TCENTRE || align == MCENTRE || align == BCENTRE)
573 	yp = y+this_width/2;
574 
575       else
576 	yp = y+max_width;
577     }
578 
579     else if (dir == 2) {
580       if (align == TLEFT || align == MLEFT || align == BLEFT)
581 	xp = x;
582 
583       else if (align == TCENTRE || align == MCENTRE || align == BCENTRE)
584 	xp = x+this_width/2;
585 
586       else
587 	xp = x+max_width;
588     }
589 
590     else {
591       if (align == TLEFT || align == MLEFT || align == BLEFT)
592 	yp = y;
593 
594       else if (align == TCENTRE || align == MCENTRE || align == BCENTRE)
595 	yp = y-this_width/2;
596 
597       else
598 	yp = y-max_width;
599     }
600 
601     /* draw the section ... */
602     XRotDrawString(dpy, rotfont, drawable, gc, xp, yp,
603 		   str3, strlen(str3));
604 
605     str3 = my_strtok((char *)NULL, str2);
606 
607     /* advance position ... */
608     if (dir == 0)
609       yp += rotfont->height;
610     else if (dir == 1)
611       xp += rotfont->height;
612     else if (dir == 2)
613       yp -= rotfont->height;
614     else
615       xp -= rotfont->height;
616   }
617   while (str3 != NULL);
618 
619   free(str1);
620 }
621 
622