DXF 是用于交换二维与三维图纸数据的格式,其设计目标是让不同的程序之间能以结构化方式传递几何、图层与标注信息。与以二进制记录为主的 DWG 相比,DXF 把数据组织为可逐行阅读的记录序列;这一差异只说明两者的编码方式不同,能否读取取决于交换双方各自实现的支持范围,而不构成格式之间的取舍结论。
解析 DXF 的过程,可以理解为从扁平的记录序列还原出带层级关系的图形对象。文件里没有指针或偏移量,段落与对象的边界完全由特定组码标出,因此解析器通常按顺序消费记录、维护一个状态机。下面从记录层、段层与对象层依次展开。
分组码:组码与值构成的二元组
DXF 的基本单位是「分组码加值」的二元组:一行是整数形式的组码,紧接一行是对应的值。组码既指示值的类型,也指示该值在所在对象中的语义位置。常见区间如下:
| 组码区间 | 值类型 | 常见含义 |
|---|---|---|
| 0–9 | 字符串 | 对象类型与名称 |
| 10–39 | 双精度浮点 | 坐标 |
| 40–59 | 双精度浮点 | 比例与尺寸 |
| 60–79 | 16 位整数 | 开关与计数 |
| 90–99 | 32 位整数 | 计数与标志位 |
| 100、102 | 字符串 | 子类标记与控制串 |
| 105 及 320–369 | 句柄 | 对象引用 |
| 210–239 | 双精度浮点 | 拉伸方向 |
| 999 | 字符串 | 注释 |
由于组码与类型绑定,解析器不必做语法推导就能判断一个值的类型。但同一组码在不同对象中的语义可能不同,例如组码 70 在有的对象里是标志位,在另一些对象里是计数,必须结合当前对象类型解释。
段(SECTION)的组织
DXF 顶层由若干段构成,每段以组码 0 的 SECTION 开始,组码 2 给出段名,以组码 0 的 ENDSEC 结束。常见段落包括:
- HEADER:图形全局变量,例如图形范围、当前图层、单位制与角度方向
- CLASSES:自定义类的定义
- TABLES:符号表集合,含图层表、线型表、文字样式表、标注样式表、块记录表等
- BLOCKS:块定义
- ENTITIES:模型空间与图纸空间中的实体
- OBJECTS:非图形对象,例如字典与布局
- THUMBNAILIMAGE:可选的预览图像
多数实现要求这些段按固定顺序出现,并在读到 EOF 时结束。段的顺序固定带来的好处是解析器可以在读完全文之前,就对后续内容的结构做出预期。
实体记录与坐标值
实体位于 ENTITIES 段内,以组码 0 给出实体类型,例如 LINE、CIRCLE、ARC、LWPOLYLINE、TEXT、INSERT。随后由若干组码给出属性:
- 组码 8 给出所属图层名
- 组码 10/20/30 给出起点、中心点等基准点坐标
- 组码 11/21/31 给出终点或方向点
- 组码 62、6、370、48 分别给出颜色、线型、线宽与线型比例
坐标值为双精度浮点,多数情况下位于世界坐标系。部分二维实体的坐标则以对象坐标系给出,需要结合组码 210 的拉伸方向换算到世界坐标系。几何之外,实体还会携带厚度、拉伸方向与可见性等字段,解析时需要一并保留。
0
SECTION
2
ENTITIES
0
LINE
8
轮廓
10
0.0
20
0.0
30
0.0
11
100.0
21
50.0
31
0.0
0
ENDSEC
0
EOF
块的展开与 INSERT
块在 BLOCKS 段中由 BLOCK 与 ENDBLK 界定,块内实体的坐标相对块基点保存。ENTITIES 段中出现的 INSERT 通过组码 2 指向块名,并由组码 10 给出插入点、41/42/43 给出轴向比例、50 给出旋转角、70 与 71 给出行列数,用于阵列插入。
展开时,块内每个实体的坐标按缩放、旋转、平移的顺序映射到宿主坐标系,再与 INSERT 自身所在层级的变换相乘。嵌套的 INSERT 会逐层叠加变换,因此展开需要递归处理。相关细节可参考图层管理与坐标系变换。
句柄与所有权
从 R13 起,多数 DXF 为每个对象分配句柄,写在组码 5 中,同一文件内的句柄互不重复。组码 330 记录对象的所有者,通常指向父对象的句柄;符号表记录、字典与布局之间的从属关系都由此表达。组码 1001 及以上的扩展数据还可以引用其他对象,用于挂接自定义信息。
并非所有文件都完整写出句柄。由程序手工生成或由简化导出器产出的文件,可能出现缺失或重复的句柄,因此解析器一般不能把句柄当作必然可靠的索引,需要准备以对象位置为兜底的引用方式。
编码形式与流式解析
DXF 有两种常见编码。ASCII 形式以文本行给出组码与值,文件体积较大,但支持逐行读取;二进制形式在开头带固定签名,随后以定长记录表示组码与值,读写成本较低。两者承载的语义一致,差别只在编码层。
由于记录是顺序排列的,解析器常实现为一个逐条消费的状态机:遇到未知组码就跳过,遇到缺失的 ENDSEC 就按下一个段的起点或文件结尾收尾。
// 逐行状态机:先读组码行,再读值行
function* readPairs(lines) {
let i = 0;
while (i < lines.length) {
const code = parseInt(lines[i].trim(), 10);
if (Number.isNaN(code)) { i += 1; continue; } // 跳过无法解释的行
yield { code, value: (lines[i + 1] ?? "").trim() };
i += 2;
}
}
对含块引用的文件,可以先把 BLOCKS 段中的块定义收集到一张表中,再处理 ENTITIES 段,这样 INSERT 出现时就能直接查到目标块。这种两遍处理牺牲一点内存,换来展开逻辑的简化。
小结
DXF 的结构可以概括为三层:分组码提供类型与语义标记,段提供顶层分类,实体与对象通过句柄表达引用。解析的关键在于把顺序无关的扁平记录还原成有层级、有引用的对象图,并对未知组码、缺失句柄与截断的段做容错处理。上述结构决定了实体坐标如何被还原,而坐标进入屏幕还需要经过矢量路径与线宽映射所述的那一层换算。