- 图形学
【免费下载链接】ezdxf
Python interface to DXF
导读
本文围绕 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 两个候选):
Arial.ttf(Windows/macOS 常见)DejaVuSansCondensed.ttf(字形宽度与 Arial 相近)DejaVuSans.ttfLiberationSans-Regular.ttfOpenSans-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:
- 先按 family 过滤(
filter_family使用前缀匹配); - 再按 style 过滤(
filter_style使用子串匹配); - 最后按 (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 | 基本笔画字体,只能渲染为线性路径 |
OUTLINE | TrueType 或类似字体,可渲染为填充路径或轮廓描边 |
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),包含五个字段:
| 属性 | 类型/默认值 | 说明 |
|---|---|---|
filename | str,默认"" | 不含父目录的字体文件名,如 "arial.ttf" |
family | str,默认"sans-serif" | 字体族名,如 "Liberation Sans"、"Arial";也允许通用字体族名 "serif"、"sans-serif"、"monospace" |
style | str,默认"Regular" | 字体风格:"Regular"、"Italic"、"Oblique"、"Bold"、"BoldOblique" 等 |
weight | int,默认400 | 数值权重(usWeightClass),范围 0–1000 |
width | int,默认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)
| 数值 | 名称 |
|---|---|
| 100 | Thin |
| 200 | ExtraLight |
| 300 | Light |
| 400 | Normal |
| 500 | Medium |
| 600 | SemiBold |
| 700 | Bold |
| 800 | ExtraBold |
| 900 | Black |
width 数值与字面名称对照(usWidthClass)
| 数值 | 名称 |
|---|---|
| 1 | UltraCondensed |
| 2 | ExtraCondensed |
| 3 | Condensed |
| 4 | SemiCondensed |
| 5 | Normal / Medium |
| 6 | SemiExpanded |
| 7 | Expanded |
| 8 | ExtraExpanded |
| 9 | UltraExpanded |
说明:
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_height | x 高度(小写 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
相关推荐
告别字体渲染异常:MacType缓存清理完全指南
告别字体渲染异常:MacType缓存清理完全指南 在Windows系统中使用MacType增强字体渲染时,你是否遇到过文字模糊、重叠或乱码等问题?这些常见的渲染
桌面应用系统编程Winlator字体渲染:TrueType字体与字体缓存优化
Winlator字体渲染:TrueType字体与字体缓存优化 引言:移动端Windows应用字体渲染的挑战 在Android设备上运行Windows应用程序时,
移动开发虚拟化BiTE架构解密:字体渲染系统实现原理
BiTE架构解密:字体渲染系统实现原理 BiTE作为专注于Rust支持的反汇编工具(Disassembler focused on comprehensive
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考