使用Freetype、Xft与Xlib时字体大小无法修改的问题求助
解决Xft字体大小无法修改的问题
问题根源
- 直接修改
font->height无效:XftFont结构体的成员是只读属性,字体加载完成后手动修改不会改变已加载的字体渲染数据。 - 使用了固定尺寸位图字体:
fixed是X11自带的位图字体,仅支持有限的固定尺寸,无法自由缩放,必须改用可缩放的矢量字体(如TrueType/OpenType格式)。 - 打开字体时未指定大小:
XftFontOpenName需要传入包含尺寸信息的字体名称字符串,才能加载对应大小的字体实例。
解决方案
- 使用
"字体名称-尺寸"的格式指定带大小的字体,或动态拼接字体名与目标尺寸。 - 移除手动修改
font->height的代码,改为在字体加载阶段指定大小。 - 修复主循环逻辑:添加X事件处理避免窗口假死,每次绘制前清除窗口内容防止文本重叠。
修改后的完整代码
#include <X11/Xlib.h> #include <X11/Xatom.h> #include <X11/extensions/Xcomposite.h> #include <X11/Xft/Xft.h> #include <stdio.h> #include <string.h> #include <unistd.h> // 使用系统自带的可缩放矢量字体(可根据实际情况替换) #define BASE_FONT_NAME "DejaVu Sans" void drawText(Display *display, Window window, const char *text, int x, int y, int fontSize) { XftFont *font; XftDraw *xftDraw; XftColor color; // 动态拼接带尺寸的字体名称:"字体名-尺寸" char fontName[256]; snprintf(fontName, sizeof(fontName), "%s-%d", BASE_FONT_NAME, fontSize); // 加载指定大小的字体 font = XftFontOpenName(display, DefaultScreen(display), fontName); if (!font) { fprintf(stderr, "无法加载字体:%s\n", fontName); return; } // 创建Xft绘图上下文 Visual *visual = DefaultVisual(display, DefaultScreen(display)); Colormap colormap = DefaultColormap(display, DefaultScreen(display)); xftDraw = XftDrawCreate(display, window, visual, colormap); // 设置白色文本颜色 XRenderColor xrenderColor = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}; XftColorAllocValue(display, visual, colormap, &xrenderColor, &color); // 绘制文本:y坐标需加上字体ascent值,保证文本基线对齐到指定位置 XftDrawStringUtf8(xftDraw, &color, font, x, y + font->ascent, (XftChar8 *)text, strlen(text)); // 释放资源 XftDrawDestroy(xftDraw); XftColorFree(display, visual, colormap, &color); XftFontClose(display, font); } int main() { Display *display = XOpenDisplay(NULL); if (!display) { fprintf(stderr, "无法打开显示\n"); return 1; } int screen = DefaultScreen(display); int screenWidth = DisplayWidth(display, screen); int screenHeight = DisplayHeight(display, screen); int windowWidth = 400; int windowHeight = 300; int windowX = (screenWidth - windowWidth) / 2; int windowY = (screenHeight - windowHeight) / 2; // 创建窗口 Window overlayWindow = XCreateSimpleWindow(display, DefaultRootWindow(display), windowX, windowY, windowWidth, windowHeight, 0, 0, BlackPixel(display, screen)); // 设置全屏属性(可选) Atom atom_net_wm_state = XInternAtom(display, "_NET_WM_STATE", False); Atom atom_fullscreen = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); XChangeProperty(display, overlayWindow, atom_net_wm_state, XA_ATOM, 32, PropModeReplace, (unsigned char *)&atom_fullscreen, 1); // 设置窗口覆盖重定向 XSetWindowAttributes attr; attr.override_redirect = True; XChangeWindowAttributes(display, overlayWindow, CWOverrideRedirect, &attr); XCompositeRedirectWindow(display, overlayWindow, CompositeRedirectAutomatic); XMapWindow(display, overlayWindow); XFlush(display); // 主循环:处理事件+绘制 XEvent ev; while (1) { // 处理X事件,避免窗口假死 while (XPending(display)) { XNextEvent(display, &ev); if (ev.type == DestroyNotify) { XDestroyWindow(display, overlayWindow); XCloseDisplay(display); return 0; } } // 清除窗口内容(黑色填充) XSetForeground(display, DefaultGC(display, screen), BlackPixel(display, screen)); XFillRectangle(display, overlayWindow, DefaultGC(display, screen), 0, 0, windowWidth, windowHeight); // 绘制不同大小的文本测试 drawText(display, overlayWindow, "字体大小24", 50, 50, 24); drawText(display, overlayWindow, "字体大小48", 50, 120, 48); drawText(display, overlayWindow, "字体大小64", 50, 200, 64); XFlush(display); // 短暂休眠降低CPU占用 usleep(16000); // 约60fps } XDestroyWindow(display, overlayWindow); XCloseDisplay(display); return 0; }
编译与测试
使用以下命令编译代码:
gcc -o xft-text xft-text.c -lX11 -lXft -lXcomposite
补充说明
- 若系统没有
DejaVu Sans字体,可替换为其他已安装的矢量字体,比如"Noto Sans"、"Roboto"等,可用fc-list命令查看系统可用字体列表。 - 动态拼接字体名称是指定大小最简便的方式,也可使用
XftFontOpenXlfd通过XLFD格式字符串指定更精细的字体属性(如字重、样式)。
内容的提问来源于stack exchange,提问作者Pedro A. Pinheiro
相关产品推荐
相关产品推荐

