ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/rxvtfont.C
Revision: 1.81
Committed: Mon Jan 9 07:19:18 2006 UTC (18 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.80: +2 -4 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 /*--------------------------------*-C-*---------------------------------*
2 root 1.66 * File: rxvtfont.C
3 root 1.1 *----------------------------------------------------------------------*
4 root 1.78 * Copyright (c) 2003-2006 Marc Lehmann <pcg@goof.com>
5 root 1.1 * - 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 2 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 root 1.70 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 root 1.1 *---------------------------------------------------------------------*/
21    
22     #include "../config.h"
23     #include "rxvt.h"
24 root 1.77 #include "rxvtlib.h"
25 root 1.41 #include "rxvtutil.h"
26 root 1.1 #include "rxvtfont.h"
27    
28     #include <cstdlib>
29 root 1.41 #include <wchar.h>
30     #include <inttypes.h>
31 root 1.1
32     #define DISPLAY r->display->display
33 root 1.75 #define TGC r->gc
34 root 1.1
35 root 1.41 #define MAX_OVERLAP (4 + 1) // max. character width in 4ths of the base width
36    
37 root 1.1 const struct rxvt_fallback_font {
38     codeset cs;
39     const char *name;
40     } fallback_fonts[] = {
41     { CS_ISO8859_1, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-1" },
42     { CS_ISO8859_15, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-15" },
43     { CS_ISO8859_15, "-*-*-*-r-*--*-*-*-*-c-*-fcd8859-15" },
44    
45     #if ENCODING_EU
46     // cyrillic
47     { CS_KOI8_R, "-*-*-*-r-*--*-*-*-*-c-*-koi8-r" },
48     { CS_KOI8_U, "-*-*-*-r-*--*-*-*-*-c-*-koi8-u" },
49    
50     { CS_ISO8859_2, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-2" },
51     { CS_ISO8859_3, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-3" },
52     { CS_ISO8859_4, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-4" },
53     { CS_ISO8859_5, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-5" },
54     { CS_ISO8859_6, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-6" },
55     { CS_ISO8859_7, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-7" },
56     { CS_ISO8859_8, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-8" },
57     { CS_ISO8859_9, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-9" },
58     { CS_ISO8859_10, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-10" },
59     { CS_ISO8859_11, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-11" },
60     { CS_ISO8859_13, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-13" },
61     { CS_ISO8859_14, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-14" },
62     { CS_ISO8859_16, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-16" },
63 root 1.41
64     # if XFT
65     { CS_KOI8_U, "xft::spacing=100:lang=ru:antialias=false" },
66    
67     { CS_ISO8859_5, "xft::spacing=100:lang=ru:antialias=false" },
68     { CS_ISO8859_6, "xft::spacing=100:lang=ar:antialias=false" },
69     { CS_ISO8859_7, "xft::spacing=100:lang=el:antialias=false" },
70     { CS_ISO8859_8, "xft::spacing=100:lang=he:antialias=false" },
71     { CS_ISO8859_9, "xft::spacing=100:lang=tr:antialias=false" },
72     { CS_ISO8859_10, "xft::spacing=100:lang=se:antialias=false" },
73     { CS_ISO8859_11, "xft::spacing=100:lang=th:antialias=false" },
74     # endif
75 root 1.1 #endif
76    
77     // japanese
78     #if ENCODING_JP || ENCODING_JP_EXT
79     # if XFT
80     // prefer xft for complex scripts
81 root 1.41 { CS_JIS0208_1990_0, "xft:Kochi Gothic:antialias=false" },
82     { CS_JIS0208_1990_0, "xft:Sazanami Mincho:antialias=false" },
83     { CS_JIS0208_1990_0, "xft:Mincho:antialias=false" },
84     { CS_JIS0208_1990_0, "xft::lang=ja:spacing=100:antialias=false" },
85 root 1.1 # endif
86     { CS_JIS0201_1976_0, "-*-mincho-*-r-*--*-*-*-*-c-*-jisx0201*-0" },
87     { CS_JIS0208_1990_0, "-*-mincho-*-r-*--*-*-*-*-c-*-jisx0208*-0" },
88     { CS_JIS0212_1990_0, "-*-mincho-*-r-*--*-*-*-*-c-*-jisx0212*-0" },
89     { CS_JIS0201_1976_0, "-*-*-*-r-*--*-*-*-*-c-*-jisx0201*-0" },
90     { CS_JIS0208_1990_0, "-*-*-*-r-*--*-*-*-*-c-*-jisx0208*-0" },
91     { CS_JIS0212_1990_0, "-*-*-*-r-*--*-*-*-*-c-*-jisx0212*-0" },
92     #endif
93    
94 root 1.46 #if ENCODING_ZH || ENCODING_ZH_EXT
95 root 1.1 # if XFT
96 root 1.56 { CS_GBK_0, "xft:AR PL KaitiM GB" },
97     { CS_GBK_0, "xft:AR PL SungtiL GB" },
98     { CS_GBK_0, "xft::spacing=100:lang=zh" },
99 root 1.1 { CS_BIG5_EXT, "xft:AR PL Mingti2L Big5" },
100     { CS_BIG5_EXT, "xft:AR PL KaitiM Big5" },
101     { CS_GB2312_1980_0, "xft:AR PL KaitiM GB" },
102     { CS_GB2312_1980_0, "xft:AR PL SungtiL GB" },
103 root 1.41 { CS_GB2312_1980_0, "xft::spacing=100:lang=zh" },
104 root 1.1 # endif
105 root 1.56 { CS_GBK_0, "-*-*-*-*-*-*-*-*-*-*-c-*-gbk*-0" },
106 root 1.1 { CS_BIG5, "-*-*-*-*-*-*-*-*-*-*-c-*-big5-0" },
107     { CS_BIG5_PLUS, "-*-*-*-*-*-*-*-*-*-*-c-*-big5p-0" },
108     { CS_BIG5_EXT, "-*-*-*-*-*-*-*-*-*-*-c-*-big5.eten-0" },
109 root 1.56 { CS_GB2312_1980_0, "-*-*-*-*-*-*-*-*-*-*-c-*-gb2312*-0" },
110 root 1.1 { CS_CNS11643_1992_1, "-*-*-*-*-*-*-*-*-*-*-c-*-gb2312*-0" },
111     { CS_CNS11643_1992_1, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-1" },
112     { CS_CNS11643_1992_2, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-2" },
113     { CS_CNS11643_1992_3, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-3" },
114     { CS_CNS11643_1992_4, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-4" },
115     { CS_CNS11643_1992_5, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-5" },
116     { CS_CNS11643_1992_6, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-6" },
117     { CS_CNS11643_1992_7, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-7" },
118     { CS_CNS11643_1992_F, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-f" },
119     #endif
120    
121 root 1.41 #if ENCODING_KR
122     { CS_KSC5601_1987_0, "-baekmuk-gulim-*-*-*-*-*-*-*-*-c-*-ksc5601*" },
123     { CS_KSC5601_1987_0, "-*-*-*-*-*-*-*-*-*-*-c-*-ksc5601*" },
124     # if XFT
125     { CS_KSC5601_1987_0, "xft:Baekmuk Gulim:antialias=false" },
126     { CS_KSC5601_1987_0, "xft::spacing=100:lang=ko:antialias=false" },
127     # endif
128 root 1.1 #endif
129 root 1.41
130     // generic font fallback
131 root 1.1 { CS_UNICODE, "-*-lucidatypewriter-*-*-*-*-*-*-*-*-m-*-iso10646-1" },
132     { CS_UNICODE, "-*-unifont-*-*-*-*-*-*-*-*-c-*-iso10646-1" },
133     { CS_UNICODE, "-*-*-*-r-*-*-*-*-*-*-c-*-iso10646-1" },
134     { CS_UNICODE, "-*-*-*-r-*-*-*-*-*-*-m-*-iso10646-1" },
135     #if XFT
136 root 1.69 { CS_UNICODE, "xft:Bitstream Vera Sans Mono:antialias=false:autohint=true" },
137     { CS_UNICODE, "xft:Courier New:antialias=false:autohint=true" },
138     { CS_UNICODE, "xft:Andale Mono:antialias=false:autohint=false" },
139     { CS_UNICODE, "xft:Arial Unicode MS:antialias=false:autohint=false" },
140 root 1.41
141 root 1.1 // FreeMono is usually uglier than x fonts, so try last only.
142 root 1.46 { CS_UNICODE, "xft:FreeMono:autohint=true" },
143 root 1.1 #endif
144    
145     { CS_UNKNOWN, 0 }
146     };
147    
148 root 1.41 // these characters are used to guess the font height and width
149 root 1.64 // pango uses a similar algorithm and doesn't trust the font either.
150 root 1.41 static uint16_t extent_test_chars[] = {
151     '0', '1', '8', 'a', 'd', 'x', 'm', 'y', 'g', 'W', 'X', '\'', '_',
152     0x00cd, 0x00d5, 0x0114, 0x0177, 0x0643, // ÍÕĔŷﻙ
153     0x304c, 0x672c, // が本
154     };
155    
156     #define NUM_EXTENT_TEST_CHARS (sizeof (extent_test_chars) / sizeof (extent_test_chars[0]))
157    
158 root 1.1 /////////////////////////////////////////////////////////////////////////////
159    
160     #if XFT
161     rxvt_drawable::~rxvt_drawable ()
162     {
163     if (xftdrawable)
164     XftDrawDestroy (xftdrawable);
165     }
166    
167     rxvt_drawable::operator XftDraw *()
168     {
169     if (!xftdrawable)
170     xftdrawable = XftDrawCreate (display->display, drawable, display->visual, display->cmap);
171    
172     return xftdrawable;
173     }
174     #endif
175    
176     /////////////////////////////////////////////////////////////////////////////
177    
178     static void *enc_buf;
179     static uint32_t enc_len;
180    
181     static inline void *
182     get_enc_buf (uint32_t len)
183     {
184     if (len > enc_len)
185     {
186     free (enc_buf);
187     enc_buf = malloc (len);
188     enc_len = len;
189     }
190    
191     return enc_buf;
192     }
193    
194     static const char *
195     enc_char (const text_t *text, uint32_t len, codeset cs, bool &zero)
196     {
197     uint8_t *buf = (uint8_t *)get_enc_buf (len);
198    
199     while (len--)
200     {
201     uint32_t c = FROM_UNICODE (cs, *text++);
202    
203     if (c == NOCHAR)
204     {
205     c = 0;
206     zero = true;
207     }
208    
209     *buf++ = c;
210     }
211    
212     return (const char *)enc_buf;
213     }
214    
215     static const XChar2b *
216     enc_xchar2b (const text_t *text, uint32_t len, codeset cs, bool &zero)
217     {
218     XChar2b *buf = (XChar2b *)get_enc_buf (len * sizeof (XChar2b));
219    
220     while (len--)
221     {
222     uint32_t c = FROM_UNICODE (cs, *text++);
223    
224     if (c == NOCHAR)
225     {
226     c = 0;
227     zero = true;
228     }
229    
230     buf->byte1 = c >> 8;
231     buf->byte2 = c;
232     buf++;
233     }
234    
235     return (XChar2b *)enc_buf;
236     }
237    
238     /////////////////////////////////////////////////////////////////////////////
239    
240     void
241 root 1.41 rxvt_font::set_name (char *name)
242     {
243     if (this->name == name)
244     return;
245    
246     if (this->name) free (this->name); // let the compiler optimize
247     this->name = name;
248     }
249    
250     void
251 root 1.1 rxvt_font::clear_rect (rxvt_drawable &d, int x, int y, int w, int h, int color)
252     {
253     if (color == Color_bg)
254     XClearArea (d.display->display, d, x, y, w, h, FALSE);
255     else if (color >= 0)
256     {
257     #if XFT
258 root 1.3 XftDrawRect (d, &r->pix_colors[color].c, x, y, w, h);
259 root 1.1 #else
260 root 1.3 XSetForeground (d.display->display, TGC, r->pix_colors[color]);
261 root 1.1 XFillRectangle (d.display->display, d, TGC, x, y, w, h);
262     #endif
263     }
264     }
265    
266     #include "table/linedraw.h"
267    
268     struct rxvt_font_default : rxvt_font {
269 root 1.41 struct rxvt_fontset *fs;
270    
271     rxvt_font_default (rxvt_fontset *fs)
272     : rxvt_font ()
273     {
274     this->fs = fs;
275     }
276 root 1.1
277     rxvt_fontprop properties ()
278     {
279     rxvt_fontprop p;
280    
281     p.width = p.height = 1;
282 root 1.76 p.ascent = rxvt_fontprop::unset;
283 root 1.1 p.weight = rxvt_fontprop::medium;
284     p.slant = rxvt_fontprop::roman;
285    
286     return p;
287     }
288    
289     bool load (const rxvt_fontprop &prop)
290     {
291     width = 1; height = 1;
292     ascent = 1; descent = 0;
293    
294 root 1.41 set_name (strdup ("built-in support font"));
295 root 1.1
296     return true;
297     }
298    
299 root 1.41 bool has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful)
300 root 1.1 {
301 root 1.41 careful = false;
302    
303 root 1.1 if (unicode <= 0x001f)
304     return true;
305    
306     if (unicode <= 0x007f)
307     return false;
308    
309     if (unicode <= 0x009f)
310     return true;
311    
312 root 1.77 if (unicode >= 0x2500 && unicode <= 0x259f &&
313 root 1.79 !r->option (Opt_skipBuiltinGlyphs))
314 root 1.1 return true;
315    
316     if (IS_COMPOSE (unicode))
317     return true;
318    
319     switch (unicode)
320     {
321     case ZERO_WIDTH_CHAR:
322 root 1.41 case NOCHAR:
323 root 1.1 return true;
324     }
325    
326     return false;
327     }
328    
329     void draw (rxvt_drawable &d, int x, int y,
330     const text_t *text, int len,
331     int fg, int bg);
332     };
333    
334     void
335     rxvt_font_default::draw (rxvt_drawable &d, int x, int y,
336     const text_t *text, int len,
337     int fg, int bg)
338     {
339 root 1.66 Display *disp = d.display->display;
340    
341 root 1.75 clear_rect (d, x, y, r->fwidth * len, r->fheight, bg);
342 root 1.1
343 root 1.66 XSetForeground (disp, TGC, r->pix_colors[fg]);
344 root 1.1
345 root 1.56 while (len)
346 root 1.1 {
347     #if ENABLE_COMBINING
348     compose_char *cc;
349     #endif
350 root 1.56 const text_t *tp = text;
351     text_t t = *tp;
352    
353     while (++text, --len && *text == NOCHAR)
354     ;
355    
356     int width = text - tp;
357 root 1.75 int fwidth = r->fwidth * width;
358 root 1.1
359 root 1.79 if (0x2500 <= t && t <= 0x259f)
360 root 1.1 {
361     uint16_t offs = linedraw_offs[t - 0x2500];
362     uint32_t *a = linedraw_command + (offs >> 4);
363     uint32_t *b = a + (offs & 15);
364    
365 root 1.56 int W = fwidth;
366 root 1.75 int H = r->fheight;
367 root 1.1
368     int x_[16];
369     int y_[16];
370    
371     for (int i = 0; i <= 8; i++)
372     {
373     x_[i] = x + ((W-1) * i + (i*7/8)) / 8;
374     y_[i] = y + ((H-1) * i + (i*7/8)) / 8;
375     }
376    
377     x_[10] = x + (W - 1) / 2; x_[9] = x_[10] - 1; x_[11] = x_[10] + 1;
378     y_[10] = y + (H - 1) / 2; y_[9] = y_[10] - 1; y_[11] = y_[10] + 1;
379    
380     XGCValues gcv;
381    
382     gcv.cap_style = CapButt;
383     gcv.line_width = 0;
384 root 1.66 XChangeGC (disp, TGC, GCLineWidth | GCCapStyle, &gcv);
385 root 1.1
386     while (a < b)
387     {
388     uint32_t command = *a++;
389    
390     int op = (command >> 24) & 255;
391     int a = (command >> 20) & 15;
392     int b = (command >> 16) & 15;
393     int x1 = x_[(command >> 12) & 15];
394     int y1 = y_[(command >> 8) & 15];
395     int x2 = x_[(command >> 4) & 15];
396     int y2 = y_[(command >> 0) & 15];
397    
398     switch (op)
399     {
400     case 0: // line
401 root 1.66 XDrawLine (disp, d, TGC, x1, y1, x2, y2);
402 root 1.1 break;
403    
404     case 1: // rectangle, possibly stippled
405     if (a)
406     {
407     static char bm[] = { 0,0 , 3,1 , 1,2 , 1,0 };
408    
409     gcv.fill_style = FillStippled;
410 root 1.66 gcv.stipple = XCreateBitmapFromData (disp, d, bm + a * 2, 2, 2);
411 root 1.1 gcv.ts_x_origin = x;
412     gcv.ts_y_origin = y;
413    
414 root 1.66 XChangeGC (disp, TGC,
415 root 1.1 GCFillStyle | GCStipple | GCTileStipXOrigin | GCTileStipYOrigin,
416     &gcv);
417     }
418    
419 root 1.66 XFillRectangle (disp, d, TGC, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
420 root 1.1
421     if (a)
422     {
423 root 1.66 XFreePixmap (disp, gcv.stipple);
424 root 1.1 gcv.stipple = 0;
425     gcv.fill_style = FillSolid;
426 root 1.66 XChangeGC (disp, TGC, GCFillStyle, &gcv);
427 root 1.1 }
428     break;
429     case 2: // arc
430 root 1.66 XDrawArc (disp, d, TGC,
431 root 1.1 x1 - W/2, y1 - H/2, W-1, H-1,
432     (a - 1) * 90*64, (b - 1) * 90*64);
433     break;
434     }
435     }
436     }
437     #if ENABLE_COMBINING
438     else if (IS_COMPOSE (t) && (cc = rxvt_composite[t]))
439     {
440 root 1.56 text_t chrs[2];
441     width = min (2, width);
442     chrs [1] = NOCHAR;
443    
444     *chrs = cc->c1;
445     rxvt_font *f1 = (*fs)[fs->find_font (cc->c1)];
446     f1->draw (d, x, y, chrs, width, fg, bg);
447 root 1.46
448 root 1.1 if (cc->c2 != NOCHAR)
449     {
450 root 1.46 bool careful;
451    
452 root 1.1 // prefer font of first character, for no good reasons
453 root 1.56 *chrs = cc->c2;
454     rxvt_font *f2 = (f1->has_char (cc->c2, 0, careful) && !careful)
455 root 1.2 ? f1
456 root 1.56 : (*fs)[fs->find_font (cc->c2)];
457 root 1.1
458 root 1.56 f2->draw (d, x, y, chrs, width, fg, -1);
459 root 1.1 }
460     }
461     #endif
462     else
463     switch (t)
464     {
465 root 1.41 case '\t':
466 root 1.1 case ZERO_WIDTH_CHAR:
467 root 1.41 case NOCHAR:
468 root 1.1 break;
469    
470     default:
471 root 1.66 XDrawRectangle (disp, d, TGC, x + 2, y + 2,
472 root 1.75 fwidth - 4, r->fheight - 4);
473 root 1.1 }
474    
475 root 1.56 x += fwidth;
476 root 1.1 }
477     }
478    
479     /////////////////////////////////////////////////////////////////////////////
480    
481     struct rxvt_font_x11 : rxvt_font {
482     rxvt_font_x11 () { f = 0; }
483    
484     void clear ();
485    
486     rxvt_fontprop properties ();
487    
488     bool load (const rxvt_fontprop &prop);
489    
490 root 1.41 bool has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful);
491 root 1.1
492     void draw (rxvt_drawable &d, int x, int y,
493     const text_t *text, int len,
494     int fg, int bg);
495    
496 root 1.41 bool slow; // wether this is a proportional font or has other funny characteristics
497 root 1.1 XFontStruct *f;
498     codeset cs;
499     bool enc2b, encm;
500    
501     char *get_property (XFontStruct *f, const char *property, const char *repl) const;
502     bool set_properties (rxvt_fontprop &p, int height, const char *weight, const char *slant, int avgwidth);
503     bool set_properties (rxvt_fontprop &p, XFontStruct *f);
504     bool set_properties (rxvt_fontprop &p, const char *name);
505     };
506    
507     char *
508     rxvt_font_x11::get_property (XFontStruct *f, const char *property, const char *repl) const
509     {
510     unsigned long value;
511    
512     if (XGetFontProperty (f, XInternAtom (DISPLAY, property, 0), &value))
513     return XGetAtomName (DISPLAY, value);
514     else
515     return rxvt_strdup (repl);
516     }
517    
518     rxvt_fontprop
519     rxvt_font_x11::properties ()
520     {
521     rxvt_fontprop p;
522     set_properties (p, f);
523     return p;
524     }
525    
526     bool
527     rxvt_font_x11::set_properties (rxvt_fontprop &p, int height, const char *weight, const char *slant, int avgwidth)
528     {
529 root 1.41 p.width = avgwidth ? (avgwidth + 1) / 10 : (height + 1) / 2;
530 root 1.1 p.height = height;
531 root 1.76 p.ascent = rxvt_fontprop::unset;
532 root 1.1 p.weight = *weight == 'B' || *weight == 'b' ? rxvt_fontprop::bold : rxvt_fontprop::medium;
533     p.slant = *slant == 'r' || *slant == 'R' ? rxvt_fontprop::roman : rxvt_fontprop::italic;
534    
535     return true;
536     }
537    
538     bool
539     rxvt_font_x11::set_properties (rxvt_fontprop &p, XFontStruct *f)
540     {
541     unsigned long height;
542 root 1.64
543     #if 0
544 root 1.1 if (!XGetFontProperty (f, XInternAtom (DISPLAY, "PIXEL_SIZE", 0), &height))
545     return false;
546 root 1.64 #else
547     height = f->ascent + f->descent;
548     #endif
549 root 1.1
550     unsigned long avgwidth;
551     if (!XGetFontProperty (f, XInternAtom (DISPLAY, "AVERAGE_WIDTH", 0), &avgwidth))
552     avgwidth = 0;
553    
554     char *weight = get_property (f, "WEIGHT_NAME", "medium");
555     char *slant = get_property (f, "SLANT", "r");
556    
557     set_properties (p, height, weight, slant, avgwidth);
558    
559     free (weight);
560     free (slant);
561    
562 root 1.76 p.ascent = f->ascent;
563    
564 root 1.1 return true;
565     }
566    
567     bool
568     rxvt_font_x11::set_properties (rxvt_fontprop &p, const char *name)
569     {
570     int slashes = 0;
571     const char *comp[13];
572    
573     for (const char *c = name; *c; c++)
574     if (*c == '-')
575     {
576     comp[slashes++] = c + 1;
577     if (slashes >= 13)
578     break;
579     }
580    
581     /* can we short-circuit the costly XLoadQueryFont? */
582     if (slashes >= 13
583     && (*comp[ 6] >= '1' && *comp[ 6] <= '9')
584     && (*comp[11] >= '0' && *comp[11] <= '9'))
585     return set_properties (p, atoi (comp[6]), comp[2], comp[3], atoi (comp[11]));
586    
587     XFontStruct *f = XLoadQueryFont (DISPLAY, name);
588    
589     if (f)
590     {
591     // the font should really exist now. if not, we have a problem
592     // (e.g. if the user did xset fp rehash just when we were searching fonts).
593     // in that case, just return garbage.
594     bool ret = set_properties (p, f);
595     XFreeFont (DISPLAY, f);
596     return ret;
597     }
598     else
599     return false;
600     }
601    
602     // fix the size of scalable fonts
603 root 1.41 static bool
604     replace_field (char *buf, const char *name, int index, const char old, const char *replace)
605 root 1.1 {
606     int slashes = 0;
607 root 1.41 const char *field, *end;
608 root 1.1
609     for (const char *c = name; *c; c++)
610     if (*c == '-')
611     {
612 root 1.41 if (slashes == index)
613     field = c + 1;
614    
615     if (slashes == index + 1)
616     end = c;
617 root 1.1
618     if (++slashes >= 13)
619     break;
620     }
621    
622 root 1.41 if (slashes >= 13 && (!old || *field == old))
623 root 1.1 {
624 root 1.41 // TODO: check for overflow in font-name
625     strncpy (buf, name, field - name);
626     buf += field - name;
627     strcpy (buf, replace);
628     strcat (buf, end);
629    
630     return true;
631 root 1.1 }
632     else
633 root 1.41 {
634     strcpy (buf, name);
635    
636     return false;
637     }
638 root 1.1 }
639    
640     bool
641     rxvt_font_x11::load (const rxvt_fontprop &prop)
642     {
643 root 1.66 Display *disp = DISPLAY;
644    
645 root 1.1 clear ();
646    
647 root 1.41 char field_str[64]; // enough for 128 bits
648    
649     // first morph the font if required
650     if (prop.weight != rxvt_fontprop::unset
651     || prop.slant != rxvt_fontprop::unset)
652     {
653     char fname[1024];
654    
655     if (name[0] != '-')
656     {
657 root 1.66 f = XLoadQueryFont (disp, name);
658 root 1.41
659     if (!f)
660     return false;
661    
662     char *new_name = get_property (f, "FONT", name);
663    
664     if (new_name)
665     set_name (new_name);
666     else
667     rxvt_warn ("font '%s' has no FONT property, continuing without.", name);
668    
669 root 1.66 XFreeFont (disp, f);
670 root 1.41 f = 0;
671     }
672    
673     if (prop.weight != rxvt_fontprop::unset)
674     {
675     replace_field (fname, name, 2, 0,
676     prop.weight < rxvt_fontprop::bold
677     ? "medium" : "bold");
678     set_name (strdup (fname));
679     }
680    
681     if (prop.slant != rxvt_fontprop::unset)
682     {
683     replace_field (fname, name, 3, 0,
684     prop.slant < rxvt_fontprop::italic
685     ? "r" : "i"); // TODO: handle "o"blique, too
686     set_name (strdup (fname));
687     }
688     }
689    
690 root 1.64 sprintf (field_str, "%d", prop.height == rxvt_fontprop::unset
691     ? 0 : prop.height);
692    
693     struct font_weight {
694     char *name;
695     int diff;
696    
697     void clear ()
698     {
699     name = 0;
700     diff = 0x7fffffff;
701     }
702    
703     font_weight () { clear (); }
704     ~font_weight () { free (name); }
705     };
706    
707 root 1.1 char **list;
708     int count;
709 root 1.66 list = XListFonts (disp, name, 4000, &count);
710 root 1.41
711 root 1.1 set_name (0);
712    
713     if (!list)
714     return false;
715    
716 root 1.64 font_weight *fonts = new font_weight[count];
717 root 1.41
718 root 1.1 for (int i = 0; i < count; i++)
719     {
720     rxvt_fontprop p;
721     char fname[1024];
722 root 1.41
723     int diff = 0;
724 root 1.48
725 root 1.41 if (replace_field (fname, list[i], 6, '0', field_str))
726     diff += 10; // slightly penalize scalable fonts
727 root 1.68 else if (replace_field (fname, list[i], 11, '0', "0"))
728     diff += 300; // more heavily penalize what looks like scaled bitmap fotns
729 root 1.1
730     if (!set_properties (p, fname))
731     continue;
732    
733 root 1.41 if (prop.height != rxvt_fontprop::unset
734     && p.height > prop.height) // weed out too large fonts
735 root 1.1 continue;
736    
737 root 1.41 if (prop.height != rxvt_fontprop::unset) diff += (prop.height - p.height) * 128;
738     if (prop.weight != rxvt_fontprop::unset) diff += abs (prop.weight - p.weight);
739     if (prop.slant != rxvt_fontprop::unset) diff += abs (prop.slant - p.slant);
740     //if (prop.width != rxvt_fontprop::unset) diff += abs (prop.width - p.width);
741 root 1.1
742 root 1.64 fonts[i].name = strdup (fname);
743     fonts[i].diff = diff;
744 root 1.1 }
745    
746     XFreeFontNames (list);
747    
748 root 1.64 // this loop only iterates when the guessed font-size is too small
749     for (;;)
750     {
751     font_weight *best = fonts + count - 1;
752    
753     for (font_weight *w = best; w-- > fonts; )
754 root 1.68 if (w->diff <= best->diff)
755 root 1.64 best = w;
756    
757     if (!best->name
758 root 1.66 || !(f = XLoadQueryFont (disp, best->name)))
759 root 1.64 break;
760    
761     set_name (best->name);
762     best->clear ();
763    
764     ascent = f->ascent;
765     descent = f->descent;
766     height = ascent + descent;
767    
768     if (prop.height == rxvt_fontprop::unset
769     || height <= prop.height)
770     break; // font is ready for use
771    
772     // PIXEL_SIZE small enough, but real height too large
773     clear ();
774     }
775 root 1.1
776 root 1.64 delete [] fonts;
777 root 1.1
778     if (!f)
779     return false;
780    
781     char *registry = get_property (f, "CHARSET_REGISTRY", 0);
782     char *encoding = get_property (f, "CHARSET_ENCODING", 0);
783    
784     if (registry && encoding)
785     {
786     char charset[64];
787     snprintf (charset, 64, "%s-%s", registry, encoding);
788    
789     cs = codeset_from_name (charset);
790     }
791     else
792     {
793     const char *charset = get_property (f, "FONT", 0);
794    
795     if (!charset)
796     charset = name;
797    
798     int count = 13;
799     while (*charset)
800     if (*charset++ == '-' && !--count)
801     break;
802    
803     cs = codeset_from_name (charset);
804     }
805    
806     free (registry);
807     free (encoding);
808    
809     if (cs == CS_UNICODE)
810     cs = CS_UNICODE_16; // X11 can have a max. of 65536 chars per font
811    
812     encm = f->min_byte1 != 0 || f->max_byte1 != 0;
813     enc2b = encm || f->max_char_or_byte2 > 255;
814    
815     slow = false;
816    
817 root 1.41 #if 1 // only used for slow detection, TODO optimize
818 root 1.1 if (f->min_bounds.width == f->max_bounds.width)
819     width = f->min_bounds.width;
820     else if (f->per_char == NULL)
821     width = f->max_bounds.width;
822     else
823     {
824     slow = true;
825    
826     int N = f->max_char_or_byte2 - f->min_char_or_byte2;
827    
828     if (encm)
829     N += (f->max_byte1 - f->min_byte1)
830     * (f->max_char_or_byte2 - f->min_char_or_byte2 + 1);
831    
832     while (N)
833     {
834     if (f->per_char[N].width > width)
835     width = f->per_char[N].width;
836    
837     --N;
838     }
839     }
840 root 1.41 #endif
841    
842     width = 1;
843    
844     for (uint16_t *t = extent_test_chars + NUM_EXTENT_TEST_CHARS; t-- > extent_test_chars; )
845     {
846     if (cs != CS_UNICODE
847     && *t > 0x100
848     && FROM_UNICODE (cs, *t) == NOCHAR)
849     continue;
850    
851     // ignore characters we wouldn't use anyways
852     bool careful;
853     if (!has_char (*t, &prop, careful))
854     continue;
855    
856     XChar2b ch = { *t >> 8, *t };
857    
858     XCharStruct g;
859     int dir_ret, asc_ret, des_ret;
860     XTextExtents16 (f, &ch, 1, &dir_ret, &asc_ret, &des_ret, &g);
861    
862 root 1.72 int wcw = wcwidth (*t); if (wcw > 0) g.width = (g.width + wcw - 1) / wcw;
863 root 1.41
864     if (width < g.width) width = g.width;
865     }
866 root 1.1
867     if (cs == CS_UNKNOWN)
868     {
869     fprintf (stderr, "unable to deduce codeset, ignoring font '%s'\n", name);
870    
871     clear ();
872 root 1.41 return false;
873     }
874 root 1.1
875 root 1.41 #if 0 // do it per-character
876     if (prop && width > prop->width)
877     {
878     clear ();
879 root 1.1 return false;
880     }
881 root 1.41 #endif
882 root 1.1
883     return true;
884     }
885    
886     void
887     rxvt_font_x11::clear ()
888     {
889     if (f)
890     {
891     XFreeFont (DISPLAY, f);
892     f = 0;
893     }
894     }
895    
896     bool
897 root 1.41 rxvt_font_x11::has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful)
898 root 1.1 {
899     uint32_t ch = FROM_UNICODE (cs, unicode);
900    
901     if (ch == NOCHAR)
902     return false;
903    
904     /* check wether the character exists in _this_ font. horrible. */
905     XCharStruct *xcs;
906    
907     if (encm)
908     {
909     unsigned char byte1 = ch >> 8;
910     unsigned char byte2 = ch & 255;
911    
912     if (byte1 < f->min_byte1 || byte1 > f->max_byte1
913     || byte2 < f->min_char_or_byte2 || byte2 > f->max_char_or_byte2)
914     return false;
915    
916 root 1.41 if (f->per_char)
917     {
918     int D = f->max_char_or_byte2 - f->min_char_or_byte2 + 1;
919     int N = (byte1 - f->min_byte1) * D + byte2 - f->min_char_or_byte2;
920 root 1.1
921 root 1.41 xcs = f->per_char + N;
922     }
923     else
924     xcs = &f->max_bounds;
925 root 1.1 }
926     else
927     {
928     if (ch < f->min_char_or_byte2 || ch > f->max_char_or_byte2)
929     return false;
930    
931 root 1.41 if (f->per_char)
932     xcs = f->per_char + (ch - f->min_char_or_byte2);
933     else
934     xcs = &f->max_bounds;
935 root 1.1 }
936    
937     if (xcs->lbearing == 0 && xcs->rbearing == 0 && xcs->width == 0
938     && xcs->ascent == 0 && xcs->descent == 0)
939     return false;
940    
941 root 1.41 if (!prop || prop->width == rxvt_fontprop::unset)
942     return true;
943    
944     // check character against base font bounding box
945     int w = xcs->width;
946     int wcw = wcwidth (unicode);
947 root 1.72 if (wcw > 0) w = (w + wcw - 1) / wcw;
948 root 1.41
949     careful = w > prop->width;
950     if (careful && w > prop->width * MAX_OVERLAP >> 2)
951     return false;
952    
953 root 1.1 return true;
954     }
955    
956     void
957     rxvt_font_x11::draw (rxvt_drawable &d, int x, int y,
958     const text_t *text, int len,
959     int fg, int bg)
960     {
961     // this looks like a mess /.
962     // and it is a mess /.
963     // yet we are trying to be perfect /.
964     // but the result still isn't perfect /.
965    
966     bool slow = this->slow
967 root 1.75 || width != r->fwidth
968     || height != r->fheight;
969 root 1.1
970 root 1.75 int base = ascent; // sorry, incorrect: r->fbase;
971 root 1.1
972     XGCValues v;
973 root 1.3 v.foreground = r->pix_colors[fg];
974 root 1.1 v.font = f->fid;
975    
976     if (enc2b)
977     {
978     const XChar2b *xc = enc_xchar2b (text, len, cs, slow);
979    
980     if (bg == Color_bg && !slow)
981     {
982 root 1.3 v.background = r->pix_colors[bg];
983 root 1.1 XChangeGC (d.display->display, TGC, GCForeground | GCBackground | GCFont, &v);
984     XDrawImageString16 (d.display->display, d, TGC, x, y + base, xc, len);
985     }
986     else
987     {
988 root 1.75 clear_rect (d, x, y, r->fwidth * len, r->fheight, bg);
989 root 1.1
990     XChangeGC (d.display->display, TGC, GCForeground | GCFont, &v);
991    
992     if (slow)
993     {
994     do
995     {
996     if (xc->byte1 || xc->byte2)
997     XDrawString16 (d.display->display, d, TGC, x, y + base, xc, 1);
998    
999 root 1.75 x += r->fwidth;
1000 root 1.1 xc++; len--;
1001     }
1002     while (len);
1003     }
1004     else
1005     XDrawString16 (d.display->display, d, TGC, x, y + base, xc, len);
1006     }
1007     }
1008     else
1009     {
1010     const char *xc = enc_char (text, len, cs, slow);
1011    
1012     if (bg == Color_bg && !slow)
1013     {
1014 root 1.3 v.background = r->pix_colors[bg];
1015 root 1.1 XChangeGC (d.display->display, TGC, GCForeground | GCBackground | GCFont, &v);
1016     XDrawImageString (d.display->display, d, TGC, x, y + base, xc, len);
1017     }
1018     else
1019     {
1020 root 1.75 clear_rect (d, x, y, r->fwidth * len, r->fheight, bg);
1021 root 1.1
1022     XChangeGC (d.display->display, TGC, GCForeground | GCFont, &v);
1023    
1024     if (slow)
1025     {
1026     do
1027     {
1028     if (*xc)
1029     XDrawString (d.display->display, d, TGC, x, y + base, xc, 1);
1030    
1031 root 1.75 x += r->fwidth;
1032 root 1.1 xc++; len--;
1033     }
1034     while (len);
1035     }
1036     else
1037     XDrawString (d.display->display, d, TGC, x, y + base, xc, len);
1038     }
1039     }
1040     }
1041    
1042     /////////////////////////////////////////////////////////////////////////////
1043    
1044     #if XFT
1045    
1046     struct rxvt_font_xft : rxvt_font {
1047     rxvt_font_xft () { f = 0; }
1048    
1049     void clear ();
1050    
1051     rxvt_fontprop properties ();
1052    
1053     bool load (const rxvt_fontprop &prop);
1054    
1055     void draw (rxvt_drawable &d, int x, int y,
1056     const text_t *text, int len,
1057     int fg, int bg);
1058    
1059 root 1.41 bool has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &carefull);
1060 root 1.1
1061     protected:
1062     XftFont *f;
1063     };
1064    
1065     void
1066     rxvt_font_xft::clear ()
1067     {
1068     if (f)
1069     {
1070     XftFontClose (DISPLAY, f);
1071     f = 0;
1072     }
1073     }
1074    
1075     rxvt_fontprop
1076     rxvt_font_xft::properties ()
1077     {
1078     rxvt_fontprop p;
1079    
1080     FT_Face face = XftLockFace (f);
1081    
1082     p.width = width;
1083     p.height = height;
1084 root 1.76 p.ascent = ascent;
1085 root 1.1 p.weight = face->style_flags & FT_STYLE_FLAG_BOLD
1086     ? rxvt_fontprop::bold : rxvt_fontprop::medium;
1087     p.slant = face->style_flags & FT_STYLE_FLAG_ITALIC
1088     ? rxvt_fontprop::italic : rxvt_fontprop::roman;
1089    
1090     XftUnlockFace (f);
1091    
1092     return p;
1093     }
1094    
1095     bool
1096     rxvt_font_xft::load (const rxvt_fontprop &prop)
1097     {
1098 root 1.66 Display *disp = DISPLAY;
1099    
1100 root 1.1 clear ();
1101    
1102     FcPattern *p = FcNameParse ((FcChar8 *) name);
1103    
1104     if (!p)
1105     return false;
1106    
1107     FcValue v;
1108    
1109 root 1.41 if (prop.height != rxvt_fontprop::unset
1110 root 1.68 && (FcPatternGet (p, FC_PIXEL_SIZE, 0, &v) != FcResultMatch
1111 root 1.50 && FcPatternGet (p, FC_SIZE, 0, &v) != FcResultMatch))
1112 root 1.1 FcPatternAddInteger (p, FC_PIXEL_SIZE, prop.height);
1113    
1114 root 1.41 if (prop.weight != rxvt_fontprop::unset
1115     && FcPatternGet (p, FC_WEIGHT, 0, &v) != FcResultMatch)
1116 root 1.1 FcPatternAddInteger (p, FC_WEIGHT, prop.weight);
1117    
1118 root 1.41 if (prop.slant != rxvt_fontprop::unset
1119     && FcPatternGet (p, FC_SLANT, 0, &v) != FcResultMatch)
1120 root 1.1 FcPatternAddInteger (p, FC_SLANT, prop.slant);
1121    
1122     #if 0 // clipping unfortunately destroys our precious double-width-characters
1123     // clip width, we can't do better, or can we?
1124     if (FcPatternGet (p, FC_CHAR_WIDTH, 0, &v) != FcResultMatch)
1125 root 1.41 FcPatternAddInteger (p, FC_CHAR_WIDTH, prop->width);
1126 root 1.1 #endif
1127    
1128 root 1.41 if (FcPatternGet (p, FC_MINSPACE, 0, &v) != FcResultMatch)
1129     FcPatternAddBool (p, FC_MINSPACE, 1);
1130    
1131     // store generated name so iso14755 view gives better results
1132     set_name ((char *)FcNameUnparse (p));
1133    
1134 root 1.1 XftResult result;
1135 root 1.66 FcPattern *match = XftFontMatch (disp, r->display->screen, p, &result);
1136 root 1.1
1137     FcPatternDestroy (p);
1138    
1139     if (!match)
1140     return false;
1141    
1142 root 1.41 int ftheight = 0;
1143     bool success = true;
1144 root 1.1
1145 root 1.41 for (;;)
1146 root 1.1 {
1147 root 1.66 f = XftFontOpenPattern (disp, FcPatternDuplicate (match));
1148 root 1.41
1149     if (!f)
1150     {
1151     success = false;
1152     break;
1153     }
1154    
1155     FT_Face face = XftLockFace (f);
1156    
1157     ascent = (face->size->metrics.ascender + 63) >> 6;
1158     descent = (-face->size->metrics.descender + 63) >> 6;
1159     height = max (ascent + descent, (face->size->metrics.height + 63) >> 6);
1160     width = 0;
1161 root 1.1
1162 root 1.41 bool scalable = face->face_flags & FT_FACE_FLAG_SCALABLE;
1163 root 1.1
1164 root 1.41 XftUnlockFace (f);
1165 root 1.1
1166 root 1.72 int glheight = height;
1167    
1168 root 1.41 for (uint16_t *t = extent_test_chars + NUM_EXTENT_TEST_CHARS; t-- > extent_test_chars; )
1169     {
1170     FcChar16 ch = *t;
1171 root 1.1
1172 root 1.41 if (cs != CS_UNICODE
1173     && ch > 0x100
1174     && FROM_UNICODE (cs, ch) == NOCHAR)
1175     continue;
1176    
1177     // ignore characters we wouldn't use anyways
1178     bool careful;
1179     if (!has_char (*t, &prop, careful))
1180     continue;
1181 root 1.1
1182 root 1.41 XGlyphInfo g;
1183 root 1.66 XftTextExtents16 (disp, f, &ch, 1, &g);
1184 root 1.1
1185 root 1.72 g.width -= g.x;
1186    
1187 root 1.41 int wcw = wcwidth (ch);
1188 root 1.72 if (wcw > 0) g.width = (g.width + wcw - 1) / wcw;
1189 root 1.1
1190 root 1.72 if (width < g.width ) width = g.width;
1191     if (height < g.height ) height = g.height;
1192     if (glheight < g.height - g.y) glheight = g.height - g.y;
1193 root 1.41 }
1194 root 1.1
1195 root 1.74 if (!width)
1196     {
1197     rxvt_warn ("unable to calculate font width for '%s', ignoring.\n", name);
1198    
1199     XftFontClose (disp, f);
1200     f = 0;
1201    
1202     success = false;
1203     break;
1204     }
1205    
1206 root 1.41 if (prop.height == rxvt_fontprop::unset
1207 root 1.72 || (height <= prop.height && glheight <= prop.height)
1208 root 1.41 || height <= 2
1209     || !scalable)
1210 root 1.1 break;
1211    
1212     if (ftheight)
1213     {
1214     // take smaller steps near the end
1215     if (height > prop.height + 1) ftheight++;
1216     if (height > prop.height + 2) ftheight++;
1217     if (height > prop.height + 3) ftheight++;
1218    
1219 root 1.41 ftheight -= height - prop.height;
1220 root 1.1 }
1221     else
1222 root 1.41 ftheight = prop.height - 1;
1223    
1224 root 1.74 XftFontClose (disp, f);
1225     FcPatternDel (match, FC_PIXEL_SIZE);
1226     FcPatternAddInteger (match, FC_PIXEL_SIZE, ftheight);
1227 root 1.1 }
1228    
1229 root 1.41 FcPatternDestroy (match);
1230    
1231     #if 0 // do it per-character
1232     if (prop.width != rxvt_fontprop::unset && width > prop.width)
1233     {
1234     clear ();
1235     success = false;
1236     }
1237     #endif
1238 root 1.1
1239 root 1.41 return success;
1240 root 1.1 }
1241    
1242     bool
1243 root 1.41 rxvt_font_xft::has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful)
1244 root 1.1 {
1245 root 1.41 careful = false;
1246    
1247     if (!XftCharExists (DISPLAY, f, unicode))
1248     return false;
1249    
1250     if (!prop || prop->width == rxvt_fontprop::unset)
1251     return true;
1252    
1253     // check character against base font bounding box
1254     FcChar32 ch = unicode;
1255     XGlyphInfo g;
1256     XftTextExtents32 (DISPLAY, f, &ch, 1, &g);
1257    
1258 root 1.72 int w = g.width - g.x;
1259 root 1.41 int wcw = wcwidth (unicode);
1260 root 1.72 if (wcw > 0) w = (w + wcw - 1) / wcw;
1261 root 1.41
1262     careful = w > prop->width;
1263     if (careful && w > prop->width * MAX_OVERLAP >> 2)
1264     return false;
1265    
1266     return true;
1267 root 1.1 }
1268    
1269     void
1270     rxvt_font_xft::draw (rxvt_drawable &d, int x, int y,
1271     const text_t *text, int len,
1272     int fg, int bg)
1273     {
1274 root 1.75 clear_rect (d, x, y, r->fwidth * len, r->fheight, bg);
1275 root 1.1
1276 root 1.41 XGlyphInfo extents;
1277 root 1.80 XftGlyphSpec *enc = (XftGlyphSpec *) get_enc_buf (len * sizeof (XftGlyphSpec));
1278     XftGlyphSpec *ep = enc;
1279    
1280 root 1.81 // cut trailing spaces
1281 root 1.80 while (len && text [len - 1] == ' ')
1282     len--;
1283 root 1.41
1284     while (len)
1285     {
1286 root 1.75 int cwidth = r->fwidth;
1287 root 1.41 FcChar32 fc = *text++; len--;
1288    
1289     while (len && *text == NOCHAR)
1290 root 1.75 text++, len--, cwidth += r->fwidth;
1291 root 1.41
1292 root 1.80 if (fc != ' ') // skip spaces
1293 root 1.71 {
1294     FT_UInt gl = XftCharIndex (d.display->display, f, fc);
1295     XftGlyphExtents (d.display->display, f, &gl, 1, &extents);
1296    
1297 root 1.80 ep->glyph = gl;
1298     ep->x = x + (cwidth - extents.xOff >> 1);
1299 root 1.81 ep->y = y + ascent;
1300 root 1.80 ep++;
1301     }
1302 root 1.1
1303 root 1.80 x += cwidth;
1304 root 1.1 }
1305 root 1.41
1306     if (ep != enc)
1307 root 1.80 XftDrawGlyphSpec (d, &r->pix_colors[fg].c, f, enc, ep - enc);
1308 root 1.1 }
1309     #endif
1310    
1311     /////////////////////////////////////////////////////////////////////////////
1312    
1313     rxvt_fontset::rxvt_fontset (rxvt_t r)
1314 root 1.73 : fontdesc (0), r (r)
1315 root 1.1 {
1316     clear ();
1317     }
1318    
1319     rxvt_fontset::~rxvt_fontset ()
1320     {
1321     clear ();
1322     }
1323    
1324     void
1325     rxvt_fontset::clear ()
1326     {
1327 root 1.76 prop.width = prop.height = prop.ascent = prop.weight = prop.slant
1328 root 1.46 = rxvt_fontprop::unset;
1329    
1330 root 1.1 for (rxvt_font **i = fonts.begin (); i != fonts.end (); i++)
1331     FONT_UNREF (*i);
1332    
1333 root 1.41 for (pagemap **p = fmap.begin (); p != fmap.end (); p++)
1334     delete *p;
1335    
1336 root 1.1 free (fontdesc); fontdesc = 0;
1337    
1338     fonts.clear ();
1339    
1340     fallback = fallback_fonts;
1341     }
1342    
1343     rxvt_font *
1344     rxvt_fontset::new_font (const char *name, codeset cs)
1345     {
1346     rxvt_font *f;
1347    
1348     if (!name || !*name)
1349     {
1350     name = "";
1351 root 1.41 f = new rxvt_font_default (this);
1352 root 1.1 }
1353     #if XFT
1354     else if (!strncmp (name, "xft:", 4))
1355     {
1356     name += 4;
1357 root 1.41 f = new rxvt_font_xft ();
1358 root 1.1 }
1359     #endif
1360     else if (!strncmp (name, "x:", 2))
1361     {
1362     name += 2;
1363     f = new rxvt_font_x11;
1364     }
1365     else
1366     f = new rxvt_font_x11;
1367    
1368     f->set_term (r);
1369     f->set_name (strdup (name));
1370    
1371     f->cs = cs;
1372     f->loaded = false;
1373    
1374     return f;
1375     }
1376    
1377     /////////////////////////////////////////////////////////////////////////////
1378    
1379     void
1380     rxvt_fontset::add_fonts (const char *desc)
1381     {
1382     if (desc)
1383     {
1384     char buf[512];
1385     const char *end;
1386    
1387     do
1388     {
1389 root 1.67 while (*desc && *desc <= ' ')
1390     desc++;
1391 root 1.1
1392 root 1.41 codeset cs = CS_UNICODE;
1393    
1394 root 1.1 if (*desc == '[')
1395     {
1396 root 1.41 char spec[256];
1397     const char *extra = ++desc; // not yet used
1398 root 1.1
1399     desc = strchr (desc, ']');
1400    
1401     if (!desc)
1402     {
1403 root 1.41 rxvt_warn ("ERROR: opening '[' without closing ']' in font specification, trying to continue.\n");
1404 root 1.1 break;
1405     }
1406    
1407 root 1.41 memcpy (spec, extra, min (desc - extra, 255));
1408     spec[min (desc - extra, 255)] = 0;
1409    
1410     if (!strncmp (extra, "codeset=", sizeof ("codeset=") - 1))
1411     cs = codeset_from_name (spec + sizeof ("codeset=") - 1);
1412     else
1413     rxvt_warn ("unknown parameter '%s' in font specification, skipping.\n", spec);
1414    
1415 root 1.1 desc++;
1416     while (*desc <= ' ') desc++;
1417     }
1418    
1419     end = strchr (desc, ',');
1420     if (!end)
1421     end = desc + strlen (desc);
1422    
1423     if (end - desc < 511)
1424     {
1425     strncpy (buf, desc, end - desc);
1426     buf[end - desc] = 0;
1427    
1428 root 1.41 fonts.push_back (new_font (buf, cs));
1429 root 1.1 }
1430 root 1.41 else
1431     rxvt_warn ("fontset element too long (>511 bytes), ignored.");
1432 root 1.1
1433     desc = end + 1;
1434     }
1435     while (*end);
1436     }
1437     }
1438    
1439     bool
1440     rxvt_fontset::realize_font (int i)
1441     {
1442 root 1.41 if (i < 0 || i >= fonts.size ())
1443     return false;
1444    
1445 root 1.1 if (fonts[i]->loaded)
1446     return true;
1447    
1448     fonts[i]->loaded = true;
1449    
1450 root 1.41 if (!fonts[i]->load (prop))
1451 root 1.1 {
1452     fonts[i]->cs = CS_UNKNOWN;
1453     return false;
1454     }
1455    
1456     return true;
1457     }
1458    
1459     bool
1460 root 1.46 rxvt_fontset::populate (const char *desc)
1461 root 1.1 {
1462     clear ();
1463    
1464     fontdesc = strdup (desc);
1465    
1466     fonts.push_back (new_font (0, CS_UNICODE));
1467     realize_font (0);
1468    
1469     add_fonts (desc);
1470    
1471     return true;
1472     }
1473    
1474     int
1475     rxvt_fontset::find_font (const char *name) const
1476     {
1477     for (rxvt_font *const *f = fonts.begin (); f < fonts.end (); f++)
1478     if ((*f)->name && !strcmp ((*f)->name, name))
1479     return f - fonts.begin ();
1480    
1481     return -1;
1482     }
1483    
1484     int
1485 root 1.41 rxvt_fontset::find_font (unicode_t unicode)
1486 root 1.1 {
1487 root 1.41 if (unicode >= 1<<20)
1488     return 0;
1489    
1490     unicode_t hi = unicode >> 8;
1491    
1492     if (hi < fmap.size ()
1493     && fmap[hi]
1494     && (*fmap[hi])[unicode & 0xff] != 0xff)
1495     return (*fmap[hi])[unicode & 0xff];
1496 root 1.1
1497 root 1.41 unsigned int i;
1498    
1499     for (i = 0; i < fonts.size (); i++)
1500 root 1.1 {
1501     rxvt_font *f = fonts[i];
1502    
1503     if (!f->loaded)
1504     {
1505     if (FROM_UNICODE (f->cs, unicode) == NOCHAR)
1506     goto next_font;
1507    
1508     if (!realize_font (i))
1509     goto next_font;
1510 root 1.76
1511     if (prop.ascent != rxvt_fontprop::unset)
1512     max_it (f->ascent, prop.ascent);
1513 root 1.1 }
1514    
1515     if (f->cs == CS_UNKNOWN)
1516     goto next_font;
1517    
1518 root 1.41 bool careful;
1519     if (f->has_char (unicode, &prop, careful))
1520     {
1521     if (careful)
1522     i |= 128;
1523 root 1.1
1524 root 1.41 goto found;
1525     }
1526 root 1.1
1527     next_font:
1528     if (i == fonts.size () - 1)
1529     {
1530     if (fallback->name)
1531     {
1532     // search through the fallback list
1533     fonts.push_back (new_font (fallback->name, fallback->cs));
1534     fallback++;
1535     }
1536 root 1.41 else
1537 root 1.1 {
1538     // try to find a new font.
1539     // only xft currently supported, as there is no
1540     // way to configure this and xft is easier to hack in,
1541     // while x11 has more framework in place already.
1542 root 1.41 // TODO: this is a real resource hog, xft takes ages(?)
1543     #if XFT && USE_SLOW_LOOKUP
1544 root 1.1 // grab the first xft font that seems suitable
1545     FcPattern *p = FcPatternCreate ();
1546    
1547     FcCharSet *s = FcCharSetCreate ();
1548     FcCharSetAddChar (s, unicode);
1549     FcPatternAddCharSet (p, FC_CHARSET, s);
1550     // charsets don't help that much, as xft might return
1551     // a non-matching font even if a better font is available :/
1552    
1553 root 1.41 x x x x TODO prop might have unset contents
1554     FcPatternAddInteger (p, FC_PIXEL_SIZE, prop.height);
1555     FcPatternAddInteger (p, FC_WEIGHT, prop.weight);
1556     FcPatternAddInteger (p, FC_SLANT, prop.slant);
1557 root 1.1 FcPatternAddBool (p, FC_MINSPACE, 1);
1558     //FcPatternAddBool (p, FC_ANTIALIAS, 1);
1559    
1560     XftResult result;
1561 root 1.66 FcPattern *match = XftFontMatch (DISPLAY, r->display->screen, p, &result);
1562 root 1.1
1563     FcPatternDestroy (p);
1564    
1565     if (match)
1566     {
1567     FcPatternDel (match, FC_CHARSET);
1568     char *font = (char *)FcNameUnparse (match);
1569     FcPatternDestroy (match);
1570    
1571     if (find_font (font) < 0)
1572     {
1573     char fontname[4096];
1574     sprintf (fontname, "xft:%-.4090s", font);
1575    
1576     fonts.push_back (new_font (fontname, CS_UNICODE));
1577     }
1578    
1579     free (font);
1580     }
1581     #endif
1582     }
1583     }
1584     }
1585    
1586 root 1.41 /* we must return SOME font */
1587     i = 0;
1588    
1589     found:
1590     // found a font, cache it
1591     if (i < 255)
1592     {
1593     while (hi >= fmap.size ())
1594     fmap.push_back (0);
1595    
1596     if (!fmap[hi])
1597     {
1598     fmap[hi] = (pagemap *)new pagemap;
1599     memset (fmap[hi], 0xff, sizeof (pagemap));
1600     }
1601    
1602     (*fmap[hi])[unicode & 0xff] = i;
1603     }
1604 root 1.1
1605 root 1.41 return i;
1606 root 1.1 }
1607    
1608    
1609