C++读写DXF/DWG文件不再头疼:libdxfrw库从编译到项目落地的实战指南
【免费下载链接】libdxfrwC++ library to read and write DXF/DWG files项目地址: https://gitcode.com/gh_mirrors/li/libdxfrw
如果你做过CAD相关的开发,大概率遇到过这样一个场景:甲方甩来一批DWG图纸,说"把里面的图层和标注信息导出来",但公司并没有采购AutoCAD,你手头只有一台Linux服务器和一堆文档需求。自己逆向DWG的二进制格式?那是一个以年为单位计算的深坑。这时候你就需要一个能直接嵌入C++项目的DXF/DWG解析库——libdxfrw正是为此而生的开源方案,它既能读写DXF文本与二进制格式,又能解析从R14到2015共七个大版本的DWG文件,许可证为GPL v2,商用前请自行评估合规性,但作为技术选型完全够用。
下面这篇文章不会按"安装→示例→总结"的套路罗列,而是沿着一个真实项目推进的节奏:先搞清楚这个库的能力边界,再把它编译进来,接着完成一次读写闭环,最后处理版本、编码这些容易翻车的细节。
先认清现实:这个库能做什么,不能做什么
很多人在选型时有个误区,以为"能读DWG"就等于"能写DWG"。libdxfrw的实际能力划分是这样的:
- DXF:支持读和写,ASCII文本格式与二进制格式都可以;
- DWG:只支持读,不支持写;
- 支持的DWG版本:从AutoCAD R14一直到2015版,覆盖了绝大多数存量图纸;
- 不支持的:2015之后的新版本格式,以及DWG写入需求。
想清楚这条边界能帮你少走弯路。如果你的业务是"把历史DWG批量转成DXF供下游处理",那它完全胜任;如果你的需求是"程序生成DWG交付",请另寻方案。
实体覆盖上,从点、线、圆、圆弧、椭圆,到轻量多段线(LWPolyline)、样条曲线、填充(Hatch)、标注(Dimension)、引线(Leader)、块引用(Insert)、图像(Image)都有对应的数据结构。表格对象方面,图层、线型、文字样式、标注样式、视口、AppId一应俱全。可以说,日常图纸里90%以上的内容它都认识。
编译入库:四条构建路径怎么选
项目同时维护了CMake、Autotools、Visual Studio工程和Docker构建脚本,选择依据很简单:
- 跨平台新项目,直接上CMake,这也是官方推荐路径;
- 老派Linux项目想塞进configure/make体系,用Autotools;
- Windows上原生开发,打开
vs2013/libdxfrw.sln即可; - 想为多个发行版产出便携二进制,用
docker/下的Dockerfile。
先看依赖:一个C++11兼容的编译器、CMake 3.10以上,以及libiconv(字符编码转换,处理中文注释和文字样式时必不可少)。Ubuntu/Debian下安装依赖:
sudo apt-get install build-essential cmake libiconv-hook-dev然后按CMake标准流程走:
mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release cmake --build . --config Release sudo cmake --build . --config Release --target install安装完成后头文件在/usr/local/include/,静态库在/usr/local/lib/libdxfrw.a。写你自己的程序时链接方式如下:
g++ -o myapp myapp.cpp -ldxfrw -liconv注意-liconv不能省,否则链接阶段会报一堆未定义符号。
核心设计:一个文件句柄,一张回调网
理解了编译,接下来是读懂它的API哲学。整个库的使用模型非常克制:对外只有一个主类dxfRW,构造时传入文件路径;真正干活的,是你自己实现的一个DRW_Interface子类。
dxfRW dxf("input.dxf"); // 绑定文件 dxf.read(&iface, false); // 读取,把内容“喂”给接口实现关键在于read()的第二个参数ext:为true时会对带厚度的实体做拉伸转换投影到2D。如果你只关心平面几何,可以试试置为true省去自己处理Z轴的麻烦。
DRW_Interface是一个纯虚函数集合,每种实体对应一个回调,比如addLine、addCircle、addMText、addHatch。这意味着解析过程是事件驱动的:库每解析出一个实体,就调用你的回调,你把数据收进自己的数据结构即可。这样做的好处是大文件不会撑爆内存——边读边消费,而不是一次性构建全图对象树。
想快速确认库里有哪些回调、实体结构长什么样,源码就是最好的文档:src/drw_interface.h定义了全部接口签名,src/drw_entities.h定义了各实体的成员字段,src/libdxfrw.h则是dxfRW的完整方法清单。库还自带一份技术规范文档SPECIFICATIONS.md,API细节、目录结构、版本支持表都在里面,建议先翻它再动手。
读取实战:把一张DXF里的内容"接住"
下面是一段可直接运行的读取示例,展示了继承DRW_Interface后最小要做什么:
#include <cstdio> #include "libdxfrw.h" class Collector : public DRW_Interface { public: // 只关心线和圆,其余回调给空实现 virtual void addLine(const DRW_Line& data) override { printf("LINE (%.2f, %.2f) -> (%.2f, %.2f)\n", data.basePoint.x, data.basePoint.y, data.secPoint.x, data.secPoint.y); } virtual void addCircle(const DRW_Circle& data) override { printf("CIRCLE center(%.2f, %.2f) r=%.2f\n", data.basePoint.x, data.basePoint.y, data.radious); } // 其余纯虚函数……省略(写全约40个) }; int main() { Collector c; dxfRW dxf("drawing.dxf"); if (!dxf.read(&c, false)) { printf("读取失败\n"); return 1; } return 0; }这段代码做了什么:当库解析到LINE实体时,addLine会被触发并收到坐标;解析到CIRCLE时同理。你不需要理解DXF内部那些0 LINE、10、20组码,坐标提取的脏活全被封装了。
值得注意的一个细节:DRW_Interface的纯虚函数有四十来个,直接继承会强迫你写一堆空壳。实际项目中常见做法是准备一个"基类适配器",把常用回调做成虚默认空实现,业务子类只覆写需要的部分,就像上面的Collector只关心线和圆。这正是dwg2dxf/dx_iface.cpp里采用的模式,你可以直接参考它的写法。
写入实战:从零生成一份DXF
读取是"库推着你走",写入则是"你推着库走"。dxfRW提供了一组writeXXX方法,每种实体对应一个。写入的入口是write(),它需要三个信息:你的接口实现、目标DXF版本、是否输出二进制。
#include "libdxfrw.h" class Producer : public DRW_Interface { public: void run(const char* path) { dxfRW dxf(path); // 输出AutoCAD 2000版本的ASCII DXF dxf.write(this, DRW::AC1015, false); } virtual void writeEntities() override { DRW_Line line; line.basePoint.x = 0; line.basePoint.y = 0; line.secPoint.x = 100; line.secPoint.y = 100; dxfW->writeLine(&line); // 注意这里的调用指针 DRW_Circle circle; circle.basePoint.x = 50; circle.basePoint.y = 50; circle.radious = 25; dxfW->writeCircle(&circle); } // 其余write*回调(writeHeader、writeLayers等)给空实现 };这段代码里有个容易踩的坑:writeEntities回调内需要通过dxfRW实例指针调用writeLine、writeCircle,所以通常把dxfRW*存成成员变量(上文示例里用dxfW表示)。write()的内部流程是:先调你的writeHeader、writeLayers、writeLTypes等表回调,再调writeEntities写入实体区,最后收尾。也就是说,写入顺序完全由你的回调决定,库只负责把这些内容按DXF语法序列化。
如果你需要把椭圆近似成多段线输出(某些下游软件不认椭圆),可以调用setEllipseParts(parts)控制分段数。这个细节在兼容性对接时很实用。
版本控制:AC1015 到底是什么
写入时指定的版本枚举在src/drw_base.h里,与AutoCAD代号一一对应:
| 枚举值 | 对应版本 |
|---|---|
| AC1009 | R11/R12 |
| AC1012 | R13 |
| AC1014 | R14 |
| AC1015 | AutoCAD 2000 |
| AC1018 | AutoCAD 2004 |
| AC1021 | AutoCAD 2007 |
| AC1024 | AutoCAD 2010 |
| AC1027 | AutoCAD 2013 |
选择版本的原则:向下兼容。老版本DXF能被新软件打开,但新版本DXF老软件不认。如果对接的是10年前的工业软件,选AC1015或AC1009更稳妥;如果对方明确要2010格式,再上AC1024。库内部在序列化时也会根据版本号决定某些组码是否输出,比如2004之后的版本才写某些扩展数据,这跟AutoCAD的官方行为一致。
DWG读取:五个阅读器各管一段
DWG解析是libdxfrw最硬核的部分。src/intern/下有一组版本专用的阅读器:
dwgreader15:R14;dwgreader18:R2004;dwgreader21:R2007;dwgreader24:R2010;dwgreader27:R2013/R2015。
库会先读取DWG文件头的版本标记,自动选择对应的阅读器,不需要你干预。DWG的二进制布局在不同版本间差异极大,每个阅读器都实现了自己那套句柄解析、对象映射逻辑,所以版本识别错了会直接读崩。
读取DWG的API和读DXF几乎一样,区别只是入口类换成了dwgR:
dwgR dwg("drawing.dwg"); dwg.read(&iface, false); // iface 同样是 DRW_Interface 实现这也解释了为什么dwg2dxf工具能把两种格式统一处理——它先按扩展名判断走dxfRW还是dwgR,读出结果后统一走DRW_Interface回调,再调write()输出。整个转换器的核心代码就几十行,是理解库用法的最佳范本,位置在dwg2dxf/目录。
中文乱码:编码与代码页的关系
DXF文件里中文标注乱码,几乎都是编码问题。DXF本身用代码页(Code Page)声明字符串编码,库的src/intern/drw_textcodec.cpp负责在读取时把文件内编码转换到本地处理,写回时再转换回去。
项目内置了四套代码页表:drw_cptable932.h(日文Shift-JIS)、drw_cptable936.h(简体中文GB2312)、drw_cptable949.h(韩文)、drw_cptable950.h(繁体中文Big5)。这些表直接编译进库,不依赖外部字典。
实战建议有两条:第一,程序里统一用UTF-8存文本,让库在边界处做转换;第二,如果发现中文乱码,先检查源DXF头部声明的代码页与实际内容是否一致——很多国产CAD写出的DXF头声明和真实编码对不上,这时往往需要先做一次编码探测。这个问题与库无关,而是上游文件本身不规范,识别出这类"脏数据"也是健壮解析的一部分。
仓库自带的两个现成工具
除了当库用,仓库还附带两个命令行工具,编译后立即可用。
dwg2dxf:DWG/DXF转DXF,支持批量。用法:
dwg2dxf input.dwg -v2000 -b output.dxf参数含义:-v2000指定输出为2000版(也可用-R12、-v2004、-v2007、-v2010),-b输出二进制DXF,-y覆盖已存在文件,-B进入批处理模式(从一个文本文件读取文件清单,输出到指定目录)。
dwg2text:从DWG中抽取文本实体(TEXT/MTEXT),适合做图纸文字检索、工程量清单提取这类场景。它的源码dwg2text/同样实现了完整的DRW_Interface,如果你想看"如何只挑文本不关心几何",这个例子比dwg2dxf更聚焦。
顺带一提,项目还配置了完整的测试体系,tests/下有针对实体、多段线、文本、表格、块、版本兼容性和错误处理的专项测试,构建后用ctest一键跑。改库源码做二次开发前,先把测试跑绿,能省下大量排查时间。
踩坑清单与下一步行动
最后把容易翻车的地方汇总一下,帮你少走弯路:
- DWG只读不写:需求是生成DWG请换方案,别在libdxfrw上死磕;
- 链接漏了
-liconv:undefined reference报错十有八九是它; DRW_Interface纯虚函数必须全实现:漏一个编译器就报错,建议做默认空实现的适配基类;- 版本选错:写DXF时版本号宁低勿高,对接老软件优先AC1015以下;
- 中文乱码先查源文件代码页:很多问题根源在输入文件本身不规范;
- 大文件读取:事件驱动模型天然省内存,不要在回调里做重量级操作(如写数据库),先收集后批量落库性能更好。
下一步建议很明确:把仓库克隆下来(git clone https://gitcode.com/gh_mirrors/li/libdxfrw),先编译跑通tests/,再照着dwg2dxf的代码写一个只提取标注信息的dwg2dim小工具。半小时内你就能拥有一套属于自己的CAD数据提取管线。
【免费下载链接】libdxfrwC++ library to read and write DXF/DWG files项目地址: https://gitcode.com/gh_mirrors/li/libdxfrw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考