☰
ezdxf 字体系统完全指南:字体发现、缓存管理与渲染原理
2026/10/15 15:07:09 网站建设 项目流程
  • 图形学

【免费下载链接】ezdxf

Python interface to DXF

项目地址:https://gitcode.com/gh_mirrors/ez/ezdxf
点击查看免费下载

导读

本文围绕 ezdxf v1.1 引入的ezdxf.fonts.fonts模块展开,它是 ezdxf 内部统一管理 TrueType/OpenType 字体(.ttf/.ttc/.otf)与 CAD 基本笔画字体(.shx/.shp/.lff)的核心枢纽,与 DXF 文档中由Textstyle实体定义的字体机制相互独立。读完本文,你将掌握:字体文件在三大操作系统中的搜索路径与默认字体选取规则、support_dirs配置的写法、系统字体缓存的重建方法(编程与命令行两种方式),以及FontFace、FontMeasurements、AbstractFont等核心抽象的含义与用法。

模块定位:渲染用字体与 DXF 字体的区别

ezdxf.fonts.fonts模块(源码位于 src/ezdxf/fonts/fonts.py)负责管理 ezdxf 内部的字体使用,它与 DXF 文件格式如何管理文字样式没有任何关系。

在 DXF 世界中,字体是通过Textstyle表项(参见 src/ezdxf/entities/textstyle.py)以 TTF 文件名(如 "LiberationSans-Regular.ttf")来存储的;而ezdxf.fonts.fonts模块解决的是另一个问题:给定一个字体文件名或字体属性,ezdxf 如何在当前系统上找到真实字体文件,并完成文本测量与字形路径渲染。二者协同工作:Textstyle提供字体声明,fonts模块提供查找、测量与渲染能力。

自 ezdxf v1.1(文档中标注versionadded:: 1.1)起,文本渲染由 fontTools 包完成,同时新增了对基本笔画字体(stroke fonts)的支持——即 CAD 应用中内置的矢量字体,如 .shx、.shp 或 .lff 文件。

⚠️版权提示:ezdxf不包含任何字体文件(.ttf、.ttc、.otf、.shx、.shp、.lff 均不内置)。原因在于这些字体文件受版权保护;而 LibreCAD 字体格式(.lff)虽然可自由获取,但采用 "GPL v2 or later" 许可,与 ezdxf 的 MIT 许可存在兼容性考量(参见 fonts/README.md)。

字体搜索位置(Font Locations)

TrueType 字体的系统搜索路径

字体管理器会递归搜索以下目录中的 .ttf、.ttc 和 .otf 字体文件。这些路径定义在 src/ezdxf/fonts/font_manager.py:

平台搜索目录
Windows~/AppData/Local/Microsoft/Windows/Fonts、<SystemRoot>/Fonts
Linux 及其他 *nix/usr/share/fonts、/usr/local/share/fonts、~/.fonts、~/.local/share/fonts、~/.local/share/texmf/fonts
macOS/Library/Fonts、/System/Library/Fonts

在 Linux 上,可以使用fc-list命令查看系统上所有可用字体及其位置。

默认字体的选取顺序

如果系统上没有任何可用字体,ezdxf 会按下述顺序选取默认字体,依次尝试,命中即止。源码中对应的完整列表见 src/ezdxf/fonts/font_manager.py(实际还包含 ArialUni.ttf、Arial Unicode.ttf 两个候选):

  1. Arial.ttf(Windows/macOS 常见)
  2. DejaVuSansCondensed.ttf(字形宽度与 Arial 相近)
  3. DejaVuSans.ttf
  4. LiberationSans-Regular.ttf
  5. OpenSans-Regular.ttf

如果这些字体在你的系统上均不存在,请安装其中之一。开源字体可以在本仓库的 fonts 目录中找到(该目录中的字体均为宽松许可,可随公开仓库分发)。

基本笔画字体的配置:support_dirs

CAD 应用(如 AutoCAD、BricsCAD)自带的基本笔画字体(.shx/.shp)在系统上没有统一的安装位置,除扫描全部驱动器外没有通用发现方法。因此,你需要在 ezdxf 配置文件中手动设置搜索路径,所有路径都会被递归搜索,相关配置项为ezdxf.options.support_dirs(其 setter/getter 实现在 src/ezdxf/_options.py)。

配置示例(写入 ezdxf 的 ini 配置文件):

[core] support_dirs = "C:\Program Files\Bricsys\BricsCAD V23 en_US\Fonts", ~/shx_fonts, ~/shp_fonts, ~/lff_fonts,

要点说明:

  • 路径支持~主目录展开,支持带引号的路径(FontManager.scan_all()会剥除路径两端的引号,见 src/ezdxf/fonts/font_manager.py);
  • 从源码实现看(src/ezdxf/fonts/font_manager.py),FontManager.build()在扫描系统默认字体目录之外,会追加options.support_dirs中的所有目录,且这些目录中不仅扫描 .shx/.shp/.lff,也扫描 .ttf/.ttc/.otf;
  • .shx字体虽然可从互联网下载,但请注意:并非所有网站声称的"免费"都属实,这些字体通常受版权保护,无法随开源项目分发。LibreCAD 的 .lff 字体可从其官方 GitHub 仓库获取(文档原文给出地址:https://github.com/LibreCAD/LibreCAD/tree/master/librecad/support/fonts)。

⚠️重要:当你向support_dirs新增字体目录,或向已有支持目录添加新字体后,必须重建字体缓存才能使用这些新字体,详见下文"重建字体缓存"一节。

字体缓存机制(Font Caching)

系统上可用的字体会被自动缓存。缓存文件保存在用户主目录~/.cache/ezdxf下(文件名font_manager_cache.json),或由环境变量XDG_CACHE_HOME指定的目录中(缓存路径的计算见 src/ezdxf/fonts/fonts.py)。

缓存生命周期:

  • 导入 ezdxf 时,缓存文件会被自动加载(模块末尾的load()调用,见 src/ezdxf/fonts/fonts.py);
  • 缓存文件不存在或加载失败时,会自动触发一次全量扫描并写回缓存;
  • 缓存采用 JSON 格式,带版本号(CURRENT_CACHE_VERSION = 2),每个条目记录字体的完整文件路径、family、style、weight、width 五项信息(序列化/反序列化实现在 src/ezdxf/fonts/font_manager.py);
  • 新增字体或新增support_dirs目录后,缓存不会自动更新,需要手动重建。

另外值得一提的是:测试系统(SUT)在 tests/conftest.py 中调用fonts.build_sut_font_manager_cache(),直接使用仓库内 fonts 目录的字体构建缓存,以保证测试在各平台(Windows、Linux、macOS)上可用字体一致。

重建字体缓存(Rebuilding the Font Cache)

当你向系统安装新字体,或在配置文件中新增support_dirs目录后,需要重建 ezdxf 的字体缓存以识别这些新字体。两种方式任选:

方式一:Python API

import ezdxf from ezdxf.fonts import fonts fonts.build_system_font_cache()

build_system_font_cache()的实现(src/ezdxf/fonts/fonts.py)会清空当前缓存、重新扫描全部搜索目录(含support_dirs),并将新缓存写回~/.cache/ezdxf/font_manager_cache.json。

方式二:ezdxf 命令行启动器

ezdxf --fonts

该命令由 src/ezdxf/main.py 实现:先打印 "Rebuilding system font cache." 并执行fonts.build_system_font_cache(),随后调用print_available_fonts()打印所有找到的字体;配合-v/--verbose参数可输出每个字体的详细信息(family、style、weight、width),见 src/ezdxf/fonts/font_manager.py。

核心 API:函数速查

ezdxf.fonts.fonts模块对外暴露的核心函数(全部定义在 src/ezdxf/fonts/fonts.py):

函数作用
make_font(font_name, cap_height, width_factor=1.0)返回基于AbstractFont的字体抽象对象,用于文本测量与文本路径渲染
get_font_face(font_name, map_shx=True)根据字体文件名返回FontFace定义;可选参数map_shx控制是否把 SHX 字体名映射为 TTF 替代字体(如 "TXT" → "txt_____.ttf")
find_font_face(font_name)返回给定字体文件名的FontFace定义,找不到时返回默认字体的FontFace
find_font_file_name(font_face)返回与给定FontFace匹配的字体文件名(不含父目录,如 "Arial.ttf")
find_best_match(family, style, weight, width, italic)基于描述性属性返回最佳匹配的FontFace,找不到返回None
get_entity_font_face(entity, doc=None)返回实体关联文字样式所定义的FontFace,支持Textstyle中的扩展字体信息
get_font_measurements(font_name, map_shx=True)获取字体的FontMeasurements(基准线、大写高度、x 高度、下行高度)
build_system_font_cache()重建系统字体缓存
load()重新加载所有缓存文件(ezdxf 导入时自动调用)
sideload_ttf(font_path, cap_height)绕过 FontManager,直接从文件系统加载 TrueType 字体的专家级接口,需要绝对路径

make_font:字体抽象的统一入口

make_font()依据文件扩展名分派到具体字体类(src/ezdxf/fonts/fonts.py):

  • .ttf/.ttc/.otf→TrueTypeFont
  • .shx/.shp→ShapeFileFont
  • .lff→LibreCadFont
  • 无扩展名(如 "TXT")→ 按 family 为字体名、style 为 ".shx" 进行最佳匹配
  • 特殊名称*monospace→ 返回仅用于测试和基础测量的MonospaceFont(不渲染字形)

find_best_match:按属性而非字形匹配

find_best_match()的匹配依据是字体的描述性属性(family、style、weight、width、italic),而非字形形状对比。匹配逻辑在 src/ezdxf/fonts/font_manager.py:

  1. 先按 family 过滤(filter_family使用前缀匹配);
  2. 再按 style 过滤(filter_style使用子串匹配);
  3. 最后按 (weight 差值、italic 是否一致、width 差值) 排序取最优。

值得注意的实现细节:width 属性被用来对笔画字体的类型进行优先级排序(见 src/ezdxf/fonts/font_manager.py)——shapefile 中没有 width 概念,因此.shx记为 5、.shp记为 6、.lff记为 7,使匹配时优先选择 .shx。

get_entity_font_face:从实体反向解析字体

get_entity_font_face()(src/ezdxf/fonts/fonts.py)用于从 DXF 实体解析其关联文字样式的FontFace:

  • 支持所有具有 "style" 属性的实体(TEXT、MTEXT、DIMENSION 等);
  • 若Textstyle表项带有扩展字体数据(XDATA "ACAD"),则使用 family/italic/bold 三元组构建FontFace;
  • 否则回退到style.dxf.font中的 TTF 文件名;
  • 对未关联文档的虚拟实体,可显式传入doc参数解析。

Textstyle.get_extended_font_data()的实现(src/ezdxf/entities/textstyle.py)表明:扩展字体数据以 ACAD XDATA 形式存储,group code 1000 存放字体族名,1071 存放包含 italic/bold 标志位的整型标志。

核心 API:类速查

FontRenderType

字体渲染类型枚举(src/ezdxf/fonts/fonts.py):

成员含义
STROKE基本笔画字体,只能渲染为线性路径
OUTLINETrueType 或类似字体,可渲染为填充路径或轮廓描边

AbstractFont 及其子类

AbstractFont是 ezdxf 的字体抽象基类(src/ezdxf/fonts/fonts.py),为文本测量与文本路径渲染提供统一接口:

方法说明
text_width(text)返回给定文本在绘图单位下的宽度
text_width_ex(text, cap_height, width_factor)同text_width,但可绕过构造时存储的cap_height与width_factor
text_path(text)返回文本的 2D 路径(GlyphPath,即NumpyPath2d)
text_path_ex(text, cap_height, width_factor)同text_path,可覆盖默认的cap_height与width_factor
space_width()返回空格字符宽度(即词间距)

五个具体子类:

  • TrueTypeFont:基于 fontTools 完成测量与渲染(src/ezdxf/fonts/fonts.py),font_render_type = OUTLINE。渲染器TTFontRenderer(src/ezdxf/fonts/ttfonts.py)通过 fontTools 的BasePen将字形轮廓转换为 ezdxfPath,支持贝塞尔曲线与可选的字距(kerning)调整;
  • ShapeFileFont:AutoCAD 形状字体(.shx/.shp),渲染由 src/ezdxf/fonts/shapefile.py 模块完成,font_render_type = STROKE;
  • LibreCadFont:LibreCAD 字体(.lff),渲染由 src/ezdxf/fonts/lff.py 模块完成,font_render_type = STROKE;
  • MonospaceFont:等宽字体抽象,仅用于测试与通用文本测量,不渲染任何字形,其text_path()返回一个宽 x 高的矩形路径(src/ezdxf/fonts/fonts.py);
  • sideload_ttf()返回的未管理字体(_UnmanagedTrueTypeFont):绕过 FontManager 直接从文件系统加载,无回退字体、无错误处理,文档明确标注为"专家级功能,请谨慎使用"。

SHX 字体的 TTF 映射

make_font()与get_font_face()会通过map_shx_to_ttf()(src/ezdxf/fonts/fonts.py)将常用 SHX 字体名映射到替代 TTF:例如TXT→txt_____.ttf、SIMPLEX→simplex_.ttf、ROMANS→romans__.ttf、ISOCP→isocp.ttf等。完整的 24 组映射见源码中的SHX_FONTS字典(src/ezdxf/fonts/fonts.py),这些替代字体来自 Autodesk 产品安装(含免费的 DWG/DXF 查看器 TrueView),因此同样无法随 ezdxf 分发。resolve_shx_font_name()(src/ezdxf/fonts/fonts.py)还支持通过order参数控制解析顺序:"t"映射为 TrueType、"s"使用 shapefile、"l"映射为 LibreCAD 字体。

FontFace:通用字体属性描述

DXF 以 TTF 文件名存储字体,而FontFace类(src/ezdxf/fonts/font_face.py)提供了一种更通用的字体指定方式,它本质上是一个具名字元组(NamedTuple),包含五个字段:

属性类型/默认值说明
filenamestr,默认""不含父目录的字体文件名,如 "arial.ttf"
familystr,默认"sans-serif"字体族名,如 "Liberation Sans"、"Arial";也允许通用字体族名 "serif"、"sans-serif"、"monospace"
stylestr,默认"Regular"字体风格:"Regular"、"Italic"、"Oblique"、"Bold"、"BoldOblique" 等
weightint,默认400数值权重(usWeightClass),范围 0–1000
widthint,默认5数值宽度(usWidthClass),范围 1–9

派生只读属性:

  • weight_str:weight 的字符串表示,如 "Thin"、"Normal"、"Bold";
  • width_str:width 的字符串表示,如 "Condensed"、"Expanded";
  • is_italic:style 中是否含 "italic";
  • is_oblique:style 中是否含 "oblique";
  • is_bold:weight > 400 时为True。

weight 数值与字面名称对照(usWeightClass)

数值名称
100Thin
200ExtraLight
300Light
400Normal
500Medium
600SemiBold
700Bold
800ExtraBold
900Black

width 数值与字面名称对照(usWidthClass)

数值名称
1UltraCondensed
2ExtraCondensed
3Condensed
4SemiCondensed
5Normal / Medium
6SemiExpanded
7Expanded
8ExtraExpanded
9UltraExpanded

说明:weight与width的数值定义来自 OpenType 规范中的 usWeightClass / usWidthClass。FontFace的属性判断逻辑(如is_bold = weight > 400)与字符串映射(get_weight_str/get_width_str)见 src/ezdxf/fonts/font_face.py;相关单元测试见 tests/test_05_tools/test_515a_fonts_truetype.py。

FontMeasurements:字体度量数据

FontMeasurements(src/ezdxf/fonts/font_measurements.py)描述一个字体的关键排版度量,包含四个浮点字段:

字段说明
baseline基准线位置
cap_height大写字母高度
x_heightx 高度(小写 x 的高度)
descender_height下行字母(如 p、q)的下降高度

常用方法与属性:

  • scale(factor):按给定系数等比缩放全部度量;
  • scale_from_baseline(desired_cap_height):以期望的大写高度为基准,从基准线起缩放;
  • shift(distance):沿垂直方向平移基准线;
  • cap_top(= baseline + cap_height)、x_top(= baseline + x_height)、bottom(= baseline - descender_height)、total_height(= cap_height + descender_height)。

这些概念对应排版学中的"字体解剖学"(Font Anatomy),可参考 Visme 的《A Visual Guide to the Anatomy of Typography》与 fonts.com 的《Anatomy of a Character》入门。

对于 TrueType 字体,度量值由TTFontRenderer._get_font_measurements()通过字形路径包围盒计算(src/ezdxf/fonts/ttfonts.py):x_height 由字母 "x" 的包围盒得出、cap_height 由字母 "A" 得出、descender_height 由字母 "p" 得出。对于未映射的 SHX 字体名,get_font_measurements()会返回基于经验因子的估算值(DESCENDER_FACTOR = 0.333、X_HEIGHT_FACTOR = 0.666,见 src/ezdxf/fonts/fonts.py)。

实战示例:组合使用核心 API

下面的示例演示了字体查找、测量与路径渲染的完整链路(可对照测试用例 tests/test_05_tools/test_515a_fonts_truetype.py、tests/test_05_tools/test_515b_fonts_shapefiles.py 验证行为):

from ezdxf.fonts import fonts # 1. 查询字体定义:找不到时回退到默认字体 face = fonts.find_font_face("LiberationSans-Regular.ttf") print(face.family, face.style, face.weight, face.width) # Liberation Sans Regular 400 5 # 2. 按属性查找最佳匹配字体文件 font_file = fonts.find_font_file_name( fonts.FontFace(family="Arial", style="Regular", weight=400, width=5) ) # 3. 创建字体抽象并测量文本(cap_height=2.5 个绘图单位) font = fonts.make_font("LiberationSans-Regular.ttf", cap_height=2.5) width = font.text_width("Hello ezdxf") path = font.text_path("Hello ezdxf") # NumpyPath2d,可用于渲染或求交 # 4. 获取字体度量 m = fonts.get_font_measurements("LiberationSans-Regular.ttf") print(m.cap_height, m.x_height, m.descender_height) # 5. 重建缓存以识别新安装的字体 fonts.build_system_font_cache()

推荐阅读路径:若想深入文字样式与 DXF 侧字体的关系,可阅读 src/ezdxf/entities/textstyle.py;若想了解文本如何被渲染为实体或路径,可参考 examples/addons/text_entity_to_path.py 与 examples/addons/text_string_to_path.py 等示例;字体缓存与测试基建的衔接可查看 tests/conftest.py。

  • 图形学

【免费下载链接】ezdxf

Python interface to DXF

项目地址:https://gitcode.com/gh_mirrors/ez/ezdxf
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询