1 |
/*----------------------------------------------------------------------* |
2 |
* File: rxvtfont.C |
3 |
*----------------------------------------------------------------------* |
4 |
* Copyright (c) 2003-2008 Marc Lehmann <schmorp@schmorp.de> |
5 |
* - original version. |
6 |
* |
7 |
* This program is free software; you can redistribute it and/or modify |
8 |
* it under the terms of the GNU General Public License as published by |
9 |
* the Free Software Foundation; either version 3 of the License, or |
10 |
* (at your option) any later version. |
11 |
* |
12 |
* This program is distributed in the hope that it will be useful, |
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
* GNU General Public License for more details. |
16 |
* |
17 |
* You should have received a copy of the GNU General Public License |
18 |
* along with this program; if not, write to the Free Software |
19 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 |
*---------------------------------------------------------------------*/ |
21 |
|
22 |
#include "../config.h" |
23 |
#include "rxvt.h" |
24 |
#include "rxvtutil.h" |
25 |
#include "rxvtfont.h" |
26 |
|
27 |
#include <stdlib.h> |
28 |
|
29 |
#include <inttypes.h> |
30 |
|
31 |
#if XFT |
32 |
# include <fontconfig/fontconfig.h> |
33 |
#endif |
34 |
|
35 |
#define MAX_OVERLAP_ROMAN (8 + 2) // max. character width in 8ths of the base width |
36 |
#define MAX_OVERLAP_ITALIC (8 + 3) // max. overlap for italic fonts |
37 |
|
38 |
#define OVERLAP_OK(w,wcw,prop) ((w) <= ( \ |
39 |
(prop)->slant >= rxvt_fontprop::italic \ |
40 |
? ((prop)->width * (wcw) * MAX_OVERLAP_ITALIC + 7) >> 3 \ |
41 |
: ((prop)->width * (wcw) * MAX_OVERLAP_ROMAN + 7) >> 3 \ |
42 |
)) |
43 |
|
44 |
static const struct rxvt_fallback_font { |
45 |
codeset cs; |
46 |
const char *name; |
47 |
} fallback_fonts[] = { |
48 |
{ CS_ISO8859_1, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-1" }, |
49 |
{ CS_ISO8859_15, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-15" }, |
50 |
{ CS_ISO8859_15, "-*-*-*-r-*--*-*-*-*-c-*-fcd8859-15" }, |
51 |
|
52 |
#if ENCODING_EU |
53 |
// cyrillic |
54 |
{ CS_KOI8_R, "-*-*-*-r-*--*-*-*-*-c-*-koi8-r" }, |
55 |
{ CS_KOI8_U, "-*-*-*-r-*--*-*-*-*-c-*-koi8-u" }, |
56 |
|
57 |
{ CS_ISO8859_2, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-2" }, |
58 |
{ CS_ISO8859_3, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-3" }, |
59 |
{ CS_ISO8859_4, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-4" }, |
60 |
{ CS_ISO8859_5, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-5" }, |
61 |
{ CS_ISO8859_6, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-6" }, |
62 |
{ CS_ISO8859_7, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-7" }, |
63 |
{ CS_ISO8859_8, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-8" }, |
64 |
{ CS_ISO8859_9, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-9" }, |
65 |
{ CS_ISO8859_10, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-10" }, |
66 |
{ CS_ISO8859_11, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-11" }, |
67 |
{ CS_ISO8859_13, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-13" }, |
68 |
{ CS_ISO8859_14, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-14" }, |
69 |
{ CS_ISO8859_16, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-16" }, |
70 |
|
71 |
# if XFT |
72 |
{ CS_KOI8_U, "xft::lang=ru" }, |
73 |
|
74 |
{ CS_ISO8859_5, "xft::lang=ru" }, |
75 |
{ CS_ISO8859_6, "xft::lang=ar" }, |
76 |
{ CS_ISO8859_7, "xft::lang=el" }, |
77 |
{ CS_ISO8859_8, "xft::lang=he" }, |
78 |
{ CS_ISO8859_9, "xft::lang=tr" }, |
79 |
{ CS_ISO8859_10, "xft::lang=se" }, |
80 |
{ CS_ISO8859_11, "xft::lang=th" }, |
81 |
# endif |
82 |
#endif |
83 |
|
84 |
// japanese |
85 |
#if ENCODING_JP || ENCODING_JP_EXT |
86 |
# if XFT |
87 |
// prefer xft for complex scripts |
88 |
{ CS_JIS0208_1990_0, "xft:Sazanami Mincho:antialias=false" }, |
89 |
{ CS_JIS0208_1990_0, "xft:Kochi Gothic:antialias=false" }, |
90 |
{ CS_JIS0208_1990_0, "xft:Mincho:antialias=false" }, |
91 |
{ CS_JIS0208_1990_0, "xft::lang=ja:antialias=false" }, |
92 |
# endif |
93 |
{ CS_JIS0201_1976_0, "-*-mincho-*-r-*--*-*-*-*-c-*-jisx0201*-0" }, |
94 |
{ CS_JIS0208_1990_0, "-*-mincho-*-r-*--*-*-*-*-c-*-jisx0208*-0" }, |
95 |
{ CS_JIS0212_1990_0, "-*-mincho-*-r-*--*-*-*-*-c-*-jisx0212*-0" }, |
96 |
{ CS_JIS0201_1976_0, "-*-*-*-r-*--*-*-*-*-c-*-jisx0201*-0" }, |
97 |
{ CS_JIS0208_1990_0, "-*-*-*-r-*--*-*-*-*-c-*-jisx0208*-0" }, |
98 |
{ CS_JIS0212_1990_0, "-*-*-*-r-*--*-*-*-*-c-*-jisx0212*-0" }, |
99 |
#endif |
100 |
|
101 |
#if ENCODING_ZH || ENCODING_ZH_EXT |
102 |
# if XFT |
103 |
{ CS_GBK_0, "xft:AR PL KaitiM GB" }, |
104 |
{ CS_GBK_0, "xft:AR PL SungtiL GB" }, |
105 |
{ CS_GBK_0, "xft::lang=zh" }, |
106 |
{ CS_BIG5_EXT, "xft:AR PL Mingti2L Big5" }, |
107 |
{ CS_BIG5_EXT, "xft:AR PL KaitiM Big5" }, |
108 |
{ CS_GB2312_1980_0, "xft:AR PL KaitiM GB" }, |
109 |
{ CS_GB2312_1980_0, "xft:AR PL SungtiL GB" }, |
110 |
{ CS_GB2312_1980_0, "xft::lang=zh" }, |
111 |
# endif |
112 |
{ CS_GBK_0, "-*-*-*-*-*-*-*-*-*-*-c-*-gbk*-0" }, |
113 |
{ CS_BIG5, "-*-*-*-*-*-*-*-*-*-*-c-*-big5-0" }, |
114 |
{ CS_BIG5_PLUS, "-*-*-*-*-*-*-*-*-*-*-c-*-big5p-0" }, |
115 |
{ CS_BIG5_EXT, "-*-*-*-*-*-*-*-*-*-*-c-*-big5.eten-0" }, |
116 |
{ CS_GB2312_1980_0, "-*-*-*-*-*-*-*-*-*-*-c-*-gb2312*-0" }, |
117 |
{ CS_CNS11643_1992_1, "-*-*-*-*-*-*-*-*-*-*-c-*-gb2312*-0" }, |
118 |
{ CS_CNS11643_1992_1, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-1" }, |
119 |
{ CS_CNS11643_1992_2, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-2" }, |
120 |
{ CS_CNS11643_1992_3, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-3" }, |
121 |
{ CS_CNS11643_1992_4, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-4" }, |
122 |
{ CS_CNS11643_1992_5, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-5" }, |
123 |
{ CS_CNS11643_1992_6, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-6" }, |
124 |
{ CS_CNS11643_1992_7, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-7" }, |
125 |
{ CS_CNS11643_1992_F, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-f" }, |
126 |
#endif |
127 |
|
128 |
#if ENCODING_KR |
129 |
{ CS_KSC5601_1987_0, "-baekmuk-gulim-*-*-*-*-*-*-*-*-c-*-ksc5601*" }, |
130 |
{ CS_KSC5601_1987_0, "-*-*-*-*-*-*-*-*-*-*-c-*-ksc5601*" }, |
131 |
# if XFT |
132 |
{ CS_KSC5601_1987_0, "xft:Baekmuk Gulim:antialias=false" }, |
133 |
{ CS_KSC5601_1987_0, "xft::lang=ko:antialias=false" }, |
134 |
# endif |
135 |
#endif |
136 |
|
137 |
//{ CS_UNICODE, "-*-unifont-*-*-*-*-*-*-*-*-c-*-iso10646-1" }, // this gem of a font has actual dotted circles within the combining character glyphs. |
138 |
#if XFT |
139 |
{ CS_UNICODE, "xft:DejaVu Sans Mono:antialias=false:autohint=true" }, |
140 |
{ CS_UNICODE, "xft:Courier New:antialias=false:autohint=true" }, |
141 |
{ CS_UNICODE, "xft:Andale Mono:antialias=false:autohint=false" }, |
142 |
{ CS_UNICODE, "xft:Arial Unicode MS:antialias=false:autohint=false" }, |
143 |
|
144 |
// FreeMono is usually uglier than x fonts, so try after the others |
145 |
{ CS_UNICODE, "xft:FreeMono:autohint=true" }, |
146 |
#endif |
147 |
|
148 |
// generic font fallback, put this last, as many iso10646 fonts have extents |
149 |
// specified for all glyphs in the range they cover, but most are simply empty |
150 |
//{ CS_UNICODE, "-*-*-*-r-*-*-*-*-*-*-c-*-iso10646-1" }, |
151 |
//{ CS_UNICODE, "-*-*-*-r-*-*-*-*-*-*-m-*-iso10646-1" }, |
152 |
{ CS_UNKNOWN, 0 } |
153 |
}; |
154 |
|
155 |
// these characters are used to guess the font height and width |
156 |
// pango uses a similar algorithm and doesn't trust the font either. |
157 |
static uint32_t extent_test_chars[] = { |
158 |
'0', '1', '8', 'a', 'd', 'x', 'm', 'y', 'g', 'W', 'X', '\'', '_', |
159 |
0x00cd, 0x00d5, 0x0114, 0x0177, 0x0643, // ÍÕĔŷﻙ |
160 |
0x304c, 0x672c, // が本 |
161 |
0x1f600, // 😀 |
162 |
}; |
163 |
|
164 |
#define dTermDisplay Display *disp = term->dpy |
165 |
#define dTermGC GC gc = term->gc |
166 |
|
167 |
///////////////////////////////////////////////////////////////////////////// |
168 |
|
169 |
static const char * |
170 |
enc_char (const text_t *text, uint32_t len, codeset cs, bool &zero) |
171 |
{ |
172 |
uint8_t *buf = rxvt_temp_buf<uint8_t> (len); |
173 |
uint8_t *res = buf; |
174 |
|
175 |
while (len--) |
176 |
{ |
177 |
uint32_t c = FROM_UNICODE (cs, *text++); |
178 |
|
179 |
if (c == NOCHAR) |
180 |
{ |
181 |
c = 0; |
182 |
zero = true; |
183 |
} |
184 |
|
185 |
*buf++ = c; |
186 |
} |
187 |
|
188 |
return (const char *)res; |
189 |
} |
190 |
|
191 |
static const XChar2b * |
192 |
enc_xchar2b (const text_t *text, uint32_t len, codeset cs, bool &zero) |
193 |
{ |
194 |
XChar2b *buf = rxvt_temp_buf<XChar2b> (len); |
195 |
XChar2b *res = buf; |
196 |
|
197 |
while (len--) |
198 |
{ |
199 |
uint32_t c = FROM_UNICODE (cs, *text++); |
200 |
|
201 |
if (c == NOCHAR) |
202 |
{ |
203 |
c = 0; |
204 |
zero = true; |
205 |
} |
206 |
|
207 |
buf->byte1 = c >> 8; |
208 |
buf->byte2 = c; |
209 |
buf++; |
210 |
} |
211 |
|
212 |
return res; |
213 |
} |
214 |
|
215 |
///////////////////////////////////////////////////////////////////////////// |
216 |
|
217 |
rxvt_font::rxvt_font () |
218 |
: name(0), width(rxvt_fontprop::unset), height(rxvt_fontprop::unset) |
219 |
{ |
220 |
} |
221 |
|
222 |
void |
223 |
rxvt_font::set_name (char *name_) |
224 |
{ |
225 |
if (name == name_) |
226 |
return; |
227 |
|
228 |
if (name) free (name); // let the compiler optimize |
229 |
name = name_; |
230 |
} |
231 |
|
232 |
void |
233 |
rxvt_font::clear_rect (rxvt_drawable &d, int x, int y, int w, int h, int color) const |
234 |
{ |
235 |
dTermDisplay; |
236 |
dTermGC; |
237 |
|
238 |
if (color == Color_bg || color == Color_transparent) |
239 |
XClearArea (disp, d, x, y, w, h, false); |
240 |
else if (color >= 0) |
241 |
{ |
242 |
#if XFT |
243 |
Picture dst; |
244 |
|
245 |
# ifdef HAVE_IMG |
246 |
if (term->bg_img |
247 |
&& !term->pix_colors[color].is_opaque () |
248 |
&& ((dst = XftDrawPicture (d)))) |
249 |
{ |
250 |
XClearArea (disp, d, x, y, w, h, false); |
251 |
|
252 |
Picture solid_color_pict = XftDrawSrcPicture (d, &term->pix_colors[color].c); |
253 |
XRenderComposite (disp, PictOpOver, solid_color_pict, None, dst, 0, 0, 0, 0, x, y, w, h); |
254 |
} |
255 |
else |
256 |
# endif |
257 |
XftDrawRect (d, &term->pix_colors[color].c, x, y, w, h); |
258 |
|
259 |
#else |
260 |
XSetForeground (disp, gc, term->pix_colors[color]); |
261 |
XFillRectangle (disp, d, gc, x, y, w, h); |
262 |
#endif |
263 |
} |
264 |
} |
265 |
|
266 |
///////////////////////////////////////////////////////////////////////////// |
267 |
|
268 |
struct rxvt_font_default : rxvt_font |
269 |
{ |
270 |
struct rxvt_fontset *fs; |
271 |
|
272 |
rxvt_font_default (rxvt_fontset *fs) |
273 |
: rxvt_font () |
274 |
{ |
275 |
this->fs = fs; |
276 |
} |
277 |
|
278 |
rxvt_fontprop properties () |
279 |
{ |
280 |
rxvt_fontprop p; |
281 |
|
282 |
p.width = p.height = 1; |
283 |
p.ascent = rxvt_fontprop::unset; |
284 |
p.weight = rxvt_fontprop::medium; |
285 |
p.slant = rxvt_fontprop::roman; |
286 |
|
287 |
return p; |
288 |
} |
289 |
|
290 |
bool load (const rxvt_fontprop &prop, bool force_prop) |
291 |
{ |
292 |
width = 1; height = 1; |
293 |
ascent = 1; descent = 0; |
294 |
|
295 |
set_name (strdup ("built-in support font")); |
296 |
|
297 |
return true; |
298 |
} |
299 |
|
300 |
bool has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful) const |
301 |
{ |
302 |
careful = false; |
303 |
|
304 |
if (unicode <= 0x001f) |
305 |
return true; |
306 |
|
307 |
if (unicode <= 0x007f) |
308 |
return false; |
309 |
|
310 |
if (unicode <= 0x009f) |
311 |
return true; |
312 |
|
313 |
#ifdef BUILTIN_GLYPHS |
314 |
if (unicode >= 0x2500 && unicode <= 0x259f && |
315 |
!term->option (Opt_skipBuiltinGlyphs)) |
316 |
return true; |
317 |
#endif |
318 |
|
319 |
// we do not check for IS_COMPOSE here, as this would |
320 |
// rob other fonts from taking over. |
321 |
|
322 |
switch (unicode) |
323 |
{ |
324 |
case ZERO_WIDTH_CHAR: |
325 |
case NOCHAR: |
326 |
return true; |
327 |
} |
328 |
|
329 |
return false; |
330 |
} |
331 |
|
332 |
void draw (rxvt_drawable &d, int x, int y, |
333 |
const text_t *text, int len, |
334 |
int fg, int bg); |
335 |
}; |
336 |
|
337 |
void |
338 |
rxvt_font_default::draw (rxvt_drawable &d, int x, int y, |
339 |
const text_t *text, int len, |
340 |
int fg, int bg) |
341 |
{ |
342 |
dTermDisplay; |
343 |
dTermGC; |
344 |
|
345 |
clear_rect (d, x, y, term->fwidth * len, term->fheight, bg); |
346 |
|
347 |
XSetForeground (disp, gc, term->pix_colors[fg]); |
348 |
|
349 |
while (len) |
350 |
{ |
351 |
const text_t *tp = text; |
352 |
text_t t = *tp; |
353 |
|
354 |
while (++text, --len && *text == NOCHAR) |
355 |
; |
356 |
|
357 |
#if ENABLE_COMBINING |
358 |
compose_char *cc; |
359 |
#endif |
360 |
int width = text - tp; |
361 |
int fwidth = term->fwidth * width; |
362 |
|
363 |
#ifdef BUILTIN_GLYPHS |
364 |
if (0x2500 <= t && t <= 0x259f) |
365 |
{ |
366 |
# include "table/linedraw.h" |
367 |
uint16_t offs = linedraw_offs[t - 0x2500]; |
368 |
uint32_t *a = linedraw_command + (offs >> 4); |
369 |
uint32_t *b = a + (offs & 15); |
370 |
|
371 |
int W = fwidth; |
372 |
int H = term->fheight; |
373 |
|
374 |
int x_[16]; |
375 |
int y_[16]; |
376 |
|
377 |
for (int i = 0; i <= 8; i++) |
378 |
{ |
379 |
x_[i] = x + ((W-1) * i + (i*7/8)) / 8; |
380 |
y_[i] = y + ((H-1) * i + (i*7/8)) / 8; |
381 |
} |
382 |
|
383 |
x_[10] = x + (W - 1) / 2; x_[9] = x_[10] - 1; x_[11] = x_[10] + 1; |
384 |
y_[10] = y + (H - 1) / 2; y_[9] = y_[10] - 1; y_[11] = y_[10] + 1; |
385 |
|
386 |
XGCValues gcv; |
387 |
|
388 |
gcv.cap_style = CapButt; |
389 |
gcv.line_width = 0; |
390 |
XChangeGC (disp, gc, GCLineWidth | GCCapStyle, &gcv); |
391 |
|
392 |
while (a < b) |
393 |
{ |
394 |
uint32_t command = *a++; |
395 |
|
396 |
int op = (command >> 24) & 255; |
397 |
int a = (command >> 20) & 15; |
398 |
int b = (command >> 16) & 15; |
399 |
int x1 = x_[(command >> 12) & 15]; |
400 |
int y1 = y_[(command >> 8) & 15]; |
401 |
int x2 = x_[(command >> 4) & 15]; |
402 |
int y2 = y_[(command >> 0) & 15]; |
403 |
|
404 |
switch (op) |
405 |
{ |
406 |
case 0: // line |
407 |
XDrawLine (disp, d, gc, x1, y1, x2, y2); |
408 |
break; |
409 |
|
410 |
case 1: // rectangle, possibly stippled |
411 |
if (a) |
412 |
{ |
413 |
static char bm[] = { 0,0 , 3,1 , 1,2 , 1,0 }; |
414 |
|
415 |
gcv.fill_style = FillStippled; |
416 |
gcv.stipple = XCreateBitmapFromData (disp, d, bm + a * 2, 2, 2); |
417 |
gcv.ts_x_origin = x; |
418 |
gcv.ts_y_origin = y; |
419 |
|
420 |
XChangeGC (disp, gc, |
421 |
GCFillStyle | GCStipple | GCTileStipXOrigin | GCTileStipYOrigin, |
422 |
&gcv); |
423 |
} |
424 |
|
425 |
XFillRectangle (disp, d, gc, x1, y1, x2 - x1 + 1, y2 - y1 + 1); |
426 |
|
427 |
if (a) |
428 |
{ |
429 |
XFreePixmap (disp, gcv.stipple); |
430 |
gcv.stipple = 0; |
431 |
gcv.fill_style = FillSolid; |
432 |
XChangeGC (disp, gc, GCFillStyle, &gcv); |
433 |
} |
434 |
break; |
435 |
case 2: // arc |
436 |
XDrawArc (disp, d, gc, |
437 |
x1 - W/2, y1 - H/2, W-1, H-1, |
438 |
(a - 1) * 90*64, (b - 1) * 90*64); |
439 |
break; |
440 |
} |
441 |
} |
442 |
} |
443 |
#else |
444 |
if (0) |
445 |
; |
446 |
#endif |
447 |
#if ENABLE_COMBINING |
448 |
else if (IS_COMPOSE (t) && (cc = rxvt_composite[t])) |
449 |
{ |
450 |
min_it (width, 2); // we only support wcwidth up to 2 |
451 |
text_t chrs[2]; |
452 |
chrs [1] = NOCHAR; |
453 |
|
454 |
*chrs = cc->c1; |
455 |
rxvt_font *f1 = (*fs)[fs->find_font_idx (cc->c1)]; |
456 |
f1->draw (d, x, y, chrs, width, fg, bg); |
457 |
|
458 |
if (cc->c2 != NOCHAR) |
459 |
{ |
460 |
bool careful; |
461 |
|
462 |
// prefer font of first character, for no good reasons |
463 |
*chrs = cc->c2; |
464 |
rxvt_font *f2 = (f1->has_char (cc->c2, 0, careful) && !careful) |
465 |
? f1 |
466 |
: (*fs)[fs->find_font_idx (cc->c2)]; |
467 |
|
468 |
f2->draw (d, x, y, chrs, width, fg, Color_none); |
469 |
} |
470 |
} |
471 |
#endif |
472 |
else |
473 |
switch (t) |
474 |
{ |
475 |
case '\t': |
476 |
case ZERO_WIDTH_CHAR: |
477 |
case NOCHAR: |
478 |
break; |
479 |
|
480 |
/* |
481 |
* If the base font does not support variation selectors, treat them as ZWC. |
482 |
* a point could be made to do this for all wcwidth == 0 characters, but I |
483 |
* decided against that until more data is available. |
484 |
*/ |
485 |
case 0xfe00: case 0xfe01: case 0xfe02: case 0xfe03: case 0xfe04: case 0xfe05: case 0xfe06: case 0xfe07: |
486 |
case 0xfe08: case 0xfe09: case 0xfe0a: case 0xfe0b: case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f: |
487 |
break; |
488 |
|
489 |
default: |
490 |
XDrawRectangle (disp, d, gc, x + 2, y + 2, |
491 |
fwidth - 4, term->fheight - 4); |
492 |
} |
493 |
|
494 |
x += fwidth; |
495 |
} |
496 |
} |
497 |
|
498 |
struct rxvt_font_overflow : rxvt_font |
499 |
{ |
500 |
struct rxvt_fontset *fs; |
501 |
|
502 |
rxvt_font_overflow (rxvt_fontset *fs) |
503 |
: rxvt_font () |
504 |
{ |
505 |
this->fs = fs; |
506 |
} |
507 |
|
508 |
rxvt_fontprop properties () |
509 |
{ |
510 |
rxvt_fontprop p; |
511 |
|
512 |
p.width = p.height = 1; |
513 |
p.ascent = rxvt_fontprop::unset; |
514 |
p.weight = rxvt_fontprop::medium; |
515 |
p.slant = rxvt_fontprop::roman; |
516 |
|
517 |
return p; |
518 |
} |
519 |
|
520 |
bool load (const rxvt_fontprop &prop, bool force_prop) |
521 |
{ |
522 |
width = 1; height = 1; |
523 |
ascent = 1; |
524 |
descent = (*fs)[2]->descent; |
525 |
|
526 |
set_name (strdup ("built-in rendition overflow font")); |
527 |
|
528 |
return true; |
529 |
} |
530 |
|
531 |
bool has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful) const |
532 |
{ |
533 |
return false; |
534 |
} |
535 |
|
536 |
void draw (rxvt_drawable &d, int x, int y, |
537 |
const text_t *text, int len, |
538 |
int fg, int bg) |
539 |
{ |
540 |
while (len) |
541 |
{ |
542 |
int fid = fs->find_font_idx (*text); |
543 |
int w = 1; |
544 |
while (w < len && text[w] == NOCHAR) |
545 |
w++; |
546 |
(*fs)[fid]->draw (d, x, y, text, w, fg, bg); |
547 |
text += w; |
548 |
len -= w; |
549 |
x += term->fwidth * w; |
550 |
} |
551 |
} |
552 |
}; |
553 |
|
554 |
///////////////////////////////////////////////////////////////////////////// |
555 |
|
556 |
struct rxvt_font_x11 : rxvt_font |
557 |
{ |
558 |
rxvt_font_x11 () { f = 0; } |
559 |
|
560 |
void clear (); |
561 |
|
562 |
rxvt_fontprop properties (); |
563 |
|
564 |
bool load (const rxvt_fontprop &prop, bool force_prop); |
565 |
|
566 |
bool has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful) const; |
567 |
|
568 |
void draw (rxvt_drawable &d, int x, int y, |
569 |
const text_t *text, int len, |
570 |
int fg, int bg); |
571 |
|
572 |
bool slow; // whether this is a proportional font or has other funny characteristics |
573 |
XFontStruct *f; |
574 |
bool enc2b, encm; |
575 |
|
576 |
char *get_property (XFontStruct *f, Atom property, const char *repl) const; |
577 |
bool set_properties (rxvt_fontprop &p, int height, const char *weight, const char *slant, int avgwidth); |
578 |
bool set_properties (rxvt_fontprop &p, XFontStruct *f); |
579 |
bool set_properties (rxvt_fontprop &p, const char *name); |
580 |
}; |
581 |
|
582 |
char * |
583 |
rxvt_font_x11::get_property (XFontStruct *f, Atom property, const char *repl) const |
584 |
{ |
585 |
unsigned long value; |
586 |
|
587 |
if (XGetFontProperty (f, property, &value)) |
588 |
return XGetAtomName (term->dpy, value); |
589 |
else |
590 |
return repl ? strdup (repl) : 0; |
591 |
} |
592 |
|
593 |
rxvt_fontprop |
594 |
rxvt_font_x11::properties () |
595 |
{ |
596 |
rxvt_fontprop p; |
597 |
set_properties (p, f); |
598 |
return p; |
599 |
} |
600 |
|
601 |
bool |
602 |
rxvt_font_x11::set_properties (rxvt_fontprop &p, int height, const char *weight, const char *slant, int avgwidth) |
603 |
{ |
604 |
p.width = width != rxvt_fontprop::unset ? width |
605 |
: avgwidth ? (avgwidth + 1) / 10 |
606 |
: (height + 1) / 2; |
607 |
p.height = height; |
608 |
p.ascent = rxvt_fontprop::unset; |
609 |
p.weight = *weight == 'B' || *weight == 'b' ? rxvt_fontprop::bold : rxvt_fontprop::medium; |
610 |
p.slant = *slant == 'r' || *slant == 'R' ? rxvt_fontprop::roman : rxvt_fontprop::italic; |
611 |
|
612 |
return true; |
613 |
} |
614 |
|
615 |
bool |
616 |
rxvt_font_x11::set_properties (rxvt_fontprop &p, XFontStruct *f) |
617 |
{ |
618 |
unsigned long height; |
619 |
|
620 |
#if 0 |
621 |
if (!XGetFontProperty (f, XInternAtom (term->dpy, "PIXEL_SIZE", 0), &height)) |
622 |
return false; |
623 |
#else |
624 |
height = f->ascent + f->descent; |
625 |
#endif |
626 |
|
627 |
unsigned long avgwidth; |
628 |
if (!XGetFontProperty (f, term->xa [XA_AVERAGE_WIDTH], &avgwidth)) |
629 |
avgwidth = 0; |
630 |
|
631 |
char *weight = get_property (f, term->xa [XA_WEIGHT_NAME], "medium"); |
632 |
char *slant = get_property (f, term->xa [XA_SLANT], "r"); |
633 |
|
634 |
set_properties (p, height, weight, slant, avgwidth); |
635 |
|
636 |
free (weight); |
637 |
free (slant); |
638 |
|
639 |
p.ascent = f->ascent; |
640 |
|
641 |
return true; |
642 |
} |
643 |
|
644 |
bool |
645 |
rxvt_font_x11::set_properties (rxvt_fontprop &p, const char *name) |
646 |
{ |
647 |
dTermDisplay; |
648 |
int slashes = 0; |
649 |
const char *comp[13]; |
650 |
|
651 |
for (const char *c = name; *c; c++) |
652 |
if (*c == '-') |
653 |
{ |
654 |
comp[slashes++] = c + 1; |
655 |
if (slashes >= 13) |
656 |
break; |
657 |
} |
658 |
|
659 |
/* can we short-circuit the costly XLoadQueryFont? */ |
660 |
if (slashes >= 13 |
661 |
&& (*comp[ 6] >= '1' && *comp[ 6] <= '9') |
662 |
&& (*comp[11] >= '0' && *comp[11] <= '9')) |
663 |
return set_properties (p, atoi (comp[6]), comp[2], comp[3], atoi (comp[11])); |
664 |
|
665 |
XFontStruct *f = XLoadQueryFont (disp, name); |
666 |
|
667 |
if (f) |
668 |
{ |
669 |
// the font should really exist now. if not, we have a problem |
670 |
// (e.g. if the user did xset fp rehash just when we were searching fonts). |
671 |
// in that case, just return garbage. |
672 |
bool ret = set_properties (p, f); |
673 |
XFreeFont (disp, f); |
674 |
return ret; |
675 |
} |
676 |
else |
677 |
return false; |
678 |
} |
679 |
|
680 |
// fix the size of scalable fonts |
681 |
static bool |
682 |
replace_field (char **ptr, const char *name, int index, const char old, const char *replace) |
683 |
{ |
684 |
int slashes = 0; |
685 |
const char *field, *end; |
686 |
|
687 |
for (const char *c = name; *c; c++) |
688 |
if (*c == '-') |
689 |
{ |
690 |
if (slashes == index) |
691 |
field = c + 1; |
692 |
|
693 |
if (slashes == index + 1) |
694 |
end = c; |
695 |
|
696 |
if (++slashes >= 13) |
697 |
break; |
698 |
} |
699 |
|
700 |
if (slashes >= 13 && (!old || *field == old)) |
701 |
{ |
702 |
size_t len = field - name; |
703 |
*ptr = (char *)malloc (len + strlen (replace) + strlen (end) + 1); |
704 |
memcpy (*ptr, name, len); |
705 |
strcpy (*ptr + len, replace); |
706 |
strcat (*ptr, end); |
707 |
|
708 |
return true; |
709 |
} |
710 |
else |
711 |
{ |
712 |
*ptr = strdup (name); |
713 |
|
714 |
return false; |
715 |
} |
716 |
} |
717 |
|
718 |
bool |
719 |
rxvt_font_x11::load (const rxvt_fontprop &prop, bool force_prop) |
720 |
{ |
721 |
dTermDisplay; |
722 |
|
723 |
clear (); |
724 |
|
725 |
char field_str[64]; // enough for 128 bits |
726 |
|
727 |
// first morph the font if required |
728 |
if (force_prop) |
729 |
{ |
730 |
char *fname; |
731 |
|
732 |
if (name[0] != '-') |
733 |
{ |
734 |
f = XLoadQueryFont (disp, name); |
735 |
|
736 |
if (!f) |
737 |
return false; |
738 |
|
739 |
char *new_name = get_property (f, XA_FONT, 0); |
740 |
|
741 |
if (new_name) |
742 |
set_name (new_name); |
743 |
else |
744 |
rxvt_warn ("font '%s' has no FONT property, continuing without.\n", name); |
745 |
|
746 |
XFreeFont (disp, f); |
747 |
f = 0; |
748 |
} |
749 |
|
750 |
if (prop.weight != rxvt_fontprop::unset) |
751 |
{ |
752 |
replace_field (&fname, name, 2, 0, |
753 |
prop.weight < rxvt_fontprop::bold |
754 |
? "medium" : "bold"); |
755 |
set_name (fname); |
756 |
} |
757 |
|
758 |
if (prop.slant != rxvt_fontprop::unset) |
759 |
{ |
760 |
replace_field (&fname, name, 3, 0, |
761 |
prop.slant < rxvt_fontprop::italic |
762 |
? "r" : "i"); // TODO: handle "o"blique, too |
763 |
set_name (fname); |
764 |
} |
765 |
} |
766 |
|
767 |
sprintf (field_str, "%d", prop.height == rxvt_fontprop::unset |
768 |
? 0 : prop.height); |
769 |
|
770 |
struct font_weight { |
771 |
char *name; |
772 |
int diff; |
773 |
|
774 |
void clear () |
775 |
{ |
776 |
name = 0; |
777 |
diff = 0x7fffffff; |
778 |
} |
779 |
|
780 |
font_weight () { clear (); } |
781 |
~font_weight () { free (name); } |
782 |
}; |
783 |
|
784 |
char **list; |
785 |
int count; |
786 |
list = XListFonts (disp, name, 4000, &count); |
787 |
|
788 |
set_name (0); |
789 |
|
790 |
if (!list) |
791 |
return false; |
792 |
|
793 |
font_weight *fonts = new font_weight[count]; |
794 |
|
795 |
for (int i = 0; i < count; i++) |
796 |
{ |
797 |
rxvt_fontprop p; |
798 |
char *fname; |
799 |
|
800 |
int diff = 0; |
801 |
|
802 |
if (replace_field (&fname, list[i], 6, '0', field_str)) |
803 |
diff += 10; // slightly penalize scalable fonts |
804 |
else |
805 |
{ |
806 |
free (fname); |
807 |
if (replace_field (&fname, list[i], 11, '0', "0")) |
808 |
diff += 300; // more heavily penalize what looks like scaled bitmap fonts |
809 |
} |
810 |
|
811 |
if (!set_properties (p, fname) |
812 |
// also weed out too large fonts |
813 |
|| (prop.height != rxvt_fontprop::unset |
814 |
&& p.height > prop.height)) |
815 |
{ |
816 |
free (fname); |
817 |
continue; |
818 |
} |
819 |
|
820 |
if (prop.height != rxvt_fontprop::unset) diff += (prop.height - p.height) * 128; |
821 |
if (prop.weight != rxvt_fontprop::unset) diff += abs (prop.weight - p.weight); |
822 |
if (prop.slant != rxvt_fontprop::unset) diff += abs (prop.slant - p.slant); |
823 |
//if (prop.width != rxvt_fontprop::unset) diff += abs (prop.width - p.width); |
824 |
|
825 |
fonts[i].name = fname; |
826 |
fonts[i].diff = diff; |
827 |
} |
828 |
|
829 |
XFreeFontNames (list); |
830 |
|
831 |
// this loop only iterates when the guessed font-size is too small |
832 |
for (;;) |
833 |
{ |
834 |
font_weight *best = fonts; |
835 |
|
836 |
for (font_weight *w = fonts + 1; w < fonts + count; w++) |
837 |
if (w->diff < best->diff) |
838 |
best = w; |
839 |
|
840 |
if (!best->name |
841 |
|| !(f = XLoadQueryFont (disp, best->name))) |
842 |
break; |
843 |
|
844 |
set_name (best->name); |
845 |
best->clear (); |
846 |
|
847 |
ascent = f->ascent; |
848 |
descent = f->descent; |
849 |
height = ascent + descent; |
850 |
|
851 |
if (prop.height == rxvt_fontprop::unset |
852 |
|| height <= prop.height) |
853 |
break; // font is ready for use |
854 |
|
855 |
// PIXEL_SIZE small enough, but real height too large |
856 |
clear (); |
857 |
} |
858 |
|
859 |
delete [] fonts; |
860 |
|
861 |
if (!f) |
862 |
return false; |
863 |
|
864 |
char *registry = get_property (f, term->xa [XA_CHARSET_REGISTRY], 0); |
865 |
char *encoding = get_property (f, term->xa [XA_CHARSET_ENCODING], 0); |
866 |
|
867 |
cs = CS_UNKNOWN; |
868 |
|
869 |
if (registry && encoding) |
870 |
{ |
871 |
char charset[64]; |
872 |
snprintf (charset, 64, "%s-%s", registry, encoding); |
873 |
|
874 |
cs = codeset_from_name (charset); |
875 |
|
876 |
if (cs == CS_UNKNOWN) |
877 |
rxvt_warn ("%s: cannot deduce encoding from registry/encoding properties \"%s\", ignoring font.\n", name, charset); |
878 |
} |
879 |
|
880 |
free (registry); |
881 |
free (encoding); |
882 |
|
883 |
if (cs == CS_UNKNOWN) |
884 |
{ |
885 |
char *value = get_property (f, XA_FONT, 0); |
886 |
const char *charset = value; |
887 |
|
888 |
if (!charset) |
889 |
charset = name; |
890 |
|
891 |
int count = 13; |
892 |
while (*charset) |
893 |
if (*charset++ == '-' && !--count) |
894 |
break; |
895 |
|
896 |
cs = codeset_from_name (charset); |
897 |
if (cs == CS_UNKNOWN) |
898 |
rxvt_warn ("%s: cannot deduce encoding from font name property \"%s\", ignoring font.\n", name, charset); |
899 |
|
900 |
free (value); |
901 |
} |
902 |
|
903 |
if (cs == CS_UNKNOWN) |
904 |
{ |
905 |
clear (); |
906 |
return false; |
907 |
} |
908 |
|
909 |
if (cs == CS_UNICODE) |
910 |
cs = CS_UNICODE_16; // X11 can have a max. of 65536 chars per font |
911 |
|
912 |
encm = f->min_byte1 != 0 || f->max_byte1 != 0; |
913 |
enc2b = encm || f->max_char_or_byte2 > 255; |
914 |
|
915 |
slow = false; |
916 |
|
917 |
#if 1 // only used for slow detection, TODO optimize |
918 |
if (f->min_bounds.width == f->max_bounds.width || !f->per_char) |
919 |
width = f->max_bounds.width; |
920 |
else |
921 |
{ |
922 |
slow = true; |
923 |
|
924 |
int N = f->max_char_or_byte2 - f->min_char_or_byte2; |
925 |
|
926 |
if (encm) |
927 |
N += (f->max_byte1 - f->min_byte1) |
928 |
* (f->max_char_or_byte2 - f->min_char_or_byte2 + 1); |
929 |
|
930 |
while (N) |
931 |
{ |
932 |
if (f->per_char[N].width > width) |
933 |
width = f->per_char[N].width; |
934 |
|
935 |
--N; |
936 |
} |
937 |
} |
938 |
#endif |
939 |
|
940 |
width = 1; |
941 |
|
942 |
for (uint32_t *t = extent_test_chars; t < extent_test_chars + ecb_array_length (extent_test_chars); t++) |
943 |
{ |
944 |
if (FROM_UNICODE (cs, *t) == NOCHAR) |
945 |
continue; |
946 |
|
947 |
// ignore characters we wouldn't use anyways |
948 |
bool careful; |
949 |
if (!has_char (*t, &prop, careful)) |
950 |
continue; |
951 |
|
952 |
// the casts are needed in C++11 (see 8.5.1) |
953 |
XChar2b ch = { (unsigned char)(*t >> 8), (unsigned char)*t }; |
954 |
|
955 |
XCharStruct g; |
956 |
int dir_ret, asc_ret, des_ret; |
957 |
XTextExtents16 (f, &ch, 1, &dir_ret, &asc_ret, &des_ret, &g); |
958 |
|
959 |
int wcw = WCWIDTH (*t); |
960 |
if (wcw > 0) g.width = (g.width + wcw - 1) / wcw; |
961 |
|
962 |
if (width < g.width) width = g.width; |
963 |
} |
964 |
|
965 |
#if 0 // do it per-character |
966 |
if (prop && width > prop->width) |
967 |
{ |
968 |
clear (); |
969 |
return false; |
970 |
} |
971 |
#endif |
972 |
|
973 |
return true; |
974 |
} |
975 |
|
976 |
void |
977 |
rxvt_font_x11::clear () |
978 |
{ |
979 |
if (f) |
980 |
{ |
981 |
XFreeFont (term->dpy, f); |
982 |
f = 0; |
983 |
} |
984 |
} |
985 |
|
986 |
bool |
987 |
rxvt_font_x11::has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful) const |
988 |
{ |
989 |
careful = false; |
990 |
|
991 |
uint32_t ch = FROM_UNICODE (cs, unicode); |
992 |
|
993 |
if (ch == NOCHAR) |
994 |
return false; |
995 |
|
996 |
/* check whether the character exists in _this_ font. horrible. */ |
997 |
XCharStruct *xcs; |
998 |
|
999 |
if (encm) |
1000 |
{ |
1001 |
unsigned char byte1 = ch >> 8; |
1002 |
unsigned char byte2 = ch & 255; |
1003 |
|
1004 |
if (byte1 < f->min_byte1 || byte1 > f->max_byte1 |
1005 |
|| byte2 < f->min_char_or_byte2 || byte2 > f->max_char_or_byte2) |
1006 |
return false; |
1007 |
|
1008 |
if (f->per_char) |
1009 |
{ |
1010 |
int D = f->max_char_or_byte2 - f->min_char_or_byte2 + 1; |
1011 |
int N = (byte1 - f->min_byte1) * D + byte2 - f->min_char_or_byte2; |
1012 |
|
1013 |
xcs = f->per_char + N; |
1014 |
} |
1015 |
else |
1016 |
xcs = &f->max_bounds; |
1017 |
} |
1018 |
else |
1019 |
{ |
1020 |
if (ch < f->min_char_or_byte2 || ch > f->max_char_or_byte2) |
1021 |
return false; |
1022 |
|
1023 |
if (f->per_char) |
1024 |
xcs = f->per_char + (ch - f->min_char_or_byte2); |
1025 |
else |
1026 |
xcs = &f->max_bounds; |
1027 |
} |
1028 |
|
1029 |
if (xcs->lbearing == 0 && xcs->rbearing == 0 && xcs->width == 0 |
1030 |
&& xcs->ascent == 0 && xcs->descent == 0) |
1031 |
return false; |
1032 |
|
1033 |
if (!prop || prop->width == rxvt_fontprop::unset) |
1034 |
return true; |
1035 |
|
1036 |
// check whether character overlaps previous/next character |
1037 |
int w = xcs->rbearing - xcs->lbearing; |
1038 |
int wcw = max (WCWIDTH (unicode), 1); |
1039 |
|
1040 |
careful = xcs->lbearing < 0 || xcs->rbearing > prop->width * wcw; |
1041 |
|
1042 |
if (careful && !OVERLAP_OK (w, wcw, prop)) |
1043 |
return false; |
1044 |
|
1045 |
return true; |
1046 |
} |
1047 |
|
1048 |
void |
1049 |
rxvt_font_x11::draw (rxvt_drawable &d, int x, int y, |
1050 |
const text_t *text, int len, |
1051 |
int fg, int bg) |
1052 |
{ |
1053 |
// this looks like a mess /. |
1054 |
// and it is a mess /. |
1055 |
// yet we are trying to be perfect /. |
1056 |
// but the result still isn't perfect /. |
1057 |
|
1058 |
dTermDisplay; |
1059 |
dTermGC; |
1060 |
|
1061 |
bool slow = this->slow |
1062 |
|| width != term->fwidth |
1063 |
|| height != term->fheight |
1064 |
|| ascent != f->ascent; |
1065 |
|
1066 |
int base = ascent; // sorry, incorrect: term->fbase; |
1067 |
|
1068 |
XGCValues v; |
1069 |
v.foreground = term->pix_colors[fg]; |
1070 |
v.font = f->fid; |
1071 |
|
1072 |
if (enc2b) |
1073 |
{ |
1074 |
const XChar2b *xc = enc_xchar2b (text, len, cs, slow); |
1075 |
|
1076 |
if (bg == Color_bg && !slow) |
1077 |
{ |
1078 |
v.background = term->pix_colors[bg]; |
1079 |
XChangeGC (disp, gc, GCForeground | GCBackground | GCFont, &v); |
1080 |
XDrawImageString16 (disp, d, gc, x, y + base, xc, len); |
1081 |
} |
1082 |
else |
1083 |
{ |
1084 |
clear_rect (d, x, y, term->fwidth * len, term->fheight, bg); |
1085 |
|
1086 |
XChangeGC (disp, gc, GCForeground | GCFont, &v); |
1087 |
|
1088 |
if (slow) |
1089 |
{ |
1090 |
do |
1091 |
{ |
1092 |
if (xc->byte1 || xc->byte2) |
1093 |
XDrawString16 (disp, d, gc, x, y + base, xc, 1); |
1094 |
|
1095 |
x += term->fwidth; |
1096 |
xc++; len--; |
1097 |
} |
1098 |
while (len); |
1099 |
} |
1100 |
else |
1101 |
XDrawString16 (disp, d, gc, x, y + base, xc, len); |
1102 |
} |
1103 |
} |
1104 |
else |
1105 |
{ |
1106 |
const char *xc = enc_char (text, len, cs, slow); |
1107 |
|
1108 |
if (bg == Color_bg && !slow) |
1109 |
{ |
1110 |
v.background = term->pix_colors[bg]; |
1111 |
XChangeGC (disp, gc, GCForeground | GCBackground | GCFont, &v); |
1112 |
XDrawImageString (disp, d, gc, x, y + base, xc, len); |
1113 |
} |
1114 |
else |
1115 |
{ |
1116 |
clear_rect (d, x, y, term->fwidth * len, term->fheight, bg); |
1117 |
|
1118 |
XChangeGC (disp, gc, GCForeground | GCFont, &v); |
1119 |
|
1120 |
if (slow) |
1121 |
{ |
1122 |
do |
1123 |
{ |
1124 |
if (*xc) |
1125 |
XDrawString (disp, d, gc, x, y + base, xc, 1); |
1126 |
|
1127 |
x += term->fwidth; |
1128 |
xc++; len--; |
1129 |
} |
1130 |
while (len); |
1131 |
} |
1132 |
else |
1133 |
XDrawString (disp, d, gc, x, y + base, xc, len); |
1134 |
} |
1135 |
} |
1136 |
} |
1137 |
|
1138 |
///////////////////////////////////////////////////////////////////////////// |
1139 |
|
1140 |
#if XFT |
1141 |
|
1142 |
struct rxvt_font_xft : rxvt_font |
1143 |
{ |
1144 |
#if XFT_CHAR_CACHE |
1145 |
// we cache the qascii range xoffsets in the name of speed, |
1146 |
// expecting terminals to deal mostly with these characters |
1147 |
// we also assume the xoff always fits into uint8_t, |
1148 |
// which is questionable, but let's see... |
1149 |
// also, it is uncomfortably big, due to the uints. |
1150 |
enum { char_cache_min = 0x20, char_cache_max = 0x7e }; |
1151 |
uint8_t xoff_cache [char_cache_max - char_cache_min + 1]; |
1152 |
FT_UInt glyph_cache [char_cache_max - char_cache_min + 1]; |
1153 |
#endif |
1154 |
|
1155 |
rxvt_font_xft () |
1156 |
{ |
1157 |
f = 0; |
1158 |
} |
1159 |
|
1160 |
void clear (); |
1161 |
|
1162 |
rxvt_fontprop properties (); |
1163 |
|
1164 |
bool load (const rxvt_fontprop &prop, bool force_prop); |
1165 |
|
1166 |
void draw (rxvt_drawable &d, int x, int y, |
1167 |
const text_t *text, int len, |
1168 |
int fg, int bg); |
1169 |
|
1170 |
bool has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful) const; |
1171 |
|
1172 |
protected: |
1173 |
XftFont *f; |
1174 |
}; |
1175 |
|
1176 |
void |
1177 |
rxvt_font_xft::clear () |
1178 |
{ |
1179 |
if (f) |
1180 |
{ |
1181 |
XftFontClose (term->dpy, f); |
1182 |
f = 0; |
1183 |
} |
1184 |
} |
1185 |
|
1186 |
rxvt_fontprop |
1187 |
rxvt_font_xft::properties () |
1188 |
{ |
1189 |
rxvt_fontprop p; |
1190 |
|
1191 |
FT_Face face = XftLockFace (f); |
1192 |
|
1193 |
p.width = width; |
1194 |
p.height = height; |
1195 |
p.ascent = ascent; |
1196 |
p.weight = face->style_flags & FT_STYLE_FLAG_BOLD |
1197 |
? rxvt_fontprop::bold : rxvt_fontprop::medium; |
1198 |
p.slant = face->style_flags & FT_STYLE_FLAG_ITALIC |
1199 |
? rxvt_fontprop::italic : rxvt_fontprop::roman; |
1200 |
|
1201 |
XftUnlockFace (f); |
1202 |
|
1203 |
return p; |
1204 |
} |
1205 |
|
1206 |
bool |
1207 |
rxvt_font_xft::load (const rxvt_fontprop &prop, bool force_prop) |
1208 |
{ |
1209 |
dTermDisplay; |
1210 |
|
1211 |
clear (); |
1212 |
|
1213 |
FcPattern *p = FcNameParse ((FcChar8 *) name); |
1214 |
|
1215 |
if (!p) |
1216 |
return false; |
1217 |
|
1218 |
FcValue v; |
1219 |
|
1220 |
if (prop.height != rxvt_fontprop::unset |
1221 |
&& (FcPatternGet (p, FC_PIXEL_SIZE, 0, &v) != FcResultMatch |
1222 |
&& FcPatternGet (p, FC_SIZE, 0, &v) != FcResultMatch)) |
1223 |
FcPatternAddInteger (p, FC_PIXEL_SIZE, prop.height); |
1224 |
|
1225 |
if (prop.weight != rxvt_fontprop::unset |
1226 |
&& (force_prop || FcPatternGet (p, FC_WEIGHT, 0, &v) != FcResultMatch)) |
1227 |
FcPatternAddInteger (p, FC_WEIGHT, prop.weight); |
1228 |
|
1229 |
if (prop.slant != rxvt_fontprop::unset |
1230 |
&& (force_prop || FcPatternGet (p, FC_SLANT, 0, &v) != FcResultMatch)) |
1231 |
FcPatternAddInteger (p, FC_SLANT, prop.slant); |
1232 |
|
1233 |
#if 0 // clipping unfortunately destroys our precious double-width-characters |
1234 |
// clip width, we can't do better, or can we? |
1235 |
if (FcPatternGet (p, FC_CHAR_WIDTH, 0, &v) != FcResultMatch) |
1236 |
FcPatternAddInteger (p, FC_CHAR_WIDTH, prop.width); |
1237 |
#endif |
1238 |
|
1239 |
if (FcPatternGet (p, FC_MINSPACE, 0, &v) != FcResultMatch) |
1240 |
FcPatternAddBool (p, FC_MINSPACE, 1); |
1241 |
|
1242 |
// store generated name so iso14755 view gives better results |
1243 |
set_name ((char *)FcNameUnparse (p)); |
1244 |
|
1245 |
XftResult result; |
1246 |
FcPattern *match = XftFontMatch (disp, term->display->screen, p, &result); |
1247 |
|
1248 |
FcPatternDestroy (p); |
1249 |
|
1250 |
if (!match) |
1251 |
return false; |
1252 |
|
1253 |
int ftheight = 0; |
1254 |
bool success = true; |
1255 |
|
1256 |
for (;;) |
1257 |
{ |
1258 |
p = FcPatternDuplicate (match); |
1259 |
f = XftFontOpenPattern (disp, p); |
1260 |
|
1261 |
if (!f) |
1262 |
{ |
1263 |
FcPatternDestroy (p); |
1264 |
success = false; |
1265 |
break; |
1266 |
} |
1267 |
|
1268 |
FT_Face face = XftLockFace (f); |
1269 |
|
1270 |
// fuck me plenty: XftLockFace can actually return 0. try not to crash. |
1271 |
// we also assume blindly that if the first lock succeeds, then subsequent |
1272 |
// locks will also succeed. |
1273 |
if (!face) |
1274 |
{ |
1275 |
XftFontClose (disp, f); |
1276 |
success = false; |
1277 |
break; |
1278 |
} |
1279 |
|
1280 |
ascent = (face->size->metrics.ascender + 63) >> 6; |
1281 |
descent = (-face->size->metrics.descender + 63) >> 6; |
1282 |
height = max (ascent + descent, (face->size->metrics.height + 63) >> 6); |
1283 |
width = 0; |
1284 |
|
1285 |
bool scalable = face->face_flags & FT_FACE_FLAG_SCALABLE; |
1286 |
|
1287 |
XftUnlockFace (f); |
1288 |
|
1289 |
int glheight = height; |
1290 |
|
1291 |
for (uint32_t *t = extent_test_chars; t < extent_test_chars + ecb_array_length (extent_test_chars); t++) |
1292 |
{ |
1293 |
FcChar32 ch = *t; |
1294 |
|
1295 |
if (cs != CS_UNICODE |
1296 |
&& ch > 0x100 |
1297 |
&& FROM_UNICODE (cs, ch) == NOCHAR) |
1298 |
continue; |
1299 |
|
1300 |
// ignore characters we wouldn't use anyways |
1301 |
bool careful; |
1302 |
if (!has_char (*t, &prop, careful)) |
1303 |
continue; |
1304 |
|
1305 |
XGlyphInfo g; |
1306 |
XftTextExtents32 (disp, f, &ch, 1, &g); |
1307 |
|
1308 |
g.width -= g.x; |
1309 |
|
1310 |
int wcw = WCWIDTH (ch); |
1311 |
if (wcw > 0) g.width = (g.width + wcw - 1) / wcw; |
1312 |
|
1313 |
if (width < g.width ) width = g.width; |
1314 |
if (height < g.height ) height = g.height; |
1315 |
if (glheight < g.height - g.y) glheight = g.height - g.y; |
1316 |
} |
1317 |
|
1318 |
if (!width) |
1319 |
{ |
1320 |
rxvt_warn ("unable to calculate font width for '%s', using max_advance_width.\n", name); |
1321 |
width = f->max_advance_width; |
1322 |
break; |
1323 |
} |
1324 |
|
1325 |
if (prop.height == rxvt_fontprop::unset |
1326 |
|| (height <= prop.height && glheight <= prop.height) |
1327 |
|| height <= 2 |
1328 |
|| !scalable) |
1329 |
break; |
1330 |
|
1331 |
if (ftheight) |
1332 |
{ |
1333 |
// take smaller steps near the end |
1334 |
if (height > prop.height + 1) ftheight++; |
1335 |
if (height > prop.height + 2) ftheight++; |
1336 |
if (height > prop.height + 3) ftheight++; |
1337 |
|
1338 |
ftheight -= height - prop.height; |
1339 |
} |
1340 |
else |
1341 |
ftheight = prop.height - 1; |
1342 |
|
1343 |
XftFontClose (disp, f); |
1344 |
FcPatternDel (match, FC_PIXEL_SIZE); |
1345 |
FcPatternAddInteger (match, FC_PIXEL_SIZE, ftheight); |
1346 |
} |
1347 |
|
1348 |
FcPatternDestroy (match); |
1349 |
|
1350 |
#if 0 // do it per-character |
1351 |
if (prop.width != rxvt_fontprop::unset && width > prop.width) |
1352 |
{ |
1353 |
clear (); |
1354 |
success = false; |
1355 |
} |
1356 |
#endif |
1357 |
|
1358 |
#if XFT_CHAR_CACHE |
1359 |
// populate char cache |
1360 |
for (FcChar16 ch = char_cache_min; ch <= char_cache_max; ++ch) |
1361 |
{ |
1362 |
FT_UInt glyph = XftCharIndex (disp, f, ch); |
1363 |
glyph_cache [ch - char_cache_min] = glyph; |
1364 |
|
1365 |
XGlyphInfo g; |
1366 |
XftGlyphExtents (disp, f, &glyph, 1, &g); |
1367 |
xoff_cache [ch - char_cache_min] = g.xOff; |
1368 |
} |
1369 |
#endif |
1370 |
|
1371 |
return success; |
1372 |
} |
1373 |
|
1374 |
bool |
1375 |
rxvt_font_xft::has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful) const |
1376 |
{ |
1377 |
careful = false; |
1378 |
|
1379 |
// handle non-bmp chars when text_t is 16 bit |
1380 |
#if ENABLE_COMBINING && !UNICODE_3 |
1381 |
if (ecb_expect_false (IS_COMPOSE (unicode))) |
1382 |
if (compose_char *cc = rxvt_composite [unicode]) |
1383 |
if (cc->c2 == NOCHAR) |
1384 |
unicode = cc->c1; |
1385 |
else |
1386 |
return false; |
1387 |
else |
1388 |
return false; |
1389 |
#endif |
1390 |
|
1391 |
FcChar32 chr = unicode; |
1392 |
|
1393 |
if (!XftCharExists (term->dpy, f, chr)) |
1394 |
return false; |
1395 |
|
1396 |
if (!prop || prop->width == rxvt_fontprop::unset) |
1397 |
return true; |
1398 |
|
1399 |
int wcw = max (WCWIDTH (chr), 1); |
1400 |
|
1401 |
XGlyphInfo g; |
1402 |
XftTextExtents32 (term->dpy, f, &chr, 1, &g); |
1403 |
|
1404 |
int cwidth = prop->width * wcw; |
1405 |
|
1406 |
// use same adjustments as in ->draw, see there |
1407 |
g.x += g.xOff ? cwidth - g.xOff >> 1 : 0; |
1408 |
g.x += g.xOff ? 0 : cwidth; |
1409 |
|
1410 |
int w = g.width - g.x; |
1411 |
|
1412 |
careful = g.x > 0 || w > cwidth; |
1413 |
|
1414 |
if (careful && !OVERLAP_OK (w, wcw, prop)) |
1415 |
return false; |
1416 |
|
1417 |
// this weeds out _totally_ broken fonts, or glyphs |
1418 |
if (!OVERLAP_OK (g.xOff, wcw, prop)) |
1419 |
return false; |
1420 |
|
1421 |
return true; |
1422 |
} |
1423 |
|
1424 |
void |
1425 |
rxvt_font_xft::draw (rxvt_drawable &d, int x, int y, |
1426 |
const text_t *text, int len, |
1427 |
int fg, int bg) |
1428 |
{ |
1429 |
XftGlyphSpec *enc = rxvt_temp_buf<XftGlyphSpec> (len); |
1430 |
XftGlyphSpec *ep = enc; |
1431 |
|
1432 |
dTermDisplay; |
1433 |
dTermGC; |
1434 |
|
1435 |
int w = term->fwidth * len; |
1436 |
int h = term->fheight; |
1437 |
|
1438 |
bool buffered = bg >= Color_transparent |
1439 |
&& term->option (Opt_buffered); |
1440 |
|
1441 |
// cut trailing spaces |
1442 |
while (len && text [len - 1] == ' ') |
1443 |
len--; |
1444 |
|
1445 |
int x_ = buffered ? 0 : x; |
1446 |
int y_ = buffered ? 0 : y; |
1447 |
|
1448 |
while (len) |
1449 |
{ |
1450 |
int cwidth = term->fwidth; |
1451 |
FcChar32 chr = *text++; len--; |
1452 |
|
1453 |
while (len && *text == NOCHAR) |
1454 |
text++, len--, cwidth += term->fwidth; |
1455 |
|
1456 |
if (chr != ' ') // skip spaces |
1457 |
{ |
1458 |
// handle non-bmp chars when text_t is 16 bit |
1459 |
#if ENABLE_COMBINING && !UNICODE_3 |
1460 |
if (ecb_expect_false (IS_COMPOSE (chr))) |
1461 |
if (compose_char *cc = rxvt_composite [chr]) |
1462 |
if (cc->c2 == NOCHAR) |
1463 |
chr = cc->c1; |
1464 |
#endif |
1465 |
|
1466 |
#if 0 |
1467 |
FT_UInt glyphs [decltype (exp)::max_size]; |
1468 |
|
1469 |
for (int i = 0; i < nchrs; ++i) |
1470 |
glyphs [i] = XftCharIndex (disp, f, chrs [i]); |
1471 |
|
1472 |
for (int i = 0; i < nchrs; ++i) |
1473 |
{ |
1474 |
XGlyphInfo ep; |
1475 |
XftGlyphExtents (disp, f, glyphs+i, 1, &ep); |
1476 |
printf ("gs %4x g %4x + %3d,%3d o %3d,%3d wh %3d,%3d\n", chrs[i],glyphs[i],ep.x,ep.y,ep.xOff,ep.yOff,ep.width,ep.height); |
1477 |
} |
1478 |
#endif |
1479 |
|
1480 |
FT_UInt glyph; |
1481 |
int xOff; |
1482 |
|
1483 |
#if XFT_CHAR_CACHE |
1484 |
if (ecb_expect_true (IN_RANGE_INC (chr, char_cache_min, char_cache_max))) |
1485 |
{ |
1486 |
glyph = glyph_cache [chr - char_cache_min]; |
1487 |
xOff = xoff_cache [chr - char_cache_min]; |
1488 |
} |
1489 |
else |
1490 |
#endif |
1491 |
{ |
1492 |
glyph = XftCharIndex (disp, f, chr); |
1493 |
XGlyphInfo extents; |
1494 |
XftGlyphExtents (disp, f, &glyph, 1, &extents); |
1495 |
xOff = extents.xOff; |
1496 |
} |
1497 |
|
1498 |
ep->glyph = glyph; |
1499 |
ep->x = x_; |
1500 |
ep->y = y_ + ascent; |
1501 |
|
1502 |
// the xft font cell might differ from the terminal font cell, |
1503 |
// in which case we use the average between the two. |
1504 |
ep->x += xOff ? cwidth - xOff >> 1 : 0; |
1505 |
|
1506 |
// xft/freetype represent combining characters as characters with zero |
1507 |
// width rendered over the previous character with some fonts, while |
1508 |
// in other fonts, they are considered normal characters, while yet |
1509 |
// in other fonts, they are shifted all over the place. |
1510 |
// we handle the first two cases by keying off on xOff being 0 |
1511 |
// for zero-width chars. normally, we would add extents.xOff |
1512 |
// of the base character here, but we don't have that, so we use cwidth. |
1513 |
ep->x += xOff ? 0 : cwidth; |
1514 |
|
1515 |
++ep; |
1516 |
} |
1517 |
|
1518 |
x_ += cwidth; |
1519 |
} |
1520 |
|
1521 |
if (buffered) |
1522 |
{ |
1523 |
if (ep != enc) |
1524 |
{ |
1525 |
rxvt_drawable &d2 = d.screen->scratch_drawable (w, h); |
1526 |
|
1527 |
#ifdef HAVE_IMG |
1528 |
Picture dst = 0; // the only assignment is done conditionally in the following if condition |
1529 |
|
1530 |
if (term->bg_img |
1531 |
&& (bg == Color_transparent || bg == Color_bg |
1532 |
|| (bg >= 0 && !term->pix_colors[bg].is_opaque () && ((dst = XftDrawPicture (d2)))))) |
1533 |
{ |
1534 |
int src_x = x, src_y = y; |
1535 |
|
1536 |
if (term->bg_flags & rxvt_term::BG_IS_TRANSPARENT) |
1537 |
{ |
1538 |
src_x += term->window_vt_x; |
1539 |
src_y += term->window_vt_y; |
1540 |
} |
1541 |
|
1542 |
if (term->bg_img->w >= src_x + w |
1543 |
&& term->bg_img->h >= src_y + h) |
1544 |
{ |
1545 |
XCopyArea (disp, term->bg_img->pm, d2, gc, |
1546 |
src_x, src_y, w, h, 0, 0); |
1547 |
} |
1548 |
else |
1549 |
{ |
1550 |
XGCValues gcv; |
1551 |
|
1552 |
gcv.fill_style = FillTiled; |
1553 |
gcv.tile = term->bg_img->pm; |
1554 |
gcv.ts_x_origin = -src_x; |
1555 |
gcv.ts_y_origin = -src_y; |
1556 |
|
1557 |
XChangeGC (disp, gc, |
1558 |
GCTile | GCTileStipXOrigin | GCTileStipYOrigin | GCFillStyle, |
1559 |
&gcv); |
1560 |
|
1561 |
XFillRectangle (disp, d2, gc, 0, 0, w, h); |
1562 |
|
1563 |
gcv.fill_style = FillSolid; |
1564 |
XChangeGC (disp, gc, GCFillStyle, &gcv); |
1565 |
} |
1566 |
|
1567 |
if (dst) |
1568 |
{ |
1569 |
Picture solid_color_pict = XftDrawSrcPicture (d2, &term->pix_colors[bg].c); |
1570 |
|
1571 |
// dst can only be set when bg >= 0 |
1572 |
XRenderComposite (disp, PictOpOver, solid_color_pict, None, dst, 0, 0, 0, 0, 0, 0, w, h); |
1573 |
} |
1574 |
} |
1575 |
else |
1576 |
#endif |
1577 |
XftDrawRect (d2, &term->pix_colors[bg >= 0 ? bg : Color_bg].c, 0, 0, w, h); |
1578 |
|
1579 |
XftDrawGlyphSpec (d2, &term->pix_colors[fg].c, f, enc, ep - enc); |
1580 |
XCopyArea (disp, d2, d, gc, 0, 0, w, h, x, y); |
1581 |
} |
1582 |
else |
1583 |
clear_rect (d, x, y, w, h, bg); |
1584 |
} |
1585 |
else |
1586 |
{ |
1587 |
clear_rect (d, x, y, w, h, bg); |
1588 |
XftDrawGlyphSpec (d, &term->pix_colors[fg].c, f, enc, ep - enc); |
1589 |
} |
1590 |
} |
1591 |
|
1592 |
#endif |
1593 |
|
1594 |
///////////////////////////////////////////////////////////////////////////// |
1595 |
|
1596 |
rxvt_fontset::rxvt_fontset (rxvt_term *term) |
1597 |
: fontdesc (0), term (term) |
1598 |
{ |
1599 |
clear (); |
1600 |
} |
1601 |
|
1602 |
rxvt_fontset::~rxvt_fontset () |
1603 |
{ |
1604 |
clear (); |
1605 |
} |
1606 |
|
1607 |
void |
1608 |
rxvt_fontset::clear () |
1609 |
{ |
1610 |
prop.width = prop.height = prop.ascent = prop.weight = prop.slant |
1611 |
= rxvt_fontprop::unset; |
1612 |
force_prop = false; |
1613 |
|
1614 |
for (rxvt_font **i = fonts.begin (); i != fonts.end (); i++) |
1615 |
(*i)->unref (); |
1616 |
|
1617 |
for (pagemap **p = fmap.begin (); p != fmap.end (); p++) |
1618 |
delete *p; |
1619 |
|
1620 |
free (fontdesc); fontdesc = 0; |
1621 |
|
1622 |
fonts.clear (); |
1623 |
|
1624 |
fallback = fallback_fonts; |
1625 |
} |
1626 |
|
1627 |
void |
1628 |
rxvt_fontset::prepare_font (rxvt_font *font, codeset cs) |
1629 |
{ |
1630 |
font->set_term (term); |
1631 |
|
1632 |
font->cs = cs; |
1633 |
font->loaded = false; |
1634 |
} |
1635 |
|
1636 |
rxvt_font * |
1637 |
rxvt_fontset::new_font (const char *name, codeset cs) |
1638 |
{ |
1639 |
rxvt_font *f; |
1640 |
|
1641 |
if (!name || !*name) |
1642 |
{ |
1643 |
name = ""; |
1644 |
f = new rxvt_font_default (this); |
1645 |
} |
1646 |
#if XFT |
1647 |
else if (!strncmp (name, "xft:", 4)) |
1648 |
{ |
1649 |
name += 4; |
1650 |
f = new rxvt_font_xft (); |
1651 |
} |
1652 |
#endif |
1653 |
else if (!strncmp (name, "x:", 2)) |
1654 |
{ |
1655 |
name += 2; |
1656 |
f = new rxvt_font_x11; |
1657 |
} |
1658 |
else |
1659 |
f = new rxvt_font_x11; |
1660 |
|
1661 |
f->set_name (strdup (name)); |
1662 |
prepare_font (f, cs); |
1663 |
|
1664 |
return f; |
1665 |
} |
1666 |
|
1667 |
///////////////////////////////////////////////////////////////////////////// |
1668 |
|
1669 |
void |
1670 |
rxvt_fontset::push_font (rxvt_font *font) |
1671 |
{ |
1672 |
// the fontCount index is reserved for the overflow font, it is only |
1673 |
// necessary when we get fontCount or more fonts, as they cannot be |
1674 |
// represented in the rendition. |
1675 |
if (fonts.size () == fontCount) |
1676 |
{ |
1677 |
rxvt_font *f = new rxvt_font_overflow (this); |
1678 |
|
1679 |
prepare_font (f, CS_UNICODE); |
1680 |
fonts.push_back (f); |
1681 |
} |
1682 |
|
1683 |
fonts.push_back (font); |
1684 |
} |
1685 |
|
1686 |
void |
1687 |
rxvt_fontset::add_fonts (const char *desc) |
1688 |
{ |
1689 |
if (desc) |
1690 |
{ |
1691 |
char buf[512]; |
1692 |
const char *end; |
1693 |
|
1694 |
do |
1695 |
{ |
1696 |
while (*desc && *desc <= ' ') |
1697 |
desc++; |
1698 |
|
1699 |
codeset cs = CS_UNICODE; |
1700 |
|
1701 |
if (*desc == '[') |
1702 |
{ |
1703 |
char spec[256]; |
1704 |
const char *extra = ++desc; // not yet used |
1705 |
|
1706 |
desc = strchr (desc, ']'); |
1707 |
|
1708 |
if (!desc) |
1709 |
{ |
1710 |
rxvt_warn ("ERROR: opening '[' without closing ']' in font specification, trying to continue.\n"); |
1711 |
break; |
1712 |
} |
1713 |
|
1714 |
memcpy (spec, extra, min (desc - extra, 255)); |
1715 |
spec[min (desc - extra, 255)] = 0; |
1716 |
|
1717 |
if (!strncmp (extra, "codeset=", sizeof ("codeset=") - 1)) |
1718 |
cs = codeset_from_name (spec + sizeof ("codeset=") - 1); |
1719 |
else |
1720 |
rxvt_warn ("unknown parameter '%s' in font specification, skipping.\n", spec); |
1721 |
|
1722 |
desc++; |
1723 |
while (*desc <= ' ' && *desc) desc++; |
1724 |
} |
1725 |
|
1726 |
end = strchr (desc, ','); |
1727 |
if (!end) |
1728 |
end = desc + strlen (desc); |
1729 |
|
1730 |
if (end - desc < 511) |
1731 |
{ |
1732 |
memcpy (buf, desc, end - desc); |
1733 |
buf[end - desc] = 0; |
1734 |
|
1735 |
push_font (new_font (buf, cs)); |
1736 |
} |
1737 |
else |
1738 |
rxvt_warn ("fontset element too long (>511 bytes), ignored.\n"); |
1739 |
|
1740 |
desc = end + 1; |
1741 |
} |
1742 |
while (*end); |
1743 |
} |
1744 |
} |
1745 |
|
1746 |
bool |
1747 |
rxvt_fontset::realize_font (int i) |
1748 |
{ |
1749 |
if (i < 0 || i >= fonts.size ()) |
1750 |
return false; |
1751 |
|
1752 |
if (fonts[i]->loaded) |
1753 |
return true; |
1754 |
|
1755 |
fonts[i]->loaded = true; |
1756 |
|
1757 |
if (!fonts[i]->load (prop, force_prop)) |
1758 |
{ |
1759 |
fonts[i]->cs = CS_UNKNOWN; |
1760 |
return false; |
1761 |
} |
1762 |
|
1763 |
return true; |
1764 |
} |
1765 |
|
1766 |
bool |
1767 |
rxvt_fontset::populate (const char *desc) |
1768 |
{ |
1769 |
clear (); |
1770 |
|
1771 |
fontdesc = strdup (desc); |
1772 |
|
1773 |
push_font (new_font (0, CS_UNICODE)); |
1774 |
realize_font (0); |
1775 |
|
1776 |
add_fonts (desc); |
1777 |
|
1778 |
return true; |
1779 |
} |
1780 |
|
1781 |
int |
1782 |
rxvt_fontset::find_font (const char *name) const |
1783 |
{ |
1784 |
for (rxvt_font *const *f = fonts.begin (); f < fonts.end (); f++) |
1785 |
if ((*f)->name && !strcmp ((*f)->name, name)) |
1786 |
return f - fonts.begin (); |
1787 |
|
1788 |
return -1; |
1789 |
} |
1790 |
|
1791 |
int |
1792 |
rxvt_fontset::find_font_idx (unicode_t unicode) |
1793 |
{ |
1794 |
// this limits fmap size. it has to accommodate COMPOSE_HI when UNICODE_3 |
1795 |
if (unicode > 0x1fffff) |
1796 |
return 0; |
1797 |
|
1798 |
unicode_t hi = unicode >> 8; |
1799 |
|
1800 |
if (hi < fmap.size ()) |
1801 |
if (pagemap *pm = fmap[hi]) |
1802 |
if ((*pm)[unicode & 0xff] != 0xff) |
1803 |
return (*pm)[unicode & 0xff]; |
1804 |
|
1805 |
unsigned int i; |
1806 |
|
1807 |
for (i = 0; i < fonts.size (); i++) |
1808 |
{ |
1809 |
rxvt_font *f = fonts[i]; |
1810 |
|
1811 |
if (!f->loaded) |
1812 |
{ |
1813 |
if (FROM_UNICODE (f->cs, unicode) == NOCHAR) |
1814 |
goto next_font; |
1815 |
|
1816 |
if (!realize_font (i)) |
1817 |
goto next_font; |
1818 |
|
1819 |
if (prop.ascent != rxvt_fontprop::unset) |
1820 |
max_it (f->ascent, prop.ascent); |
1821 |
} |
1822 |
|
1823 |
if (f->cs == CS_UNKNOWN) |
1824 |
goto next_font; |
1825 |
|
1826 |
bool careful; |
1827 |
if (f->has_char (unicode, &prop, careful)) |
1828 |
{ |
1829 |
i = (i << 1) | careful; |
1830 |
|
1831 |
goto found; |
1832 |
} |
1833 |
|
1834 |
next_font: |
1835 |
if (i == fonts.size () - 1) |
1836 |
{ |
1837 |
// compose characters are handled by the default font, unless another font takes over |
1838 |
// we do not go via the fallback list for speed reasons. |
1839 |
if (IS_COMPOSE (unicode)) |
1840 |
return 0; |
1841 |
|
1842 |
if (fallback->name) |
1843 |
{ |
1844 |
// search through the fallback list |
1845 |
push_font (new_font (fallback->name, fallback->cs)); |
1846 |
fallback++; |
1847 |
} |
1848 |
else |
1849 |
{ |
1850 |
// try to find a new font. |
1851 |
// only xft currently supported, as there is no |
1852 |
// way to configure this and xft is easier to hack in, |
1853 |
// while x11 has more framework in place already. |
1854 |
// TODO: this is a real resource hog, xft takes ages(?) |
1855 |
#if XFT && USE_SLOW_LOOKUP |
1856 |
// grab the first xft font that seems suitable |
1857 |
// TOOD: should go first for cellchar (spacing 110) then mono, then else |
1858 |
FcPattern *p = FcPatternCreate (); |
1859 |
|
1860 |
FcCharSet *s = FcCharSetCreate (); |
1861 |
FcCharSetAddChar (s, unicode); |
1862 |
FcPatternAddCharSet (p, FC_CHARSET, s); |
1863 |
// charsets don't help that much, as xft might return |
1864 |
// a non-matching font even if a better font is available :/ |
1865 |
|
1866 |
x x x x TODO prop might have unset contents |
1867 |
FcPatternAddInteger (p, FC_PIXEL_SIZE, prop.height); |
1868 |
FcPatternAddInteger (p, FC_WEIGHT, prop.weight); |
1869 |
FcPatternAddInteger (p, FC_SLANT, prop.slant); |
1870 |
FcPatternAddBool (p, FC_MINSPACE, 1); |
1871 |
//FcPatternAddBool (p, FC_ANTIALIAS, 1); |
1872 |
|
1873 |
XftResult result; |
1874 |
FcPattern *match = XftFontMatch (term->dpy, term->display->screen, p, &result); |
1875 |
|
1876 |
FcPatternDestroy (p); |
1877 |
|
1878 |
if (match) |
1879 |
{ |
1880 |
FcPatternDel (match, FC_CHARSET); |
1881 |
char *font = (char *)FcNameUnparse (match); |
1882 |
FcPatternDestroy (match); |
1883 |
|
1884 |
if (find_font (font) < 0) |
1885 |
{ |
1886 |
char fontname[4096]; |
1887 |
snprintf (fontname, sizeof (fontname), "xft:%s", font); |
1888 |
|
1889 |
push_font (new_font (fontname, CS_UNICODE)); |
1890 |
} |
1891 |
|
1892 |
free (font); |
1893 |
} |
1894 |
#endif |
1895 |
} |
1896 |
} |
1897 |
} |
1898 |
|
1899 |
/* we must return SOME font */ |
1900 |
i = 0; |
1901 |
|
1902 |
found: |
1903 |
// found a font, cache it |
1904 |
if (i < 255) |
1905 |
{ |
1906 |
while (hi >= fmap.size ()) |
1907 |
fmap.push_back (0); |
1908 |
|
1909 |
if (!fmap[hi]) |
1910 |
{ |
1911 |
fmap[hi] = new pagemap; |
1912 |
memset (fmap[hi], 0xff, sizeof (pagemap)); |
1913 |
} |
1914 |
|
1915 |
(*fmap[hi])[unicode & 0xff] = i; |
1916 |
} |
1917 |
|
1918 |
return i; |
1919 |
} |
1920 |
|