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