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

使用Pillow渲染字体字形时出现空白方框的问题求助

问题根因

渲染出现空白方框是字体内置的.notdef缺字占位符,核心原因是你加载的字体本身不包含目标Unicode字符的字形定义,和布局引擎、编码参数无关:

  • 现有代码仅校验字体文件是否可正常加载,未校验字体是否支持目标字符,只要字体能成功加载就直接执行渲染,必然会出现缺字空框
  • Pillow 缺字时不会抛出异常,会自动渲染缺字占位方框,原有try-except逻辑完全无法捕获这类情况。
解决方案

无需手动遍历25万张渲染图剔除无效结果,在渲染前增加字形有效性校验即可,Pillow 8.0.0及以上版本提供的getbbox()、getmask()接口可以在渲染前快速判断字符是否被字体支持,校验耗时远低于渲染后做图像识别排查。

  • 缺字时getbbox()会返回.notdef占位符的边界,getmask()返回的像素数据只有方框轮廓的少量非白像素,正常字形的有效绘制像素占比远高于空框
  • 校验逻辑嵌入渲染流程后,批量渲染时可以自动跳过不支持目标字符的字体,全程无需人工介入。

修正后可自动过滤空框的实现代码

from PIL import Image, ImageFont, ImageDraw

def draw_glyph(font_file, unicode_char, glyph_size=128):
    try:
        font = ImageFont.truetype(
            font=font_file,
            encoding="",
            size=glyph_size,
            layout_engine=ImageFont.LAYOUT_RAQM,
        )
    except Exception:
        return None

    # 渲染前校验字符是否存在于当前字体中
    bbox = font.getbbox(unicode_char, anchor='mm')
    char_mask = font.getmask(unicode_char, anchor='mm')
    # 统计mask中非0(实际绘制内容)的像素占比,缺字空框的非0像素占比极低
    non_blank_pixel = sum(1 for p in char_mask if p > 0)
    # 阈值可根据使用的字体灵活调整
    if non_blank_pixel < glyph_size * glyph_size * 0.005:
        return None

    img = Image.new('L', (glyph_size, glyph_size), 'white')
    draw = ImageDraw.Draw(img)
    draw.text(
        xy=(glyph_size//2, glyph_size//2),
        text=unicode_char,
        font=font,
        anchor='mm',
    )
    return img
补充说明
  • 若使用的Pillow版本低于8.0.0,先升级到新版本即可使用上述字形查询接口,低版本无对应查询API,只能渲染后统计图像非白像素占比判断是否为空框,运行效率更低
  • 代码里的非空像素占比阈值0.005可根据实际使用的字体调整:如果出现极细笔画字符被误判为缺字,可适当调低阈值;如果仍有漏判的空框,可适当调高阈值
  • 字体加载时保持encoding=""默认参数即可,自定义编码值反而可能导致Unicode字形映射错误。

内容的提问来源于stack exchange,提问作者tachycardiazxc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 12:12:22