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