ezdxf 自定义数据存储:UserRecord 与 BinaryRecord 完全指南(XRECORD 深度解析)
图形学【免费下载链接】ezdxfPython interface to DXF项目地址https://gitcode.com/gh_mirrors/ez/ezdxf点击查看免费下载导读本文以 ezdxf 官方文档 docs/source/user_record.rst 为主体系统讲解如何在 DXF 文件中以XRECORD对象为载体安全地存储自定义应用数据。UserRecord负责存储int、float、str、Vec2、Vec3以及任意嵌套的list/dict结构BinaryRecord则用于存储任意二进制数据BLOB。读完本文你将掌握这两类辅助类的数据类型约束、底层 DXF 标签tag布局、三种典型挂载场景扩展字典、DICTIONARY、纯二进制存储以及读写全流程并了解对应的源码实现与测试验证。背景为什么需要 XRECORD 与自定义数据存储DXF 文件本身是一个高度灵活的数据容器除了图形实体还允许应用程序写入自定义数据。ezdxf 支持的常见自定义数据通道包括HEADER 段变量如$USERI1~$USERI5、$USERR1~$USERR5仅 10 个预定义变量容量极小且易被其他应用覆盖XDATA 扩展数据依附于实体AutoCAD 单实体限制 16kB且可用的组码有限XDICT 扩展字典 XRECORD可以挂载在任意实体上容量不受 16kB 限制OBJECTS 段中的 XRECORD / DICTIONARY适合存放文档级或对象级数据。UserRecord与BinaryRecord就是围绕XRECORD提供的简单且安全的高层封装。官方 XRECORD 参考文档 docs/source/dxfobjects/xrecord.rst 明确指出XRECORD 实体由 1~369 号组码构成This object is similar in concept to XDATA but is not limited by size or order概念上类似 XDATA但不受大小与顺序限制。版本前提XRECORD 方案要求 DXF 版本为R2000AC1015或更高。对于 DXF R12 及更早版本唯一可用的自定义数据通道是 XDATA扩展数据详见 docs/source/user_xdata.rst。一、UserRecord在 XRECORD 中存取 Python 结构UserRecord把一组 Python 对象序列化到XRECORD的标签容器中并能在读取时完整还原。它支持的 Python 类型包括类型说明int32 位整数受 DXF 标准限制而非 Python 限制float双精度C double浮点数str字符串Vec2/Vec3ezdxf 数学类型src/ezdxf/math 提供list/dict可嵌套的容器类型数据结构硬性要求根据 docs/source/user_record.rst 的规定写入UserRecord的 Python 结构必须满足顶层结构必须是list实现MutableSequence接口的序列字符串最长 2049 字符且不能包含换行符\n或\r字典的键必须是简单 Python 类型int、float、str嵌套支持list和dict可以在彼此内部任意嵌套例如list中放dict、dict的值是list、dict嵌套dict等。这些约束在 src/ezdxf/urecord.py 中有对应的运行时校验tags_from_list()对字符串做长度2049 抛DXFValueError和非法换行检查对不支持的 Python 类型如tuple、set抛DXFTypeError。底层 DXF 标签布局Tag LayoutUserRecord在XRECORD对象中使用的组码是固定的、不可更改的。只有UserRecord的首个标签是(2, 用户记录名)其余数据的映射如下Python 类型DXF 标签str(1, value)长度 2050 字符且不含换行int(90, value)32 位整数float(40, value)C doubleVec2(10, x), (20, y)Vec3(10, x), (20, y), (30, z)list以(302, [)开始以(302, ])结束dict以(302, {)开始以(302, })结束注意与普通 XRECORD 约定组码 1/2/10/40/90/330 等见 docs/source/tutorials/custom_data.rst不同的是UserRecord使用组码 302作为结构标签[、]、{、}该设计在 src/ezdxf/urecord.py 模块 docstring 中有明确说明The group code 302 is used as a structure tag.UserRecord 核心 API从 src/ezdxf/urecord.py 的类定义看UserRecord提供以下成员xrecord底层ezdxf.entities.XRecord实例name用户记录名任意字符串长度 2050 字符且不含换行dataPython 数据本体顶层必须是listMutableSequence支持 str/int/float/Vec2/Vec3/list/dict 及任意嵌套handle属性底层XRecord的 DXF 句柄commit()将data写回底层xrecord非with语句使用时必须手动调用支持上下文管理器with UserRecord(...) as ur:在退出块时自动调用commit()__str__返回str(self.data)。构造签名来自源码__init__UserRecord(xrecordNone, *, nameUserRecord, docNone)xrecord指定承载数据的XRecord实例为None时自动新建。若同时传入doc新 XRECORD 会被加入 DXF 文档的 OBJECTS 段doc.objects.new_entity(XRECORD, {})name记录名默认UserRecorddoc目标 DXF 文档对象。二、BinaryRecord在 XRECORD 中存储任意二进制数据BinaryRecord将bytes/bytearray/memoryview形式的二进制数据作为 BLOB 存入XRECORD。其data属性即二进制数据本体。关键成员与行为xrecord底层XRecord实例data二进制数据bytes、bytearray或memoryviewcommit()写入底层xrecord同样在with语句退出时自动调用__str__返回十六进制字符串表示如FEFE见测试用例。二进制数据在XRECORD中的物理存储方式以组码160的标签记录数据总字节数随后将数据按每块 127 字节切分逐块写入组码310的十六进制字符串标签。该过程由 src/ezdxf/lldxf/tags.py 中的binary_data_to_dxf_tags(data, length_group_code160, value_group_code310, value_size127)完成读取时由parse_binary_data()依据 160 标签的存在性拼接所有 310 标签还原原始字节src/ezdxf/urecord.py。提示把图片、Office 文档等二进制内容塞进 DXF 文件在技术上是可行的但 AutoCAD 会直接忽略这些数据——它们只对你的应用程序有意义docs/source/tutorials/custom_data.rst。三、实战三种典型使用场景完整代码官方示例 examples/user_data_stored_in_XRECORD.py 演示了三种最典型的挂载与读写方式下面完整解析。示例中先生成一个含齿轮轮廓gear来自 src/ezdxf/render/forms的文档并记录其句柄用于重载后定位实体。场景 1通过扩展字典XDICT挂载到图形实体扩展字典可以把 XRECORD 关联到任意 DXF 实体上是最常用的实体级自定义数据方案。注意通过扩展字典挂载的自定义数据不会随宿主实体一起做几何变换。from ezdxf.urecord import UserRecord from ezdxf.math import Vec3 with UserRecord(docdoc) as user_record: # 1. 在文档中新建 XRECORD并放入 user_record.xrecord # 2. 为 LWPOLYLINE 实体创建扩展字典 xdict gear_.new_extension_dict() # 3. 扩展字典成为 XRECORD 的拥有者owner xdict.link_dxf_object(MyData, user_record.xrecord) # 4. 写入受支持的类型与嵌套结构 user_record.data [ MyString, 4711, 3.1415, Vec3(1, 2, 3), { MyIntList: [1, 2, 3], MyFloatList: [4.5, 5.6, 7.8], }, ] # with 块退出时自动 commit()读取时先判断实体是否拥有扩展字典再按名称取出 XRECORD 并包裹为UserRecordif gear_.has_extension_dict: xdict gear_.get_extension_dict() entity xdict.get(MyData) if isinstance(entity, XRecord): user_record UserRecord(entity) pprint(user_record.data)场景 2存放到 DICTIONARY 对象文档级数据如果数据属于整个文档而非某个实体可以放进 OBJECTS 段的具名 DICTIONARY。doc.rootdict是 OBJECTS 段树状结构的根字典示例用get_required_dict(MyDict)获取或创建子字典再通过add_xrecord(MyData)创建 XRECORD对应 src/ezdxf/entities/dictionary.py 的get_required_dict()与add_xrecord()以及 src/ezdxf/sections/objects.py 的ObjectsSection.add_xrecord()。my_dict doc.rootdict.get_required_dict(MyDict) xrecord my_dict.add_xrecord(MyData) # DICTIONARY 是 XRECORD 的拥有者 # 不用 with手动构造 手动提交 user_record UserRecord(xrecord) user_record.data [Just another user record, 4711, 3.1415] user_record.commit() # 关键一步把 data 写回 XRECORD # 读取 entity my_dict[MyData] if isinstance(entity, XRecord): user_record UserRecord(entity) pprint(user_record.data)场景 3存储二进制数据import zlib from ezdxf.urecord import BinaryRecord my_dict doc.rootdict.get_required_dict(MyDict) xrecord my_dict.add_xrecord(MyBinaryData) with BinaryRecord(xrecord) as binary_record: data bStore any binary data, even line breaks\r\n * 20 # 二进制数据不挑剔换行甚至建议先压缩 binary_record.data zlib.compress(data, level9) # 退出 with 块时自动 commit()读取后解压即可还原entity my_dict[MyBinaryData] if isinstance(entity, XRecord): binary_record BinaryRecord(entity) pprint(binary_record.data) # 压缩后的字节 pprint(zlib.decompress(binary_record.data)) # 还原的原文三个场景写完统一doc.saveas(...)保存重载文件后按实体句柄doc.entitydb.get(handle)重新定位实体再读取数据。四、源码级原理序列化与反序列化如何工作UserRecord的核心逻辑集中在 src/ezdxf/urecord.py写入方向compile_user_record(name, data)先写入(2, name)首标签再递归调用tags_from_list()把每个元素转为对应组码。str走(1, ...)、int走(90, ...)、float走(40, ...)、Vec3直接写(10, ...)、Vec2会被升维为Vec3写(10, 20, 30)三轴遇到Sequence写(302, [)递归后写(302, ])遇到Mapping则用key_value_list()把键值对展平写入(302, {) ... (302, })之间。任何不支持的实例类型都会抛出DXFTypeError。读取方向parse_xrecord()/parse_items()先校验首标签是否为(2, name)类型或名称不匹配抛DXFTypeError然后用一个栈解析嵌套结构——遇到(302, [)或(302, {)压栈遇到(302, ])或(302, })弹栈合并END_DICT时用dict(take2(items))把键值对两两配对成字典。若结构标签不匹配缺开标签或缺闭标签会抛DXFStructureError。底层 XRECORD 实体XRecord定义在 src/ezdxf/entities/dxfobj.pyDXFTYPE XRECORD其tags属性就是原始 DXF 标签容器TagsUserRecord.commit()实际执行self.xrecord.tags compile_user_record(...)把编译结果整体赋回。ezdxf 不会对 XRECORD 内容做额外校验因此UserRecord/BinaryRecord承担了保证数据合法性的职责。测试验证行为即规范单元测试 tests/test_01_dxf_entities/test_139_user_record.py 覆盖了上述全部行为可作为行为规范参考平面记录解析str/float/int/Vec3 各自还原为正确 Python 类型嵌套结构往返顶层 list、list 内嵌 list、dict 内嵌 dict、dict 值放 list、list 内放 dict 等 6 种结构均能compile - parse无损还原错误路径缺开/闭标签抛DXFStructureError非法组码抛DXFValueError字符串含\n/\r或超过 2049 字符抛DXFValueErrorcommit()必要性不调用commit()时xrecord.tags为空调用后生成[(2, MyRecord), (1, str), (90, 1), (40, 3.1415)]BinaryRecordcommit()后生成[(160, 2), (310, b\xfe\xfe)]且可以安全存储\r\n换行字节。五、注意事项与最佳实践版本限制XRECORD 需要 DXF R2000R12 用户只能使用 XDATAdocs/source/user_record.rst。必须提交脱离with语句时修改data后务必手动调用commit()否则数据不会进入 XRECORD源码与测试都强调了这一点。字符串约束UserRecord的字符串 ≤ 2049 字符且不能含\n/\r需要任意字节内容时改用BinaryRecord。几何数据不被变换存放在 XRECORD 中的长度值、比例因子、顶点等几何属性不会随宿主实体一起被变换examples/user_data_stored_in_XRECORD.py 明确标注该注意事项。引用方式XRECORD 本身是 OBJECTS 段的独立对象必须被某处引用才有意义——常用途径是实体的扩展字典、具名 DICTIONARY或通过组码 330 句柄引用完整背景见 docs/source/tutorials/custom_data.rst。推荐读写入口创建 XRECORD 时优先使用工厂方法doc.objects.add_xrecord()或Dictionary.add_xrecord()避免直接实例化对象类docs/source/dxfobjects/xrecord.rst。结语UserRecord与BinaryRecord为 ezdxf 提供了一条类型安全 结构清晰的 XRECORD 自定义数据通道前者面向可嵌套的 Python 原生结构后者面向任意二进制 BLOB。理解其固定组码布局与commit()语义配合扩展字典与 DICTIONARY 两种挂载方式即可在 DXF R2000 文件中可靠地存取应用专属数据。进一步的完整教程请参阅 docs/source/tutorials/custom_data.rstAPI 细节可查阅 src/ezdxf/urecord.py 与 docs/source/dxfobjects/xrecord.rst。赞分享图形学【免费下载链接】ezdxfPython interface to DXF项目地址https://gitcode.com/gh_mirrors/ez/ezdxf点击查看免费下载相关推荐ezdxf 自定义数据存储全指南Header 变量、XDATA、扩展字典与 XRECORDezdxf 自定义数据存储全指南Header 变量、XDATA、扩展字典与 XRECORD 本篇指南面向使用 ezdxf 在 DXF 图纸中附加应用自定义数据图形学ezdxf XRecord 完全指南在 DXF 文件中存储应用自定义数据的官方实现与实战用法ezdxf XRecord 完全指南在 DXF 文件中存储应用自定义数据的官方实现与实战用法 本篇技术指南以 ezdxf 官方文档《XRecord》为基础深图形学ezdxf 扩展数据 XDATA 完全指南在 DXF 实体上存储自定义数据ezdxf 扩展数据 XDATA 完全指南在 DXF 实体上存储自定义数据 导读 扩展数据Extended Data简称 XDATA是 DXF 格式中用图形学上一篇从失望到惊喜如何用Mac Mouse Fix彻底改变你的macOS鼠标体验下一篇如何在Cypress中集成Visual-Regression-Tracker提升前端测试效率的实战教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考