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