1 // XFontImp.cc for FbTk fluxbox toolkit
2 // Copyright (c) 2002 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21 
22 #include "XFontImp.hh"
23 #include "App.hh"
24 #include "GContext.hh"
25 #include "FbPixmap.hh"
26 #include "I18n.hh"
27 
28 #include <X11/Xutil.h>
29 
30 #include <iostream>
31 #include <new>
32 #include <cstdio>
33 #include <cstring>
34 #include <algorithm>
35 
36 
37 using std::cerr;
38 using std::endl;
39 using std::string;
40 using std::nothrow;
41 
42 namespace FbTk {
43 
XFontImp(const char * fontname)44 XFontImp::XFontImp(const char *fontname):m_fontstruct(0) {
45     for (int i = ROT0; i <= ROT270; ++i) {
46         m_rotfonts[i] = 0;
47         m_rotfonts_loaded[i] = false;
48     }
49 
50     if (fontname != 0)
51         load(fontname);
52 }
53 
~XFontImp()54 XFontImp::~XFontImp() {
55     if (m_fontstruct != 0)
56         XFreeFont(App::instance()->display(), m_fontstruct);
57 
58     for (int i = ROT0; i <= ROT270; ++i)
59         if (m_rotfonts[i] != 0)
60             freeRotFont(m_rotfonts[i]);
61 }
62 
ascent() const63 int XFontImp::ascent() const {
64     if (m_fontstruct == 0)
65         return 0;
66 
67     return m_fontstruct->ascent;
68 }
69 
load(const string & fontname)70 bool XFontImp::load(const string &fontname) {
71 
72     XFontStruct *font = XLoadQueryFont(App::instance()->display(), fontname.c_str());
73     if (font == 0)
74         return false;
75     if (m_fontstruct != 0) // free old font struct, if any
76         XFreeFont(App::instance()->display(), m_fontstruct);
77 
78     m_fontstruct = font; //set new font
79 
80     for (int i = ROT0; i <= ROT270; ++i) {
81         m_rotfonts_loaded[i] = false;
82         if (m_rotfonts[i] != 0) {
83             freeRotFont(m_rotfonts[i]);
84             m_rotfonts[i] = 0;
85         }
86     }
87 
88     return true;
89 }
90 
drawText(const FbDrawable & w,int screen,GC gc,const char * text,size_t len,int x,int y,FbTk::Orientation orient)91 void XFontImp::drawText(const FbDrawable &w, int screen, GC gc, const char* text, size_t len, int x, int y, FbTk::Orientation orient) {
92 
93     if (!text || !*text || m_fontstruct == 0)
94         return;
95 
96     std::string localestr = FbStringUtil::FbStrToLocale(FbString(text, len));
97 
98     // use roated font functions?
99     if (orient != ROT0 && validOrientation(orient)) {
100         drawRotText(w.drawable(), screen, gc, localestr.c_str(), localestr.size(), x, y, orient);
101         return;
102     }
103 
104     XSetFont(w.display(), gc, m_fontstruct->fid);
105     XDrawString(w.display(), w.drawable(), gc, x, y, localestr.data(), localestr.size());
106 }
107 
textWidth(const char * text,unsigned int size) const108 unsigned int XFontImp::textWidth(const char* text, unsigned int size) const {
109 
110     if (!text || !*text || m_fontstruct == 0)
111         return 0;
112 
113     std::string localestr = FbStringUtil::FbStrToLocale(FbString(text, size));
114 
115     return XTextWidth(m_fontstruct, localestr.data(), localestr.size());
116 }
117 
height() const118 unsigned int XFontImp::height() const {
119     if (m_fontstruct == 0)
120         return 0;
121 
122     return m_fontstruct->ascent + m_fontstruct->descent;
123 }
124 
rotate(FbTk::Orientation orient)125 void XFontImp::rotate(FbTk::Orientation orient) {
126     //we must have a font loaded before we rotate
127     if (m_fontstruct == 0 || m_fontstruct->per_char == 0 || orient == ROT0)
128         return;
129 
130     _FB_USES_NLS;
131 
132     // X system default vars
133     Display *dpy = App::instance()->display();
134     Window rootwin = DefaultRootWindow(dpy);
135     int screen = DefaultScreen(dpy);
136 
137     char text[3];
138     int ichar, i, j, index, boxlen = 60;
139     int vert_w, vert_h, vert_len, bit_w, bit_h, bit_len;
140     int min_char, max_char;
141     unsigned char *vertdata, *bitdata;
142     int ascent, descent, lbearing, rbearing;
143 
144     // create the depth 1 canvas bitmap
145     FbTk::FbPixmap canvas(rootwin, boxlen, boxlen, 1);
146 
147     // create graphic context for our canvas
148     FbTk::GContext font_gc(canvas);
149     font_gc.setBackground(None);
150     font_gc.setFont(m_fontstruct->fid);
151 
152     // allocate space for rotated font
153     m_rotfonts[orient] = new(nothrow) XRotFontStruct;
154     XRotFontStruct *rotfont = m_rotfonts[orient];
155 
156     if (rotfont == 0) {
157         cerr<<"RotFont: "<<_FBTK_CONSOLETEXT(Error, OutOfMemory, "Out of memory", "Something couldn't allocate memory")<<endl;
158         return;
159     }
160 
161     // determine which characters are defined in font
162     min_char = m_fontstruct->min_char_or_byte2;
163     max_char = m_fontstruct->max_char_or_byte2;
164 
165     // we only want printable chars
166     if (min_char<32)
167         min_char = 32;
168     if (max_char>126)
169         max_char = 126;
170 
171     /* some overall font data ... */
172     rotfont->min_char = min_char;
173     rotfont->max_char = max_char;
174     rotfont->max_ascent = m_fontstruct->max_bounds.ascent;
175     rotfont->max_descent = m_fontstruct->max_bounds.descent;
176     rotfont->height = rotfont->max_ascent + rotfont->max_descent;
177 
178     // font needs rotation
179     // loop through each character
180     for (ichar = min_char; ichar <= max_char; ichar++) {
181         index = ichar - m_fontstruct->min_char_or_byte2;
182 
183         // per char dimensions ...
184         ascent = rotfont->per_char[ichar-32].ascent = m_fontstruct->per_char[index].ascent;
185         descent =  rotfont->per_char[ichar-32].descent = m_fontstruct->per_char[index].descent;
186         lbearing = rotfont->per_char[ichar-32].lbearing = m_fontstruct->per_char[index].lbearing;
187         rbearing = rotfont->per_char[ichar-32].rbearing = m_fontstruct->per_char[index].rbearing;
188         rotfont->per_char[ichar-32].width = m_fontstruct->per_char[index].width;
189 
190         // some space chars have zero body, but a bitmap can't have
191         if (!ascent && !descent)
192             ascent = rotfont->per_char[ichar-32].ascent =   1;
193         if (!lbearing && !rbearing)
194             rbearing = rotfont->per_char[ichar-32].rbearing = 1;
195 
196         // glyph width and height when vertical
197         vert_w = rbearing - lbearing;
198         vert_h = ascent + descent;
199 
200         // width in bytes
201         vert_len = (vert_w-1)/8+1;
202 
203         font_gc.setForeground(None);
204         canvas.fillRectangle(font_gc.gc(),
205                              0, 0,
206                              boxlen, boxlen);
207         // draw the character centre top right on canvas
208         snprintf(text, 1, "%c", ichar);
209         font_gc.setForeground(1);
210         XDrawImageString(dpy, canvas.drawable(), font_gc.gc(),
211                          boxlen/2 - lbearing,
212                          boxlen/2 - descent, text, 1);
213 
214         // reserve memory for first XImage
215         vertdata = (unsigned char *)calloc((unsigned)(vert_len * vert_h), 1);
216 
217         XImage *I1 = XCreateImage(dpy, DefaultVisual(dpy, screen),
218                                   1, XYBitmap,
219                                   0, (char *)vertdata,
220                                   vert_w, vert_h, 8, 0);
221 
222         if (I1 == None) {
223             cerr << "RotFont: " << _FBTK_CONSOLETEXT(Error, CreateXImage,
224                                          "Can't create XImage",
225                                          "XCreateImage failed for some reason")
226                  << "." << endl;
227             free(vertdata);
228             delete rotfont;
229             m_rotfonts[orient] = 0;
230             return;
231         }
232 
233         I1->byte_order = I1->bitmap_bit_order = MSBFirst;
234 
235         // extract character from canvas
236         XGetSubImage(dpy, canvas.drawable(),
237                      boxlen/2, boxlen/2 - vert_h,
238                      vert_w, vert_h, 1, XYPixmap, I1, 0, 0);
239 
240         I1->format = XYBitmap;
241 
242         // width, height of rotated character
243         if (orient == ROT180) {
244             bit_w = vert_w;
245             bit_h = vert_h;
246         } else {
247             bit_w = vert_h;
248             bit_h = vert_w;
249         }
250 
251         // width in bytes
252         bit_len = (bit_w-1)/8 + 1;
253 
254         rotfont->per_char[ichar-32].glyph.bit_w = bit_w;
255         rotfont->per_char[ichar-32].glyph.bit_h = bit_h;
256 
257         // reserve memory for the rotated image
258         bitdata = (unsigned char *)calloc((unsigned)(bit_h * bit_len), 1);
259 
260         // create the image
261         XImage *I2 = XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap, 0,
262                           (char *)bitdata, bit_w, bit_h, 8, 0);
263 
264         if (I2 == None) {
265             cerr << "XFontImp: " <<_FBTK_CONSOLETEXT(Error, CreateXImage,
266                                           "Can't create XImage",
267                                           "XCreateImage failed for some reason")
268                  << "." << endl;
269             XDestroyImage(I1);
270             free(bitdata);
271             delete rotfont;
272             m_rotfonts[orient] = 0;
273             return;
274         }
275 
276         I2->byte_order = I2->bitmap_bit_order = MSBFirst;
277 
278         // map vertical data to rotated character
279         for (j = 0; j < bit_h; j++) {
280             for (i = 0; i < bit_w; i++) {
281                 char val = 0;
282                 if (orient == ROT270) {
283                     val = vertdata[i*vert_len + (vert_w-j-1)/8] &
284                         (128>>((vert_w-j-1)%8));
285                 } else if (orient == ROT180) {
286                     val = vertdata[(vert_h-j-1)*vert_len +
287                                    (vert_w-i-1)/8] & (128>>((vert_w-i-1)%8));
288                 } else {
289                     val = vertdata[(vert_h-i-1)*vert_len + j/8] &
290                         (128>>(j%8));
291                 }
292                 if (val) {
293                     bitdata[j*bit_len + i/8] = bitdata[j*bit_len + i/8] |
294                         (128>>(i%8));
295                 }
296             }
297         }
298 
299         // create this character's bitmap
300         rotfont->per_char[ichar-32].glyph.bm =
301             XCreatePixmap(dpy, rootwin, bit_w, bit_h, 1);
302 
303         // put the image into the bitmap
304         XPutImage(dpy, rotfont->per_char[ichar-32].glyph.bm,
305                   font_gc.gc(), I2, 0, 0, 0, 0, bit_w, bit_h);
306 
307         // free the image and data
308         XDestroyImage(I1);
309         XDestroyImage(I2);
310     }
311 
312 }
313 
freeRotFont(XRotFontStruct * rotfont)314 void XFontImp::freeRotFont(XRotFontStruct *rotfont) {
315     // loop through each character and free its pixmap
316     for (int ichar = rotfont->min_char - 32;
317          ichar <= rotfont->max_char - 32; ++ichar) {
318         XFreePixmap(App::instance()->display(), rotfont->per_char[ichar].glyph.bm);
319     }
320 
321     delete rotfont;
322     rotfont = 0;
323 }
324 
drawRotText(Drawable w,int screen,GC gc,const char * text,size_t len,int x,int y,FbTk::Orientation orient) const325 void XFontImp::drawRotText(Drawable w, int screen, GC gc, const char* text, size_t len, int x, int y, FbTk::Orientation orient) const {
326 
327     if (!text || !*text || len<1)
328         return;
329 
330     Display *dpy = App::instance()->display();
331     static GC my_gc = 0;
332     int xp, yp, ichar;
333 
334     XRotFontStruct *rotfont = m_rotfonts[orient];
335 
336     if (my_gc == 0)
337         my_gc = XCreateGC(dpy, w, 0, 0);
338 
339     XCopyGC(dpy, gc, GCForeground|GCBackground, my_gc);
340 
341     // vertical or upside down
342     XSetFillStyle(dpy, my_gc, FillStippled);
343 
344     // loop through each character in texting
345     for (size_t i = 0; i<len; i++) {
346         ichar = text[i]-32;
347 
348         // make sure it's a printing character
349         if (ichar >= 0 && ichar<95) {
350             // suitable offset
351             if (orient == ROT270) {
352                 xp = x-rotfont->per_char[ichar].ascent;
353                 yp = y-rotfont->per_char[ichar].rbearing;
354             } else if (orient == ROT180) {
355                 xp = x-rotfont->per_char[ichar].rbearing;
356                 yp = y-rotfont->per_char[ichar].descent+1;
357             } else { // ROT90
358                 xp = x-rotfont->per_char[ichar].descent;
359                 yp = y+rotfont->per_char[ichar].lbearing;
360             }
361 
362             // draw the glyph
363             XSetStipple(dpy, my_gc, rotfont->per_char[ichar].glyph.bm);
364 
365             XSetTSOrigin(dpy, my_gc, xp, yp);
366 
367             XFillRectangle(dpy, w, my_gc, xp, yp,
368                            rotfont->per_char[ichar].glyph.bit_w,
369                            rotfont->per_char[ichar].glyph.bit_h);
370 
371             // advance position
372             if (orient == ROT270)
373                 y -= rotfont->per_char[ichar].width;
374             else if (orient == ROT180)
375                 x -= rotfont->per_char[ichar].width;
376             else
377                 y += rotfont->per_char[ichar].width;
378         }
379     }
380 }
381 
382 
validOrientation(FbTk::Orientation orient)383 bool XFontImp::validOrientation(FbTk::Orientation orient) {
384     if (orient == ROT0 || m_rotfonts[orient])
385         return true;
386 
387     if (m_rotfonts_loaded[orient])
388         return false; // load must have failed
389 
390     m_rotfonts_loaded[orient] = true;
391     rotate(orient);
392 
393     return m_rotfonts[orient] != 0;
394 }
395 
396 }
397