How to use fallback fonts to draw text with xlib/libxft ? For example, when drawing a string, the current font does not contain certain characters, but another font does. So, how to use fallback fonts to draw characters?
This way work, but low efficiency:
#include <stdio.h>
#include <stdint.h>
#include <locale.h>
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
#define ARRAY_NUM(a) (sizeof(a)/sizeof(a[0]))
#define FONT_NAMES (const char *[]){"DejaVu Sans", "Noto Sans Devanagari", "Noto Color Emoji"}
#define FONT_N ARRAY_NUM(FONT_NAMES)
Display *display=NULL;
Window root;
int screen;
Visual *visual=NULL;
Colormap colormap;
XftFont *fonts[FONT_N];
XftColor color;
int get_utf8_codepoint(const char *str, uint32_t *codepoint)
{
const uint8_t *p=(const uint8_t*)str;
int len=0;
if(*p < 0x80) // 單字節字符
len=1, *codepoint=*p;
else if((*p>>5) == 0x06) // 雙字節字符
len=2, *codepoint=(*p & 0x1F)<<6 | (*(p+1) & 0x3F);
else if((*p>>4) == 0x0E) // 三字節字符
len=3, *codepoint=(*p & 0x0F)<<12 | (*(p+1) & 0x3F)<<6 | (*(p+2) & 0x3F);
else if((*p>>3) == 0x1E) // 四字節字符
len=4, *codepoint=(*p & 0x07)<<18 | (*(p+1) & 0x3F)<<12 | (*(p+2) & 0x3F)<<6 | (*(p+3) & 0x3F);
else // 非utf8編碼字符
len=0;
return len;
}
void load_fonts(void)
{
for(int i=0; i<FONT_N; i++)
fonts[i]=XftFontOpenName(display, screen, FONT_NAMES[i]);
}
void close_fonts(void)
{
for(int i=0; i<FONT_N; i++)
XftFontClose(display, fonts[i]);
}
void draw_utf8_string(Drawable drawable, const char *s, int x, int y)
{
XGlyphInfo glyphInfo;
int len=0;
int32_t codepoint;
XftDraw *xftDraw=XftDrawCreate(display, drawable, visual, colormap);
while(*s)
{
len=get_utf8_codepoint(s, &codepoint);
for(int i=0; i<FONT_N; i++)
if (XftCharExists(display, fonts[i], codepoint)) {
XftDrawStringUtf8(xftDraw, &color, fonts[i], x, y, (XftChar8*)s, len);
XftTextExtentsUtf8(display, fonts[i], (XftChar8*)s, len, &glyphInfo);
x += glyphInfo.xOff;
break;
}
s+=len;
}
XftDrawDestroy(xftDraw);
}
int main(void)
{
const char *s="Hello🔋,你好🥲,नमस्ते🤡.";
display=XOpenDisplay(NULL);
root=DefaultRootWindow(display);
screen=DefaultScreen(display);
visual=DefaultVisual(display, screen);
colormap=DefaultColormap(display, screen);
if(!setlocale(LC_ALL, "") || !XSupportsLocale())
fprintf(stderr, "warning: no locale support\n");
XftColorAllocName(display, visual, colormap, "yellow", &color);
load_fonts();
draw_utf8_string(root, s, 100, 100);
close_fonts();
XCloseDisplay(display);
return 0;
}