如何用C语言通过Fontconfig/FreeType API获取字体支持的码点范围
用C语言实现
fc-match --format='%{charset}\n'功能,获取字体支持的码点 我想要实现类似以下命令的功能:
fc-match --format='%{charset}\n' <insert font family or path>
但要用C语言实现。我正在用SDL编写一个文本编辑器,需要自行处理字体回退逻辑。我的思路是先获取默认字体:
char* get_default_font_path(char* family) { FcInit(); FcPattern *pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, family, NULL); FcConfigSubstitute(NULL, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); FcResult result; FcPattern *match = FcFontMatch(NULL, pattern, &result); FcChar8 *font_path = NULL; FcPatternGetString(match, FC_FILE, 0, &font_path); font_path = strdup((const char*)font_path); FcPatternDestroy(match); FcResult charset = FcPatternGetCharSet(match, FC_CHARSET); return font_path; }
获取默认字体支持的所有码点(或码点范围),然后针对用户输入的每个字符,检查其码点是否在该列表中,如果不在,则找到支持该字符的回退字体。
char* find_fallback_font(const char *input) { FcInit(); FcPattern *pattern = FcPatternCreate(); FcCharSet *charset = FcCharSetCreate(); const char *p = input; while (*p) { FcChar32 ucs4; unsigned char c = (unsigned char)*p; if ((c & 0x80) == 0) { ucs4 = c; p++; } else if ((c & 0xE0) == 0xC0) { ucs4 = ((c & 0x1F) << 6) | (p[1] & 0x3F); p += 2; } else if ((c & 0xF0) == 0xE0) { ucs4 = ((c & 0x0F) << 12) | ((p[1] & 0x3F) << 6) | (p[2] & 0x3F); p += 3; } else if ((c & 0xF8) == 0xF0) { ucs4 = ((c & 0x07) << 18) | ((p[1] & 0x3F) << 12) | ((p[2] & 0x3F) << 6) | (p[3] & 0x3F); p += 4; } else { break; } FcCharSetAddChar(charset, ucs4); } FcPatternAddCharSet(pattern, FC_CHARSET, charset); FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); FcConfigSubstitute(NULL, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); FcResult result; FcPattern *match = FcFontMatch(NULL, pattern, &result); char *font_path = NULL; if (match) { FcChar8 *file = NULL; if (FcPatternGetString(match, FC_FILE, 0, &file) == FcResultMatch) { font_path = strdup((const char*)file); } FcPatternDestroy(match); } FcCharSetDestroy(charset); FcPatternDestroy(pattern); FcFini(); return font_path; }
我打算用哈希表记录每个码点对应的字体,但目前不知道如何用C/C++获取字体支持的码点。
获取字体支持码点的实现方案
1. 修正字体信息获取逻辑
你原有的get_default_font_path函数存在内存访问错误:销毁match后再调用FcPatternGetCharSet,会导致非法访问。以下是修正后的函数,可同时获取字体路径和字符集:
void get_default_font_info(char* family, char** font_path, FcCharSet** out_charset) { FcInit(); FcPattern *pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, family, NULL); FcConfigSubstitute(NULL, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); FcResult result; FcPattern *match = FcFontMatch(NULL, pattern, &result); if (!match) { *font_path = NULL; *out_charset = NULL; FcPatternDestroy(pattern); return; } // 获取字体路径 FcChar8 *file = NULL; if (FcPatternGetString(match, FC_FILE, 0, &file) == FcResultMatch) { *font_path = strdup((const char*)file); } else { *font_path = NULL; } // 获取并复制字符集(避免依赖已销毁的pattern) FcCharSet *charset = NULL; if (FcPatternGetCharSet(match, FC_CHARSET, 0, &charset) == FcResultMatch) { *out_charset = FcCharSetCopy(charset); } else { *out_charset = NULL; } FcPatternDestroy(match); FcPatternDestroy(pattern); }
2. 遍历字符集获取所有码点
使用Fontconfig的FcCharSetFirstPage和FcCharSetNextPage遍历字符集的码点范围,提取所有支持的码点:
void iterate_font_charset(FcCharSet* charset, FcChar32** codepoints, size_t* count) { if (!charset) { *codepoints = NULL; *count = 0; return; } FcChar32 start, end; FcCharSetPage page; size_t total = 0; // 先统计总码点数量 if (FcCharSetFirstPage(charset, &start, &end, &page)) { do { total += end - start + 1; } while (FcCharSetNextPage(&page, &start, &end)); } // 分配内存存储码点 *codepoints = malloc(total * sizeof(FcChar32)); if (!*codepoints) { *count = 0; return; } // 填充码点数组 size_t idx = 0; if (FcCharSetFirstPage(charset, &start, &end, &page)) { do { for (FcChar32 c = start; c <= end; c++) { (*codepoints)[idx++] = c; } } while (FcCharSetNextPage(&page, &start, &end)); } *count = total; }
3. 单个码点支持性检查
如果只需验证某个码点是否被字体支持,直接使用FcCharSetHasChar即可:
bool is_codepoint_supported(FcCharSet* charset, FcChar32 codepoint) { return charset ? FcCharSetHasChar(charset, codepoint) : false; }
4. 内存管理注意事项
- 不再使用的
FcCharSet需调用FcCharSetDestroy销毁 - 动态分配的码点数组需手动调用
free释放 - 建议在程序初始化时调用一次
FcInit,退出时调用FcFini,避免重复初始化/销毁
内容的提问来源于stack exchange,提问作者Cgates Cgates
相关产品推荐
相关产品推荐

