ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/rxvtfont.C
Revision: 1.4
Committed: Mon Aug 16 12:04:57 2004 UTC (19 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.3: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 /*--------------------------------*-C-*---------------------------------*
2     * File: defaultfont.C
3     *----------------------------------------------------------------------*
4     * Copyright (c) 2003-2004 Marc Lehmann <pcg@goof.com>
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 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     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20     *---------------------------------------------------------------------*/
21    
22     #include "../config.h"
23     #include "rxvt.h"
24     #include "rxvtfont.h"
25    
26     #include <cstdlib>
27    
28     #define DISPLAY r->display->display
29     #define TGC r->TermWin.gc
30    
31     const struct rxvt_fallback_font {
32     codeset cs;
33     const char *name;
34     } fallback_fonts[] = {
35     { CS_ISO8859_1, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-1" },
36     { CS_ISO8859_15, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-15" },
37     { CS_ISO8859_15, "-*-*-*-r-*--*-*-*-*-c-*-fcd8859-15" },
38    
39     #if ENCODING_EU
40     // cyrillic
41     { CS_KOI8_R, "-*-*-*-r-*--*-*-*-*-c-*-koi8-r" },
42     { CS_KOI8_U, "-*-*-*-r-*--*-*-*-*-c-*-koi8-u" },
43    
44     { CS_ISO8859_2, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-2" },
45     { CS_ISO8859_3, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-3" },
46     { CS_ISO8859_4, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-4" },
47     { CS_ISO8859_5, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-5" },
48     { CS_ISO8859_6, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-6" },
49     { CS_ISO8859_7, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-7" },
50     { CS_ISO8859_8, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-8" },
51     { CS_ISO8859_9, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-9" },
52     { CS_ISO8859_10, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-10" },
53     { CS_ISO8859_11, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-11" },
54     { CS_ISO8859_13, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-13" },
55     { CS_ISO8859_14, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-14" },
56     { CS_ISO8859_16, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-16" },
57     #endif
58    
59     // japanese
60     #if ENCODING_JP || ENCODING_JP_EXT
61     # if XFT
62     // prefer xft for complex scripts
63     { CS_UNICODE, "xft:Kochi Gothic:antialias=false" },
64     # endif
65     { CS_JIS0201_1976_0, "-*-mincho-*-r-*--*-*-*-*-c-*-jisx0201*-0" },
66     { CS_JIS0208_1990_0, "-*-mincho-*-r-*--*-*-*-*-c-*-jisx0208*-0" },
67     { CS_JIS0212_1990_0, "-*-mincho-*-r-*--*-*-*-*-c-*-jisx0212*-0" },
68     { CS_JIS0201_1976_0, "-*-*-*-r-*--*-*-*-*-c-*-jisx0201*-0" },
69     { CS_JIS0208_1990_0, "-*-*-*-r-*--*-*-*-*-c-*-jisx0208*-0" },
70     { CS_JIS0212_1990_0, "-*-*-*-r-*--*-*-*-*-c-*-jisx0212*-0" },
71     #endif
72    
73     #if ENCODING_CN || ENCODING_CN_EXT
74     # if XFT
75     { CS_BIG5_EXT, "xft:AR PL Mingti2L Big5" },
76     { CS_BIG5_EXT, "xft:AR PL KaitiM Big5" },
77     { CS_GB2312_1980_0, "xft:AR PL KaitiM GB" },
78     { CS_GB2312_1980_0, "xft:AR PL SungtiL GB" },
79     # endif
80     { CS_BIG5, "-*-*-*-*-*-*-*-*-*-*-c-*-big5-0" },
81     { CS_BIG5_PLUS, "-*-*-*-*-*-*-*-*-*-*-c-*-big5p-0" },
82     { CS_BIG5_EXT, "-*-*-*-*-*-*-*-*-*-*-c-*-big5.eten-0" },
83     { CS_CNS11643_1992_1, "-*-*-*-*-*-*-*-*-*-*-c-*-gb2312*-0" },
84     { CS_CNS11643_1992_1, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-1" },
85     { CS_CNS11643_1992_2, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-2" },
86     { CS_CNS11643_1992_3, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-3" },
87     { CS_CNS11643_1992_4, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-4" },
88     { CS_CNS11643_1992_5, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-5" },
89     { CS_CNS11643_1992_6, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-6" },
90     { CS_CNS11643_1992_7, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-7" },
91     { CS_CNS11643_1992_F, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-f" },
92     #endif
93    
94     #if XFT
95     { CS_UNICODE, "xft:Andale Mono" },
96     { CS_UNICODE, "xft:Arial Unicode MS" },
97     #endif
98     { CS_UNICODE, "-*-lucidatypewriter-*-*-*-*-*-*-*-*-m-*-iso10646-1" },
99     { CS_UNICODE, "-*-unifont-*-*-*-*-*-*-*-*-c-*-iso10646-1" },
100     { CS_UNICODE, "-*-*-*-r-*-*-*-*-*-*-c-*-iso10646-1" },
101     { CS_UNICODE, "-*-*-*-r-*-*-*-*-*-*-m-*-iso10646-1" },
102     #if XFT
103     // FreeMono is usually uglier than x fonts, so try last only.
104     //{ CS_UNICODE, "xft:FreeMono" },
105     #endif
106    
107     { CS_UNKNOWN, 0 }
108     };
109    
110     /////////////////////////////////////////////////////////////////////////////
111    
112     #if XFT
113     rxvt_drawable::~rxvt_drawable ()
114     {
115     if (xftdrawable)
116     XftDrawDestroy (xftdrawable);
117     }
118    
119     rxvt_drawable::operator XftDraw *()
120     {
121     if (!xftdrawable)
122     xftdrawable = XftDrawCreate (display->display, drawable, display->visual, display->cmap);
123    
124     return xftdrawable;
125     }
126     #endif
127    
128     /////////////////////////////////////////////////////////////////////////////
129    
130     static void *enc_buf;
131     static uint32_t enc_len;
132    
133     static inline void *
134     get_enc_buf (uint32_t len)
135     {
136     if (len > enc_len)
137     {
138     free (enc_buf);
139     enc_buf = malloc (len);
140     enc_len = len;
141     }
142    
143     return enc_buf;
144     }
145    
146     static const char *
147     enc_char (const text_t *text, uint32_t len, codeset cs, bool &zero)
148     {
149     uint8_t *buf = (uint8_t *)get_enc_buf (len);
150    
151     while (len--)
152     {
153     uint32_t c = FROM_UNICODE (cs, *text++);
154    
155     if (c == NOCHAR)
156     {
157     c = 0;
158     zero = true;
159     }
160    
161     *buf++ = c;
162     }
163    
164     return (const char *)enc_buf;
165     }
166    
167     static const XChar2b *
168     enc_xchar2b (const text_t *text, uint32_t len, codeset cs, bool &zero)
169     {
170     XChar2b *buf = (XChar2b *)get_enc_buf (len * sizeof (XChar2b));
171    
172     while (len--)
173     {
174     uint32_t c = FROM_UNICODE (cs, *text++);
175    
176     if (c == NOCHAR)
177     {
178     c = 0;
179     zero = true;
180     }
181    
182     buf->byte1 = c >> 8;
183     buf->byte2 = c;
184     buf++;
185     }
186    
187     return (XChar2b *)enc_buf;
188     }
189    
190     /////////////////////////////////////////////////////////////////////////////
191    
192     void
193     rxvt_font::clear_rect (rxvt_drawable &d, int x, int y, int w, int h, int color)
194     {
195     if (color == Color_bg)
196     XClearArea (d.display->display, d, x, y, w, h, FALSE);
197     else if (color >= 0)
198     {
199     #if XFT
200 root 1.3 XftDrawRect (d, &r->pix_colors[color].c, x, y, w, h);
201 root 1.1 #else
202 root 1.3 XSetForeground (d.display->display, TGC, r->pix_colors[color]);
203 root 1.1 XFillRectangle (d.display->display, d, TGC, x, y, w, h);
204     #endif
205     }
206     }
207    
208     #include "table/linedraw.h"
209    
210     struct rxvt_font_default : rxvt_font {
211    
212     rxvt_fontprop properties ()
213     {
214     rxvt_fontprop p;
215    
216     p.width = p.height = 1;
217     p.weight = rxvt_fontprop::medium;
218     p.slant = rxvt_fontprop::roman;
219    
220     return p;
221     }
222    
223     bool load (const rxvt_fontprop &prop)
224     {
225     width = 1; height = 1;
226     ascent = 1; descent = 0;
227    
228     set_name (strdup ("built-in pseudofont"));
229    
230     return true;
231     }
232    
233     bool has_codepoint (unicode_t unicode)
234     {
235     if (unicode <= 0x001f)
236     return true;
237    
238     if (unicode <= 0x007f)
239     return false;
240    
241     if (unicode <= 0x009f)
242     return true;
243    
244     if (unicode >= 0x2500 && unicode <= 0x259f)
245     return true;
246    
247     if (IS_COMPOSE (unicode))
248     return true;
249    
250     switch (unicode)
251     {
252     case ZERO_WIDTH_CHAR:
253     return true;
254     }
255    
256     return false;
257     }
258    
259     void draw (rxvt_drawable &d, int x, int y,
260     const text_t *text, int len,
261     int fg, int bg);
262     };
263    
264     void
265     rxvt_font_default::draw (rxvt_drawable &d, int x, int y,
266     const text_t *text, int len,
267     int fg, int bg)
268     {
269     clear_rect (d, x, y, r->TermWin.fwidth * len, r->TermWin.fheight, bg);
270    
271 root 1.3 XSetForeground (d.display->display, TGC, r->pix_colors[fg]);
272 root 1.1
273     while (len--)
274     {
275     #if ENABLE_COMBINING
276     compose_char *cc;
277     #endif
278     text_t t = *text++;
279    
280     if (0x2500 <= t && t <= 0x259f)
281     {
282     uint16_t offs = linedraw_offs[t - 0x2500];
283     uint32_t *a = linedraw_command + (offs >> 4);
284     uint32_t *b = a + (offs & 15);
285    
286     int W = r->TermWin.fwidth;
287     int H = r->TermWin.fheight;
288    
289     int x_[16];
290     int y_[16];
291    
292     for (int i = 0; i <= 8; i++)
293     {
294     x_[i] = x + ((W-1) * i + (i*7/8)) / 8;
295     y_[i] = y + ((H-1) * i + (i*7/8)) / 8;
296     }
297    
298     x_[10] = x + (W - 1) / 2; x_[9] = x_[10] - 1; x_[11] = x_[10] + 1;
299     y_[10] = y + (H - 1) / 2; y_[9] = y_[10] - 1; y_[11] = y_[10] + 1;
300    
301     XGCValues gcv;
302    
303     gcv.cap_style = CapButt;
304     gcv.line_width = 0;
305     XChangeGC (d.display->display, TGC, GCLineWidth | GCCapStyle, &gcv);
306    
307     while (a < b)
308     {
309     uint32_t command = *a++;
310    
311     int op = (command >> 24) & 255;
312     int a = (command >> 20) & 15;
313     int b = (command >> 16) & 15;
314     int x1 = x_[(command >> 12) & 15];
315     int y1 = y_[(command >> 8) & 15];
316     int x2 = x_[(command >> 4) & 15];
317     int y2 = y_[(command >> 0) & 15];
318    
319     switch (op)
320     {
321     case 0: // line
322     XDrawLine (d.display->display, d, TGC, x1, y1, x2, y2);
323     break;
324    
325     case 1: // rectangle, possibly stippled
326     if (a)
327     {
328     static char bm[] = { 0,0 , 3,1 , 1,2 , 1,0 };
329    
330     gcv.fill_style = FillStippled;
331     gcv.stipple = XCreateBitmapFromData (d.display->display, d, bm + a * 2, 2, 2);
332     gcv.ts_x_origin = x;
333     gcv.ts_y_origin = y;
334    
335     XChangeGC (d.display->display, TGC,
336     GCFillStyle | GCStipple | GCTileStipXOrigin | GCTileStipYOrigin,
337     &gcv);
338     }
339    
340     XFillRectangle (d.display->display, d, TGC, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
341    
342     if (a)
343     {
344     XFreePixmap (d.display->display, gcv.stipple);
345     gcv.stipple = 0;
346     gcv.fill_style = FillSolid;
347     XChangeGC (d.display->display, TGC, GCFillStyle, &gcv);
348     }
349     break;
350     case 2: // arc
351     XDrawArc (d.display->display, d, TGC,
352     x1 - W/2, y1 - H/2, W-1, H-1,
353     (a - 1) * 90*64, (b - 1) * 90*64);
354     break;
355     }
356     }
357     }
358     #if ENABLE_COMBINING
359     else if (IS_COMPOSE (t) && (cc = rxvt_composite[t]))
360     {
361     rxvt_font *f1 = (*fs)[fs->find_font (cc->c1)];
362     f1->draw (d, x, y, &(t = cc->c1), 1, fg, bg);
363     if (cc->c2 != NOCHAR)
364     {
365     // prefer font of first character, for no good reasons
366     rxvt_font *f2 = f1->has_codepoint (cc->c2)
367 root 1.2 ? f1
368     : (*fs)[fs->find_font (cc->c2)];
369 root 1.1
370     f2->draw (d, x, y, &(t = cc->c2), 1, fg, -1);
371     }
372     }
373     #endif
374     else
375     switch (t)
376     {
377     case ZERO_WIDTH_CHAR:
378     break;
379    
380     default:
381     int w = 0;
382     while (len > 0 && *text == NOCHAR)
383     {
384     ++text;
385     --len;
386     w += r->TermWin.fwidth;
387     }
388    
389     XDrawRectangle (d.display->display, d, TGC, x + 2, y + 2,
390     w + r->TermWin.fwidth - 4, r->TermWin.fheight - 4);
391     x += w;
392     }
393    
394     x += r->TermWin.fwidth;
395     }
396     }
397    
398     /////////////////////////////////////////////////////////////////////////////
399    
400     struct rxvt_font_x11 : rxvt_font {
401     rxvt_font_x11 () { f = 0; }
402    
403     void clear ();
404    
405     rxvt_fontprop properties ();
406    
407     bool load (const rxvt_fontprop &prop);
408    
409     bool has_codepoint (unicode_t unicode);
410    
411     void draw (rxvt_drawable &d, int x, int y,
412     const text_t *text, int len,
413     int fg, int bg);
414    
415     XFontStruct *f;
416     codeset cs;
417     bool enc2b, encm;
418    
419     char *get_property (XFontStruct *f, const char *property, const char *repl) const;
420     bool set_properties (rxvt_fontprop &p, int height, const char *weight, const char *slant, int avgwidth);
421     bool set_properties (rxvt_fontprop &p, XFontStruct *f);
422     bool set_properties (rxvt_fontprop &p, const char *name);
423     };
424    
425     char *
426     rxvt_font_x11::get_property (XFontStruct *f, const char *property, const char *repl) const
427     {
428     unsigned long value;
429    
430     if (XGetFontProperty (f, XInternAtom (DISPLAY, property, 0), &value))
431     return XGetAtomName (DISPLAY, value);
432     else
433     return rxvt_strdup (repl);
434     }
435    
436     rxvt_fontprop
437     rxvt_font_x11::properties ()
438     {
439     rxvt_fontprop p;
440     set_properties (p, f);
441     return p;
442     }
443    
444     bool
445     rxvt_font_x11::set_properties (rxvt_fontprop &p, int height, const char *weight, const char *slant, int avgwidth)
446     {
447     p.width = avgwidth ? (avgwidth + 1) / 10 : (height + 1) / 2;
448     p.height = height;
449     p.weight = *weight == 'B' || *weight == 'b' ? rxvt_fontprop::bold : rxvt_fontprop::medium;
450     p.slant = *slant == 'r' || *slant == 'R' ? rxvt_fontprop::roman : rxvt_fontprop::italic;
451    
452     return true;
453     }
454    
455     bool
456     rxvt_font_x11::set_properties (rxvt_fontprop &p, XFontStruct *f)
457     {
458     unsigned long height;
459     if (!XGetFontProperty (f, XInternAtom (DISPLAY, "PIXEL_SIZE", 0), &height))
460     return false;
461    
462     unsigned long avgwidth;
463     if (!XGetFontProperty (f, XInternAtom (DISPLAY, "AVERAGE_WIDTH", 0), &avgwidth))
464     avgwidth = 0;
465    
466     char *weight = get_property (f, "WEIGHT_NAME", "medium");
467     char *slant = get_property (f, "SLANT", "r");
468    
469     set_properties (p, height, weight, slant, avgwidth);
470    
471     free (weight);
472     free (slant);
473    
474     return true;
475     }
476    
477     bool
478     rxvt_font_x11::set_properties (rxvt_fontprop &p, const char *name)
479     {
480     int slashes = 0;
481     const char *comp[13];
482    
483     for (const char *c = name; *c; c++)
484     if (*c == '-')
485     {
486     comp[slashes++] = c + 1;
487     if (slashes >= 13)
488     break;
489     }
490    
491     /* can we short-circuit the costly XLoadQueryFont? */
492     if (slashes >= 13
493     && (*comp[ 6] >= '1' && *comp[ 6] <= '9')
494     && (*comp[11] >= '0' && *comp[11] <= '9'))
495     return set_properties (p, atoi (comp[6]), comp[2], comp[3], atoi (comp[11]));
496    
497     XFontStruct *f = XLoadQueryFont (DISPLAY, name);
498    
499     if (f)
500     {
501     // the font should really exist now. if not, we have a problem
502     // (e.g. if the user did xset fp rehash just when we were searching fonts).
503     // in that case, just return garbage.
504     bool ret = set_properties (p, f);
505     XFreeFont (DISPLAY, f);
506     return ret;
507     }
508     else
509     return false;
510     }
511    
512     // fix the size of scalable fonts
513     static void
514     fix_scalable (char *buf, const char *name, const rxvt_fontprop &prop)
515     {
516     int slashes = 0;
517     const char *size;
518    
519     for (const char *c = name; *c; c++)
520     if (*c == '-')
521     {
522     if (slashes == 6)
523     size = c + 1;
524    
525     if (++slashes >= 13)
526     break;
527     }
528    
529     if (slashes >= 13 && size[0] == '0')
530     {
531     strncpy (buf, name, size - name);
532     buf += size - name;
533     buf += sprintf (buf, "%d", prop.height);
534     strcpy (buf, size + 1);
535     }
536     else
537     strcpy (buf, name);
538     }
539    
540     bool
541     rxvt_font_x11::load (const rxvt_fontprop &prop)
542     {
543     clear ();
544    
545     char **list;
546     int count;
547     list = XListFonts (DISPLAY, name, 1024, &count);
548     set_name (0);
549    
550     if (!list)
551     return false;
552    
553     int bestdiff = 0x7fffffff;
554     for (int i = 0; i < count; i++)
555     {
556     rxvt_fontprop p;
557     char fname[1024];
558     fix_scalable (fname, list[i], prop);
559    
560     if (!set_properties (p, fname))
561     continue;
562    
563     if (p.height > prop.height) // weed out too large fonts
564     continue;
565    
566     int diff = (prop.height - p.height) * 32
567     + abs (prop.weight - p.weight)
568     + abs (prop.slant - p.slant );
569    
570     if (!name // compare against best found so far
571     || diff < bestdiff)
572     {
573     set_name (strdup (fname));
574     bestdiff = diff;
575     }
576     }
577    
578     XFreeFontNames (list);
579    
580     if (!name)
581     return false;
582    
583     f = XLoadQueryFont (DISPLAY, name);
584    
585     if (!f)
586     return false;
587    
588     char *registry = get_property (f, "CHARSET_REGISTRY", 0);
589     char *encoding = get_property (f, "CHARSET_ENCODING", 0);
590    
591     if (registry && encoding)
592     {
593     char charset[64];
594     snprintf (charset, 64, "%s-%s", registry, encoding);
595    
596     cs = codeset_from_name (charset);
597     }
598     else
599     {
600     const char *charset = get_property (f, "FONT", 0);
601    
602     if (!charset)
603     charset = name;
604    
605     int count = 13;
606     while (*charset)
607     if (*charset++ == '-' && !--count)
608     break;
609    
610     cs = codeset_from_name (charset);
611     }
612    
613     free (registry);
614     free (encoding);
615    
616     if (cs == CS_UNICODE)
617     cs = CS_UNICODE_16; // X11 can have a max. of 65536 chars per font
618    
619     encm = f->min_byte1 != 0 || f->max_byte1 != 0;
620     enc2b = encm || f->max_char_or_byte2 > 255;
621    
622     ascent = f->ascent;
623     descent = f->descent;
624     height = ascent + descent;
625    
626     slow = false;
627    
628     if (f->min_bounds.width == f->max_bounds.width)
629     width = f->min_bounds.width;
630     else if (f->per_char == NULL)
631     width = f->max_bounds.width;
632     else
633     {
634     slow = true;
635    
636     int N = f->max_char_or_byte2 - f->min_char_or_byte2;
637    
638     if (encm)
639     N += (f->max_byte1 - f->min_byte1)
640     * (f->max_char_or_byte2 - f->min_char_or_byte2 + 1);
641    
642     while (N)
643     {
644     if (f->per_char[N].width > width)
645     width = f->per_char[N].width;
646    
647     --N;
648     }
649     }
650    
651     if (cs == CS_UNKNOWN)
652     {
653     fprintf (stderr, "unable to deduce codeset, ignoring font '%s'\n", name);
654    
655     clear ();
656    
657     return false;
658     }
659    
660     return true;
661     }
662    
663     void
664     rxvt_font_x11::clear ()
665     {
666     if (f)
667     {
668     XFreeFont (DISPLAY, f);
669     f = 0;
670     }
671     }
672    
673     bool
674     rxvt_font_x11::has_codepoint (unicode_t unicode)
675     {
676     uint32_t ch = FROM_UNICODE (cs, unicode);
677    
678     if (ch == NOCHAR)
679     return false;
680    
681     /* check wether the character exists in _this_ font. horrible. */
682     XCharStruct *xcs;
683    
684     if (encm)
685     {
686     unsigned char byte1 = ch >> 8;
687     unsigned char byte2 = ch & 255;
688    
689     if (byte1 < f->min_byte1 || byte1 > f->max_byte1
690     || byte2 < f->min_char_or_byte2 || byte2 > f->max_char_or_byte2)
691     return false;
692    
693     if (!f->per_char)
694     return true;
695    
696     int D = f->max_char_or_byte2 - f->min_char_or_byte2 + 1;
697     int N = (byte1 - f->min_byte1) * D + byte2 - f->min_char_or_byte2;
698    
699     xcs = f->per_char + N;
700     }
701     else
702     {
703     if (ch < f->min_char_or_byte2 || ch > f->max_char_or_byte2)
704     return false;
705    
706     if (!f->per_char)
707     return true;
708    
709     xcs = f->per_char + (ch - f->min_char_or_byte2);
710     }
711    
712     if (xcs->lbearing == 0 && xcs->rbearing == 0 && xcs->width == 0
713     && xcs->ascent == 0 && xcs->descent == 0)
714     return false;
715    
716     return true;
717     }
718    
719     void
720     rxvt_font_x11::draw (rxvt_drawable &d, int x, int y,
721     const text_t *text, int len,
722     int fg, int bg)
723     {
724     // this looks like a mess /.
725     // and it is a mess /.
726     // yet we are trying to be perfect /.
727     // but the result still isn't perfect /.
728    
729     bool slow = this->slow
730     || width != r->TermWin.fwidth
731     || height != r->TermWin.fheight;
732    
733     int base = r->TermWin.fbase;
734    
735     XGCValues v;
736 root 1.3 v.foreground = r->pix_colors[fg];
737 root 1.1 v.font = f->fid;
738    
739     if (enc2b)
740     {
741     const XChar2b *xc = enc_xchar2b (text, len, cs, slow);
742    
743     if (bg == Color_bg && !slow)
744     {
745 root 1.3 v.background = r->pix_colors[bg];
746 root 1.1 XChangeGC (d.display->display, TGC, GCForeground | GCBackground | GCFont, &v);
747     XDrawImageString16 (d.display->display, d, TGC, x, y + base, xc, len);
748     }
749     else
750     {
751     clear_rect (d, x, y, r->TermWin.fwidth * len, r->TermWin.fheight, bg);
752    
753     XChangeGC (d.display->display, TGC, GCForeground | GCFont, &v);
754    
755     if (slow)
756     {
757     do
758     {
759     if (xc->byte1 || xc->byte2)
760     XDrawString16 (d.display->display, d, TGC, x, y + base, xc, 1);
761    
762     x += r->TermWin.fwidth;
763     xc++; len--;
764     }
765     while (len);
766     }
767     else
768     XDrawString16 (d.display->display, d, TGC, x, y + base, xc, len);
769     }
770     }
771     else
772     {
773     const char *xc = enc_char (text, len, cs, slow);
774    
775     if (bg == Color_bg && !slow)
776     {
777 root 1.3 v.background = r->pix_colors[bg];
778 root 1.1 XChangeGC (d.display->display, TGC, GCForeground | GCBackground | GCFont, &v);
779     XDrawImageString (d.display->display, d, TGC, x, y + base, xc, len);
780     }
781     else
782     {
783     clear_rect (d, x, y, r->TermWin.fwidth * len, r->TermWin.fheight, bg);
784    
785     XChangeGC (d.display->display, TGC, GCForeground | GCFont, &v);
786    
787     if (slow)
788     {
789     do
790     {
791     if (*xc)
792     XDrawString (d.display->display, d, TGC, x, y + base, xc, 1);
793    
794     x += r->TermWin.fwidth;
795     xc++; len--;
796     }
797     while (len);
798     }
799     else
800     XDrawString (d.display->display, d, TGC, x, y + base, xc, len);
801     }
802     }
803     }
804    
805     /////////////////////////////////////////////////////////////////////////////
806    
807     #if XFT
808     #if 0
809     #define UNIBITS 21
810     //#define SWATHBITS (UNIBITS / 2 + 3) // minimum size for "full" tables
811     #define SWATHBITS 8
812     #endif
813    
814     struct rxvt_font_xft : rxvt_font {
815     rxvt_font_xft () { f = 0; }
816    
817     void clear ();
818    
819     rxvt_fontprop properties ();
820    
821     bool load (const rxvt_fontprop &prop);
822    
823     void draw (rxvt_drawable &d, int x, int y,
824     const text_t *text, int len,
825     int fg, int bg);
826    
827     bool has_codepoint (unicode_t unicode);
828    
829     protected:
830     XftFont *f;
831     };
832    
833     void
834     rxvt_font_xft::clear ()
835     {
836     if (f)
837     {
838     XftFontClose (DISPLAY, f);
839     f = 0;
840     }
841     }
842    
843     rxvt_fontprop
844     rxvt_font_xft::properties ()
845     {
846     rxvt_fontprop p;
847    
848     FT_Face face = XftLockFace (f);
849    
850     p.width = width;
851     p.height = height;
852     p.weight = face->style_flags & FT_STYLE_FLAG_BOLD
853     ? rxvt_fontprop::bold : rxvt_fontprop::medium;
854     p.slant = face->style_flags & FT_STYLE_FLAG_ITALIC
855     ? rxvt_fontprop::italic : rxvt_fontprop::roman;
856    
857     XftUnlockFace (f);
858    
859     return p;
860     }
861    
862     bool
863     rxvt_font_xft::load (const rxvt_fontprop &prop)
864     {
865     #if 0
866     for (int i = 0; i < SWATHCOUNT; i++)
867     cvr[i] = 0;
868     #endif
869    
870     clear ();
871    
872     FcPattern *p = FcNameParse ((FcChar8 *) name);
873    
874     if (!p)
875     return false;
876    
877     FcValue v;
878    
879     if (FcPatternGet (p, FC_PIXEL_SIZE, 0, &v) != FcResultMatch)
880     FcPatternAddInteger (p, FC_PIXEL_SIZE, prop.height);
881    
882     if (FcPatternGet (p, FC_WEIGHT, 0, &v) != FcResultMatch)
883     FcPatternAddInteger (p, FC_WEIGHT, prop.weight);
884    
885     if (FcPatternGet (p, FC_SLANT, 0, &v) != FcResultMatch)
886     FcPatternAddInteger (p, FC_SLANT, prop.slant);
887    
888     if (FcPatternGet (p, FC_MINSPACE, 0, &v) != FcResultMatch)
889     FcPatternAddBool (p, FC_MINSPACE, 1);
890    
891     #if 0 // clipping unfortunately destroys our precious double-width-characters
892     // clip width, we can't do better, or can we?
893     if (FcPatternGet (p, FC_CHAR_WIDTH, 0, &v) != FcResultMatch)
894     FcPatternAddInteger (p, FC_CHAR_WIDTH, prop.width);
895     #endif
896    
897     XftResult result;
898     FcPattern *match = XftFontMatch (DISPLAY, DefaultScreen (DISPLAY), p, &result);
899    
900     FcPatternDestroy (p);
901    
902     if (!match)
903     return false;
904    
905     f = XftFontOpenPattern (DISPLAY, match);
906    
907     if (!f)
908     {
909     FcPatternDestroy (match);
910     return false;
911     }
912    
913     FT_Face face = XftLockFace (f);
914    
915     slow = !FT_IS_FIXED_WIDTH (face);
916    
917     int ftheight = 0;
918    
919     for (;;)
920     {
921     XGlyphInfo g1, g2;
922     FcChar8 c;
923    
924     c = 'i'; XftTextExtents8 (DISPLAY, f, &c, 1, &g1);
925     c = 'W'; XftTextExtents8 (DISPLAY, f, &c, 1, &g2);
926    
927     if (g1.xOff != g2.xOff) // don't simply trust the font
928     slow = true;
929    
930     width = g2.xOff;
931     ascent = (face->size->metrics.ascender + 63) >> 6;
932     descent = (-face->size->metrics.descender + 63) >> 6;
933     height = ascent + descent;
934    
935     if (height <= prop.height || !prop.height)
936     break;
937    
938     if (ftheight)
939     {
940     // take smaller steps near the end
941     if (height > prop.height + 1) ftheight++;
942     if (height > prop.height + 2) ftheight++;
943     if (height > prop.height + 3) ftheight++;
944    
945     FT_Set_Pixel_Sizes (face, 0, ftheight -= height - prop.height);
946     }
947     else
948     FT_Set_Pixel_Sizes (face, 0, ftheight = prop.height);
949     }
950    
951     XftUnlockFace (f);
952    
953     return true;
954     }
955    
956     bool
957     rxvt_font_xft::has_codepoint (unicode_t unicode)
958     {
959     return XftCharExists (DISPLAY, f, unicode);
960     }
961    
962     void
963     rxvt_font_xft::draw (rxvt_drawable &d, int x, int y,
964     const text_t *text, int len,
965     int fg, int bg)
966     {
967     clear_rect (d, x, y, r->TermWin.fwidth * len, r->TermWin.fheight, bg);
968    
969     if (!slow && width == r->TermWin.fwidth && 0)
970     {
971     if (sizeof (text_t) == sizeof (FcChar16))
972 root 1.3 XftDrawString16 (d, &r->pix_colors[fg].c, f, x, y + r->TermWin.fbase, (const FcChar16 *)text, len);
973 root 1.1 else
974 root 1.3 XftDrawString32 (d, &r->pix_colors[fg].c, f, x, y + r->TermWin.fbase, (const FcChar32 *)text, len);
975 root 1.1 }
976     else
977     {
978     while (len)
979     {
980     if (*text != NOCHAR && *text != ' ')
981     {
982     int fwidth = r->TermWin.fwidth;
983     if (len >= 2 && text[1] == NOCHAR)
984     fwidth *= 2;
985    
986     XGlyphInfo extents;
987     if (sizeof (text_t) == sizeof (FcChar16))
988     {
989     XftTextExtents16 (d.display->display, f, (const FcChar16 *)text, 1, &extents);
990 root 1.3 XftDrawString16 (d, &r->pix_colors[fg].c, f, x + extents.x + (fwidth - extents.width) / 2,
991 root 1.1 y + r->TermWin.fbase, (const FcChar16 *)text, 1);
992     }
993     else
994     {
995     XGlyphInfo extents;
996     XftTextExtents32 (d.display->display, f, (const FcChar32 *)text, 1, &extents);
997 root 1.3 XftDrawString32 (d, &r->pix_colors[fg].c, f, x + extents.x + (fwidth - extents.width) / 2,
998 root 1.1 y + r->TermWin.fbase, (const FcChar32 *)text, 1);
999     }
1000     }
1001    
1002     x += r->TermWin.fwidth;
1003     text++;
1004     len--;
1005     }
1006     }
1007     }
1008     #endif
1009    
1010     /////////////////////////////////////////////////////////////////////////////
1011    
1012     rxvt_fontset::rxvt_fontset (rxvt_t r)
1013     : r (r), fontdesc (0)
1014     {
1015     clear ();
1016     }
1017    
1018     rxvt_fontset::~rxvt_fontset ()
1019     {
1020     clear ();
1021     }
1022    
1023     void
1024     rxvt_fontset::clear ()
1025     {
1026     for (rxvt_font **i = fonts.begin (); i != fonts.end (); i++)
1027     FONT_UNREF (*i);
1028    
1029     free (fontdesc); fontdesc = 0;
1030    
1031     fonts.clear ();
1032     base_id = 0;
1033     base_prop.height = 0x7fffffff;
1034     base_prop.weight = rxvt_fontprop::medium;
1035     base_prop.slant = rxvt_fontprop::roman;
1036    
1037     fallback = fallback_fonts;
1038     }
1039    
1040     rxvt_font *
1041     rxvt_fontset::new_font (const char *name, codeset cs)
1042     {
1043     rxvt_font *f;
1044    
1045     if (!name || !*name)
1046     {
1047     name = "";
1048     f = new rxvt_font_default;
1049     }
1050     #if XFT
1051     else if (!strncmp (name, "xft:", 4))
1052     {
1053     name += 4;
1054     f = new rxvt_font_xft;
1055     }
1056     #endif
1057     else if (!strncmp (name, "x:", 2))
1058     {
1059     name += 2;
1060     f = new rxvt_font_x11;
1061     }
1062     else
1063     f = new rxvt_font_x11;
1064    
1065     f->fs = this;
1066     f->set_term (r);
1067     f->set_name (strdup (name));
1068    
1069     f->cs = cs;
1070     f->loaded = false;
1071    
1072     return f;
1073     }
1074    
1075     /////////////////////////////////////////////////////////////////////////////
1076    
1077     void
1078     rxvt_fontset::add_fonts (const char *desc)
1079     {
1080     if (desc)
1081     {
1082     char buf[512];
1083     const char *end;
1084    
1085     do
1086     {
1087     while (*desc <= ' ') desc++;
1088    
1089     if (*desc == '[')
1090     {
1091     fprintf (stderr, "extra font parameters not yet supported, skipping.\n");
1092    
1093     //const char *extra = desc++; // not yet used
1094    
1095     desc = strchr (desc, ']');
1096    
1097     if (!desc)
1098     {
1099     fprintf (stderr, "ERROR: opening '[' without closing ']' in font specification.\n");
1100     break;
1101     }
1102    
1103     desc++;
1104     while (*desc <= ' ') desc++;
1105     }
1106    
1107     end = strchr (desc, ',');
1108     if (!end)
1109     end = desc + strlen (desc);
1110    
1111     if (end - desc < 511)
1112     {
1113     strncpy (buf, desc, end - desc);
1114     buf[end - desc] = 0;
1115    
1116     fonts.push_back (new_font (buf, CS_UNICODE));
1117     }
1118    
1119     desc = end + 1;
1120     }
1121     while (*end);
1122     }
1123     }
1124    
1125     bool
1126     rxvt_fontset::realize_font (int i)
1127     {
1128     if (fonts[i]->loaded)
1129     return true;
1130    
1131     fonts[i]->loaded = true;
1132    
1133     if (!fonts[i]->load (base_prop))
1134     {
1135     fonts[i]->cs = CS_UNKNOWN;
1136     return false;
1137     }
1138    
1139     return true;
1140     }
1141    
1142     bool
1143     rxvt_fontset::populate (const char *desc)
1144     {
1145     clear ();
1146    
1147     fontdesc = strdup (desc);
1148    
1149     fonts.push_back (new_font (0, CS_UNICODE));
1150     realize_font (0);
1151    
1152     add_fonts (desc);
1153    
1154     if (!base_id)
1155     base_id = 1;
1156    
1157     // we currently need a base-font, no matter what
1158     if ((int)fonts.size () <= base_id || !realize_font (base_id))
1159     {
1160     puts ("unable to load specified font (s), falling back to 'fixed'\n");
1161     add_fonts ("fixed");
1162     base_id = fonts.size () - 1;
1163     }
1164    
1165     if ((int)fonts.size () <= base_id || !realize_font (base_id))
1166     return false;
1167    
1168     base_prop = fonts[base_id]->properties ();
1169    
1170     return true;
1171     }
1172    
1173     int
1174     rxvt_fontset::find_font (const char *name) const
1175     {
1176     for (rxvt_font *const *f = fonts.begin (); f < fonts.end (); f++)
1177     if ((*f)->name && !strcmp ((*f)->name, name))
1178     return f - fonts.begin ();
1179    
1180     return -1;
1181     }
1182    
1183     int
1184     rxvt_fontset::find_font (unicode_t unicode, bool bold)
1185     {
1186    
1187     for (unsigned int i = !!(0x20 <= unicode && unicode <= 0x7f); // skip pseudo-font for ascii
1188     i < fonts.size ();
1189     i++)
1190     {
1191     rxvt_font *f = fonts[i];
1192    
1193     if (!f->loaded)
1194     {
1195     if (FROM_UNICODE (f->cs, unicode) == NOCHAR)
1196     goto next_font;
1197    
1198     if (!realize_font (i))
1199     goto next_font;
1200     }
1201    
1202     if (f->cs == CS_UNKNOWN)
1203     goto next_font;
1204    
1205     if (bold && f->properties ().weight < rxvt_fontprop::bold)
1206     goto next_font;
1207    
1208     if (f->has_codepoint (unicode))
1209     return i;
1210    
1211     next_font:
1212     if (i == fonts.size () - 1)
1213     {
1214     if (fallback->name)
1215     {
1216     // search through the fallback list
1217     fonts.push_back (new_font (fallback->name, fallback->cs));
1218     fallback++;
1219     }
1220 root 1.4 else if (!bold)
1221 root 1.1 {
1222     // try to find a new font.
1223     // only xft currently supported, as there is no
1224     // way to configure this and xft is easier to hack in,
1225     // while x11 has more framework in place already.
1226     #if XFT
1227     // grab the first xft font that seems suitable
1228     FcPattern *p = FcPatternCreate ();
1229    
1230     FcCharSet *s = FcCharSetCreate ();
1231     FcCharSetAddChar (s, unicode);
1232     FcPatternAddCharSet (p, FC_CHARSET, s);
1233     // charsets don't help that much, as xft might return
1234     // a non-matching font even if a better font is available :/
1235    
1236     FcPatternAddInteger (p, FC_PIXEL_SIZE, base_prop.height);
1237     FcPatternAddInteger (p, FC_WEIGHT, base_prop.weight);
1238     FcPatternAddInteger (p, FC_SLANT, base_prop.slant);
1239     FcPatternAddBool (p, FC_MINSPACE, 1);
1240     //FcPatternAddBool (p, FC_ANTIALIAS, 1);
1241    
1242     XftResult result;
1243     FcPattern *match = XftFontMatch (DISPLAY, DefaultScreen (DISPLAY), p, &result);
1244    
1245     FcPatternDestroy (p);
1246    
1247     if (match)
1248     {
1249     FcPatternDel (match, FC_CHARSET);
1250     char *font = (char *)FcNameUnparse (match);
1251     FcPatternDestroy (match);
1252    
1253     if (find_font (font) < 0)
1254     {
1255     char fontname[4096];
1256     sprintf (fontname, "xft:%-.4090s", font);
1257    
1258     fonts.push_back (new_font (fontname, CS_UNICODE));
1259     }
1260    
1261     free (font);
1262     }
1263     #endif
1264     }
1265     }
1266     }
1267    
1268     // if no bold font found, use a regular one
1269     if (bold)
1270     return find_font (unicode);
1271    
1272     return 0; /* we must return SOME font */
1273     }
1274    
1275    
1276