Krolyxon
Commit eb96c71

Branches

Refs →

nsxiveb96c71

Try to match a fallback font if needed

Fixes #276

Instead of rendering the entire filename at once, Xft will let us do it
character by character. This will allow sxiv to query fontconfig for
a font that can provide any missing codepoints, if needed.

A known issue of this patch is that the "..." dots rendering will not
work properly for very long multibyte filenames. That is because we
cannot easily predict the final width of the rendered filename before
drawing it. I couldn't figure out a clean way to deal with this, so I
ended up just truncating the offending filenames.
Author
Squibby
Date
2017-12-06 16:56 -0300
Committer
Squibby · 2017-12-06 20:39 -0300
Parent
f026618
Commit
eb96c7172555f96b07969c6abf74740bb03e36f9

+75 −10

5 files changed

  1. M Makefile +1 −1
  2. M main.c +2 −2
  3. M sxiv.h +2 −1
  4. M util.c +29 −0
  5. M window.c +41 −6
M Makefile View
@@ -28,7 +28,7 @@ LIB_EXIF_0 =
2828 LIB_EXIF_1 = -lexif
2929 LIB_GIF_0 =
3030 LIB_GIF_1 = -lgif
31−LDLIBS = -lImlib2 -lX11 -lXft \
31+LDLIBS = -lImlib2 -lX11 -lXft -lfontconfig \
3232 $(LIB_EXIF_$(HAVE_LIBEXIF)) $(LIB_GIF_$(HAVE_GIFLIB))
3333
3434 OBJS = autoreload_$(AUTORELOAD).o commands.o image.o main.o options.o \
M main.c View
@@ -390,8 +390,8 @@ void update_info(void)
390390 if (ow_info) {
391391 fn = strlen(files[fileidx].name);
392392 if (fn < l->size &&
393− win_textwidth(&win.env, files[fileidx].name, fn, true) +
394− win_textwidth(&win.env, r->buf, r->p - r->buf, true) < win.w)
393+ win_textwidth(&win.env, files[fileidx].name, fn, true, NULL) +
394+ win_textwidth(&win.env, r->buf, r->p - r->buf, true, NULL) < win.w)
395395 {
396396 strncpy(l->buf, files[fileidx].name, l->size);
397397 } else {
M sxiv.h View
@@ -362,6 +362,7 @@ int r_opendir(r_dir_t*, const char*, bool);
362362 int r_closedir(r_dir_t*);
363363 char* r_readdir(r_dir_t*);
364364 int r_mkdir(char*);
365+void* utf8codepoint(const void * __restrict__ str, long * __restrict__ out_codepoint);
365366
366367
367368 /* window.c */
@@ -442,7 +443,7 @@ void win_toggle_bar(win_t*);
442443 void win_clear(win_t*);
443444 void win_draw(win_t*);
444445 void win_draw_rect(win_t*, int, int, int, int, bool, int, unsigned long);
445−int win_textwidth(const win_env_t*, const char*, unsigned int, bool);
446+int win_textwidth(const win_env_t*, const char*, unsigned int, bool, XftFont*);
446447 void win_set_title(win_t*, const char*);
447448 void win_set_cursor(win_t*, cursor_t);
448449 void win_cursor_pos(win_t*, int*, int*);
M util.c View
@@ -204,3 +204,32 @@ int r_mkdir(char *path)
204204 }
205205 return 0;
206206 }
207+
208+/* copied from sheredom's utf8.h (public domain) https://github.com/sheredom/utf8.h */
209+
210+void* utf8codepoint(const void* __restrict__ str, long* __restrict__ out_codepoint)
211+{
212+ const char *s = (const char *)str;
213+
214+ if (0xf0 == (0xf8 & s[0])) {
215+ // 4 byte utf8 codepoint
216+ *out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) |
217+ ((0x3f & s[2]) << 6) | (0x3f & s[3]);
218+ s += 4;
219+ } else if (0xe0 == (0xf0 & s[0])) {
220+ // 3 byte utf8 codepoint
221+ *out_codepoint = ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]);
222+ s += 3;
223+ } else if (0xc0 == (0xe0 & s[0])) {
224+ // 2 byte utf8 codepoint
225+ *out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]);
226+ s += 2;
227+ } else {
228+ // 1 byte utf8 codepoint otherwise
229+ *out_codepoint = s[0];
230+ s += 1;
231+ }
232+
233+ return (void *)s;
234+}
235+
M window.c View
@@ -360,9 +360,40 @@ void win_clear(win_t *win)
360360 XFillRectangle(e->dpy, win->buf.pm, gc, 0, 0, win->buf.w, win->buf.h);
361361 }
362362
363+void win_draw_bar_text(win_t *win, XftDraw *d, XftColor *color, XftFont *font, int x, int y, char *text, int maxlen, int maximum_x)
364+{
365+ size_t len = 0;
366+ int xshift = 0, newshift;
367+ long codep;
368+ char *p, *nextp;
369+ FcCharSet* fccharset;
370+ XftFont* fallback = NULL;
371+
372+ for (p = text; *p && (len < maxlen); p = nextp, len++) {
373+ nextp = utf8codepoint(p, &codep);
374+ if (!XftCharExists(win->env.dpy, font, codep)) {
375+ fccharset = FcCharSetCreate();
376+ FcCharSetAddChar(fccharset, codep);
377+ fallback = XftFontOpen(win->env.dpy, win->env.scr,
378+ FC_CHARSET, FcTypeCharSet, fccharset,
379+ FC_SCALABLE, FcTypeBool, FcTrue,
380+ NULL);
381+ FcCharSetDestroy(fccharset);
382+ }
383+ newshift = win_textwidth(&win->env, p, (int) (nextp-p), false, (fallback ? fallback : font));
384+ if (xshift + newshift <= maximum_x)
385+ XftDrawStringUtf8(d, color, (fallback ? fallback : font), x + xshift, y, (XftChar8*)p, (int) (nextp-p));
386+ xshift += newshift;
387+ if (fallback) {
388+ XftFontClose(win->env.dpy, fallback);
389+ fallback = NULL;
390+ }
391+ }
392+}
393+
363394 void win_draw_bar(win_t *win)
364395 {
365− int len, olen, x, y, w, tw;
396+ int len, olen, x, y, w, tw, maximum_x;
366397 char rest[3];
367398 const char *dots = "...";
368399 win_env_t *e;
@@ -385,7 +416,7 @@ void win_draw_bar(win_t *win)
385416 XSetBackground(e->dpy, gc, win->bar.bgcol.pixel);
386417
387418 if ((len = strlen(r->buf)) > 0) {
388− if ((tw = win_textwidth(e, r->buf, len, true)) > w)
419+ if ((tw = win_textwidth(e, r->buf, len, true, font)) > w)
389420 return;
390421 x = win->w - tw + H_TEXT_PAD;
391422 w -= tw;
@@ -393,9 +424,10 @@ void win_draw_bar(win_t *win)
393424 }
394425 if ((len = strlen(l->buf)) > 0) {
395426 olen = len;
396− while (len > 0 && (tw = win_textwidth(e, l->buf, len, true)) > w)
427+ while (len > 0 && (tw = win_textwidth(e, l->buf, len, true, font)) > w)
397428 len--;
398429 if (len > 0) {
430+ maximum_x = w;
399431 if (len != olen) {
400432 w = strlen(dots);
401433 if (len <= w)
@@ -404,7 +436,7 @@ void win_draw_bar(win_t *win)
404436 memcpy(l->buf + len - w, dots, w);
405437 }
406438 x = H_TEXT_PAD;
407− XftDrawStringUtf8(d, &win->bar.fgcol, font, x, y, (XftChar8*)l->buf, len);
439+ win_draw_bar_text(win, d, &win->bar.fgcol, font, x, y, l->buf, len, maximum_x);
408440 if (len != olen)
409441 memcpy(l->buf + len - w, rest, w);
410442 }
@@ -437,11 +469,14 @@ void win_draw_rect(win_t *win, int x, int y, int w, int h, bool fill, int lw,
437469 XDrawRectangle(win->env.dpy, win->buf.pm, gc, x, y, w, h);
438470 }
439471
440−int win_textwidth(const win_env_t *e, const char *text, unsigned int len, bool with_padding)
472+int win_textwidth(const win_env_t *e, const char *text, unsigned int len, bool with_padding, XftFont *fnt)
441473 {
442474 XGlyphInfo ext;
443475
444− XftTextExtentsUtf8(e->dpy, font, (XftChar8*)text, len, &ext);
476+ if(!fnt)
477+ fnt = font;
478+
479+ XftTextExtentsUtf8(e->dpy, fnt, (XftChar8*)text, len, &ext);
445480 return ext.xOff + (with_padding ? 2 * H_TEXT_PAD : 0);
446481 }
447482