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

使用Python向字体添加Glyph时遇AttributeError问题求助

问题:FontTools添加Glyph时触发AttributeError: 'table__g_l_y_f' object has no attribute 'glyphs'

我用Python自定义Glyph创建新字体,已经实现了将SVG解析为FontTools的TTGlyph对象的函数,该函数返回TTGlyph对象、Glyph名称及码点。但调用create_font_from_glyphs脚本时出现以下错误,无法成功向字体添加Glyph:

原代码

import os
from fontTools.ttLib import TTFont, newTable
from fontTools.ttLib.tables._c_m_a_p import CmapSubtable
from fontTools.ttLib.tables._n_a_m_e import NameRecord
from fontTools.ttLib.tables._g_l_y_f import TTGlyph

def parse_svg_to_glyph(svg_file_path):
    # 已实现的SVG解析逻辑,返回TTGlyph、glyph名称、码点列表
    glyph = TTGlyph()
    glyph_name = os.path.splitext(os.path.basename(svg_file_path))[0]
    codepoints = [ord(glyph_name[0])] if glyph_name else []
    return glyph, glyph_name, codepoints

def create_font_from_glyphs(glyph_data, output_path):
    font = TTFont()

    font['head'] = newTable('head')
    font['head'].unitsPerEm = 1000

    font['hhea'] = newTable('hhea')
    font['hhea'].ascent = 900
    font['hhea'].descent = -100

    font['maxp'] = newTable('maxp')
    font['maxp'].numGlyphs = len(glyph_data) + 1  
    font['glyf'] = newTable('glyf')

    for glyph, glyph_name, _ in glyph_data:
        font['glyf'][glyph_name] = glyph

    cmap = newTable('cmap')
    cmap.tableVersion = 0
    cmap.tables = []
    subtable = CmapSubtable.newSubtable(4)
    subtable.platformID = 3
    subtable.platEncID = 1
    subtable.language = 0
    subtable.cmap = {unicode: glyph_name for _, glyph_name, unicodes in glyph_data for unicode in unicodes}
    cmap.tables.append(subtable)
    font['cmap'] = cmap

    font_name = "Example Font"
    name = newTable('name')
    name.names = []  
    nameRecord = NameRecord()
    nameRecord.nameID = 1
    nameRecord.platformID = 3
    nameRecord.platEncID = 1
    nameRecord.langID = 0x409
    nameRecord.string = font_name.encode('utf-16-be')
    name.names.append(nameRecord)
    font['name'] = name

    font['post'] = newTable('post')
    font['post'].italicAngle = 0
    font['post'].underlinePosition = -100
    font['post'].underlineThickness = 50
    font['post'].isFixedPitch = 0
    font.save(output_path)

def main():
    svg_dir_path = "data/svg" 

    glyph_data = []
    for filename in os.listdir(svg_dir_path):
        if filename.endswith('.svg'):
            svg_file_path = os.path.join(svg_dir_path, filename)
            glyph, glyph_name, codepoints = parse_svg_to_glyph(svg_file_path)
            glyph_data.append((glyph, glyph_name, codepoints))

    create_font_from_glyphs(glyph_data, "test.ttf")

if __name__ == "__main__":
    main()

错误信息

font['glyf'][glyph_name] = glyph
    ~~~~~~~~~~~~^^^^^^^^^^^^
 self.glyphs[glyphName] = glyph
    ^^^^^^^^^^^
 AttributeError: 'table__g_l_y_f' object has no attribute 'glyphs'

解决方案

1. 初始化glyf表的glyphs字典

通过newTable('glyf')创建的glyf表对象默认没有glyphs属性,而FontTools在通过下标访问font['glyf'][glyph_name]时会尝试操作这个字典。需要在创建glyf表后手动初始化:

font['glyf'] = newTable('glyf')
font['glyf'].glyphs = {}  # 初始化glyphs字典

2. 添加默认的.notdef Glyph

代码中maxp.numGlyphs设置为len(glyph_data)+1,意味着需要包含一个默认Glyph(通常是.notdef),否则字体结构不完整,可能导致保存或使用出错:

# 创建默认的.notdef glyph
notdef_glyph = TTGlyph()
font['glyf']['.notdef'] = notdef_glyph

3. 补充字体表的必要属性

原代码缺少部分字体表的必填属性,会导致保存时出现警告或错误,需要补充完整。以下是修正后的完整create_font_from_glyphs函数:

def create_font_from_glyphs(glyph_data, output_path):
    font = TTFont()

    # 初始化head表并补充必要属性
    font['head'] = newTable('head')
    font['head'].unitsPerEm = 1000
    font['head'].fontRevision = 1.0
    font['head'].checkSumAdjustment = 0
    font['head'].magicNumber = 0x5F0F3CF5
    font['head'].flags = 0x0001
    font['head'].created = 0
    font['head'].modified = 0

    # 初始化hhea表并补充必要属性
    font['hhea'] = newTable('hhea')
    font['hhea'].ascent = 900
    font['hhea'].descent = -100
    font['hhea'].lineGap = 0
    font['hhea'].advanceWidthMax = 1000
    font['hhea'].minLeftSideBearing = 0
    font['hhea'].minRightSideBearing = 0
    font['hhea'].xMaxExtent = 1000
    font['hhea'].caretSlopeRise = 1
    font['hhea'].caretSlopeRun = 0

    font['maxp'] = newTable('maxp')
    font['maxp'].numGlyphs = len(glyph_data) + 1  

    # 初始化glyf表及glyphs字典
    font['glyf'] = newTable('glyf')
    font['glyf'].glyphs = {}

    # 添加默认.notdef glyph
    notdef_glyph = TTGlyph()
    font['glyf']['.notdef'] = notdef_glyph

    # 添加自定义glyphs
    for glyph, glyph_name, _ in glyph_data:
        font['glyf'][glyph_name] = glyph

    # 初始化cmap表
    cmap = newTable('cmap')
    cmap.tableVersion = 0
    cmap.tables = []
    subtable = CmapSubtable.newSubtable(4)
    subtable.platformID = 3
    subtable.platEncID = 1
    subtable.language = 0
    subtable.cmap = {unicode: glyph_name for _, glyph_name, unicodes in glyph_data for unicode in unicodes}
    cmap.tables.append(subtable)
    font['cmap'] = cmap

    # 初始化name表,补充PostScript名称
    font_name = "Example Font"
    name = newTable('name')
    name.names = []  
    # 字体名称(nameID 1)
    nameRecord = NameRecord()
    nameRecord.nameID = 1
    nameRecord.platformID = 3
    nameRecord.platEncID = 1
    nameRecord.langID = 0x409
    nameRecord.string = font_name.encode('utf-16-be')
    name.names.append(nameRecord)
    # PostScript名称(nameID 6)
    nameRecord_ps = NameRecord()
    nameRecord_ps.nameID = 6
    nameRecord_ps.platformID = 3
    nameRecord_ps.platEncID = 1
    nameRecord_ps.langID = 0x409
    nameRecord_ps.string = "ExampleFont".encode('utf-16-be')
    name.names.append(nameRecord_ps)
    font['name'] = name

    # 初始化post表并补充必要属性
    font['post'] = newTable('post')
    font['post'].italicAngle = 0
    font['post'].underlinePosition = -100
    font['post'].underlineThickness = 50
    font['post'].isFixedPitch = 0
    font['post'].formatType = 3
    # 映射glyph名称到索引
    glyph_names = ['.notdef'] + [g[1] for g in glyph_data]
    font['post'].mapping = {name: idx for idx, name in enumerate(glyph_names)}

    font.save(output_path)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 00:07:02