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.
This commit is contained in:
parent
f02661879f
commit
eb96c71725
2
Makefile
2
Makefile
|
@ -28,7 +28,7 @@ LIB_EXIF_0 =
|
||||||
LIB_EXIF_1 = -lexif
|
LIB_EXIF_1 = -lexif
|
||||||
LIB_GIF_0 =
|
LIB_GIF_0 =
|
||||||
LIB_GIF_1 = -lgif
|
LIB_GIF_1 = -lgif
|
||||||
LDLIBS = -lImlib2 -lX11 -lXft \
|
LDLIBS = -lImlib2 -lX11 -lXft -lfontconfig \
|
||||||
$(LIB_EXIF_$(HAVE_LIBEXIF)) $(LIB_GIF_$(HAVE_GIFLIB))
|
$(LIB_EXIF_$(HAVE_LIBEXIF)) $(LIB_GIF_$(HAVE_GIFLIB))
|
||||||
|
|
||||||
OBJS = autoreload_$(AUTORELOAD).o commands.o image.o main.o options.o \
|
OBJS = autoreload_$(AUTORELOAD).o commands.o image.o main.o options.o \
|
||||||
|
|
4
main.c
4
main.c
|
@ -390,8 +390,8 @@ void update_info(void)
|
||||||
if (ow_info) {
|
if (ow_info) {
|
||||||
fn = strlen(files[fileidx].name);
|
fn = strlen(files[fileidx].name);
|
||||||
if (fn < l->size &&
|
if (fn < l->size &&
|
||||||
win_textwidth(&win.env, files[fileidx].name, fn, true) +
|
win_textwidth(&win.env, files[fileidx].name, fn, true, NULL) +
|
||||||
win_textwidth(&win.env, r->buf, r->p - r->buf, true) < win.w)
|
win_textwidth(&win.env, r->buf, r->p - r->buf, true, NULL) < win.w)
|
||||||
{
|
{
|
||||||
strncpy(l->buf, files[fileidx].name, l->size);
|
strncpy(l->buf, files[fileidx].name, l->size);
|
||||||
} else {
|
} else {
|
||||||
|
|
3
sxiv.h
3
sxiv.h
|
@ -362,6 +362,7 @@ int r_opendir(r_dir_t*, const char*, bool);
|
||||||
int r_closedir(r_dir_t*);
|
int r_closedir(r_dir_t*);
|
||||||
char* r_readdir(r_dir_t*);
|
char* r_readdir(r_dir_t*);
|
||||||
int r_mkdir(char*);
|
int r_mkdir(char*);
|
||||||
|
void* utf8codepoint(const void * __restrict__ str, long * __restrict__ out_codepoint);
|
||||||
|
|
||||||
|
|
||||||
/* window.c */
|
/* window.c */
|
||||||
|
@ -442,7 +443,7 @@ void win_toggle_bar(win_t*);
|
||||||
void win_clear(win_t*);
|
void win_clear(win_t*);
|
||||||
void win_draw(win_t*);
|
void win_draw(win_t*);
|
||||||
void win_draw_rect(win_t*, int, int, int, int, bool, int, unsigned long);
|
void win_draw_rect(win_t*, int, int, int, int, bool, int, unsigned long);
|
||||||
int win_textwidth(const win_env_t*, const char*, unsigned int, bool);
|
int win_textwidth(const win_env_t*, const char*, unsigned int, bool, XftFont*);
|
||||||
void win_set_title(win_t*, const char*);
|
void win_set_title(win_t*, const char*);
|
||||||
void win_set_cursor(win_t*, cursor_t);
|
void win_set_cursor(win_t*, cursor_t);
|
||||||
void win_cursor_pos(win_t*, int*, int*);
|
void win_cursor_pos(win_t*, int*, int*);
|
||||||
|
|
29
util.c
29
util.c
|
@ -204,3 +204,32 @@ int r_mkdir(char *path)
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* copied from sheredom's utf8.h (public domain) https://github.com/sheredom/utf8.h */
|
||||||
|
|
||||||
|
void* utf8codepoint(const void* __restrict__ str, long* __restrict__ out_codepoint)
|
||||||
|
{
|
||||||
|
const char *s = (const char *)str;
|
||||||
|
|
||||||
|
if (0xf0 == (0xf8 & s[0])) {
|
||||||
|
// 4 byte utf8 codepoint
|
||||||
|
*out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) |
|
||||||
|
((0x3f & s[2]) << 6) | (0x3f & s[3]);
|
||||||
|
s += 4;
|
||||||
|
} else if (0xe0 == (0xf0 & s[0])) {
|
||||||
|
// 3 byte utf8 codepoint
|
||||||
|
*out_codepoint = ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]);
|
||||||
|
s += 3;
|
||||||
|
} else if (0xc0 == (0xe0 & s[0])) {
|
||||||
|
// 2 byte utf8 codepoint
|
||||||
|
*out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]);
|
||||||
|
s += 2;
|
||||||
|
} else {
|
||||||
|
// 1 byte utf8 codepoint otherwise
|
||||||
|
*out_codepoint = s[0];
|
||||||
|
s += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void *)s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
47
window.c
47
window.c
|
@ -360,9 +360,40 @@ void win_clear(win_t *win)
|
||||||
XFillRectangle(e->dpy, win->buf.pm, gc, 0, 0, win->buf.w, win->buf.h);
|
XFillRectangle(e->dpy, win->buf.pm, gc, 0, 0, win->buf.w, win->buf.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
int xshift = 0, newshift;
|
||||||
|
long codep;
|
||||||
|
char *p, *nextp;
|
||||||
|
FcCharSet* fccharset;
|
||||||
|
XftFont* fallback = NULL;
|
||||||
|
|
||||||
|
for (p = text; *p && (len < maxlen); p = nextp, len++) {
|
||||||
|
nextp = utf8codepoint(p, &codep);
|
||||||
|
if (!XftCharExists(win->env.dpy, font, codep)) {
|
||||||
|
fccharset = FcCharSetCreate();
|
||||||
|
FcCharSetAddChar(fccharset, codep);
|
||||||
|
fallback = XftFontOpen(win->env.dpy, win->env.scr,
|
||||||
|
FC_CHARSET, FcTypeCharSet, fccharset,
|
||||||
|
FC_SCALABLE, FcTypeBool, FcTrue,
|
||||||
|
NULL);
|
||||||
|
FcCharSetDestroy(fccharset);
|
||||||
|
}
|
||||||
|
newshift = win_textwidth(&win->env, p, (int) (nextp-p), false, (fallback ? fallback : font));
|
||||||
|
if (xshift + newshift <= maximum_x)
|
||||||
|
XftDrawStringUtf8(d, color, (fallback ? fallback : font), x + xshift, y, (XftChar8*)p, (int) (nextp-p));
|
||||||
|
xshift += newshift;
|
||||||
|
if (fallback) {
|
||||||
|
XftFontClose(win->env.dpy, fallback);
|
||||||
|
fallback = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void win_draw_bar(win_t *win)
|
void win_draw_bar(win_t *win)
|
||||||
{
|
{
|
||||||
int len, olen, x, y, w, tw;
|
int len, olen, x, y, w, tw, maximum_x;
|
||||||
char rest[3];
|
char rest[3];
|
||||||
const char *dots = "...";
|
const char *dots = "...";
|
||||||
win_env_t *e;
|
win_env_t *e;
|
||||||
|
@ -385,7 +416,7 @@ void win_draw_bar(win_t *win)
|
||||||
XSetBackground(e->dpy, gc, win->bar.bgcol.pixel);
|
XSetBackground(e->dpy, gc, win->bar.bgcol.pixel);
|
||||||
|
|
||||||
if ((len = strlen(r->buf)) > 0) {
|
if ((len = strlen(r->buf)) > 0) {
|
||||||
if ((tw = win_textwidth(e, r->buf, len, true)) > w)
|
if ((tw = win_textwidth(e, r->buf, len, true, font)) > w)
|
||||||
return;
|
return;
|
||||||
x = win->w - tw + H_TEXT_PAD;
|
x = win->w - tw + H_TEXT_PAD;
|
||||||
w -= tw;
|
w -= tw;
|
||||||
|
@ -393,9 +424,10 @@ void win_draw_bar(win_t *win)
|
||||||
}
|
}
|
||||||
if ((len = strlen(l->buf)) > 0) {
|
if ((len = strlen(l->buf)) > 0) {
|
||||||
olen = len;
|
olen = len;
|
||||||
while (len > 0 && (tw = win_textwidth(e, l->buf, len, true)) > w)
|
while (len > 0 && (tw = win_textwidth(e, l->buf, len, true, font)) > w)
|
||||||
len--;
|
len--;
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
|
maximum_x = w;
|
||||||
if (len != olen) {
|
if (len != olen) {
|
||||||
w = strlen(dots);
|
w = strlen(dots);
|
||||||
if (len <= w)
|
if (len <= w)
|
||||||
|
@ -404,7 +436,7 @@ void win_draw_bar(win_t *win)
|
||||||
memcpy(l->buf + len - w, dots, w);
|
memcpy(l->buf + len - w, dots, w);
|
||||||
}
|
}
|
||||||
x = H_TEXT_PAD;
|
x = H_TEXT_PAD;
|
||||||
XftDrawStringUtf8(d, &win->bar.fgcol, font, x, y, (XftChar8*)l->buf, len);
|
win_draw_bar_text(win, d, &win->bar.fgcol, font, x, y, l->buf, len, maximum_x);
|
||||||
if (len != olen)
|
if (len != olen)
|
||||||
memcpy(l->buf + len - w, rest, w);
|
memcpy(l->buf + len - w, rest, w);
|
||||||
}
|
}
|
||||||
|
@ -437,11 +469,14 @@ void win_draw_rect(win_t *win, int x, int y, int w, int h, bool fill, int lw,
|
||||||
XDrawRectangle(win->env.dpy, win->buf.pm, gc, x, y, w, h);
|
XDrawRectangle(win->env.dpy, win->buf.pm, gc, x, y, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
int win_textwidth(const win_env_t *e, const char *text, unsigned int len, bool with_padding)
|
int win_textwidth(const win_env_t *e, const char *text, unsigned int len, bool with_padding, XftFont *fnt)
|
||||||
{
|
{
|
||||||
XGlyphInfo ext;
|
XGlyphInfo ext;
|
||||||
|
|
||||||
XftTextExtentsUtf8(e->dpy, font, (XftChar8*)text, len, &ext);
|
if(!fnt)
|
||||||
|
fnt = font;
|
||||||
|
|
||||||
|
XftTextExtentsUtf8(e->dpy, fnt, (XftChar8*)text, len, &ext);
|
||||||
return ext.xOff + (with_padding ? 2 * H_TEXT_PAD : 0);
|
return ext.xOff + (with_padding ? 2 * H_TEXT_PAD : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue