You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 23:37:06