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

如何正确实现获取字体支持的Unicode范围及例外字符的逻辑?

多语言文本字体渲染的码点范围合并实现

针对你提出的字体码点范围合并需求,以下是符合规则的具体实现方案:

前置准备

首先需要明确几个核心数据:

  • 无效码点集合:包含Unicode控制字符、保留字符等不可打印字符,需提前定义好。
  • 原始码点范围:通过fontTools提取字体支持的连续码点区间,确保按起始码点升序排列。

核心合并规则实现

规则1:间隔全为无效码点则合并

遍历排序后的原始范围,检查当前范围与下一个范围的间隔区间[当前范围结束值+1, 下一个范围起始值-1]内所有码点是否都属于无效集合。若是,直接合并两个范围。

规则2:间隔占比≤5%则合并并记录例外

若规则1不满足,计算下一个范围起始值 / 当前范围结束值的比值,若≤1.05,则合并范围,同时将间隔内的有效码点(不在无效集合中的码点)加入例外列表。

完整代码实现

from fontTools.ttLib import TTFont

# 提取字体支持的连续码点范围
def get_font_codepoint_ranges(font_path):
    font = TTFont(font_path)
    cmap = font.getBestCmap()
    codepoints = sorted(cmap.keys())
    if not codepoints:
        return []
    
    ranges = []
    start = codepoints[0]
    prev_cp = start
    for cp in codepoints[1:]:
        if cp == prev_cp + 1:
            prev_cp = cp
        else:
            ranges.append((start, prev_cp))
            start = cp
            prev_cp = cp
    ranges.append((start, prev_cp))
    font.close()
    return ranges

# 构建无效码点集合(可根据需求扩展)
def get_invalid_codepoints():
    invalid = set()
    # 基础控制字符
    invalid.update(range(0x00, 0x20))
    invalid.add(0x7F)
    # 可补充其他无效区块,如Unicode保留区、专用区等
    return invalid

# 执行范围合并逻辑
def merge_font_ranges(original_ranges, invalid_cps):
    if not original_ranges:
        return [], []
    
    merged_ranges = []
    exception_codepoints = []
    curr_start, curr_end = original_ranges[0]

    for next_start, next_end in original_ranges[1:]:
        gap_start = curr_end + 1
        gap_end = next_start - 1

        # 检查间隔内是否全为无效码点
        gap_valid = []
        if gap_start <= gap_end:
            for cp in range(gap_start, gap_end + 1):
                if cp not in invalid_cps:
                    gap_valid.append(cp)
        
        if not gap_valid:
            # 规则1:间隔全无效,合并范围
            curr_end = next_end
        else:
            # 检查规则2:间隔占比是否≤5%
            if next_start / curr_end <= 1.05:
                # 合并范围,记录有效例外
                exception_codepoints.extend(gap_valid)
                curr_end = next_end
            else:
                # 无法合并,保存当前范围
                merged_ranges.append((curr_start, curr_end))
                curr_start, curr_end = next_start, next_end
    
    # 加入最后一个合并后的范围
    merged_ranges.append((curr_start, curr_end))
    # 例外码点去重排序
    exception_codepoints = sorted(list(set(exception_codepoints)))
    return merged_ranges, exception_codepoints

# 测试Andika字体前10个范围合并
if __name__ == "__main__":
    andika_font_path = "Andika-Regular.ttf"
    invalid_cps = get_invalid_codepoints()
    original_ranges = get_font_codepoint_ranges(andika_font_path)
    
    test_ranges = original_ranges[:10]
    merged, exceptions = merge_font_ranges(test_ranges, invalid_cps)
    print("合并后范围:", merged)
    print("例外码点:", exceptions)

关键注意事项

  1. 原始范围排序:必须确保提取的原始码点范围是升序排列的,否则合并逻辑会出错,上述代码中已通过sorted(cmap.keys())实现。
  2. 无效码点扩展:根据实际需求补充无效码点集合,比如Unicode的私人使用区、未分配码点等,确保合并逻辑的准确性。
  3. 例外判断逻辑:后续判断字符时,若码点在合并范围内,需额外检查是否在例外列表中,若是则需fallback到备用字体。
  4. 性能优化:例外列表可转为集合,提升查询效率;合并后的范围可用于二分查找,快速判断码点是否在范围内。

内容的提问来源于stack exchange,提问作者Ξένη Γήινος

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 09:27:06