news 2026/8/20 15:31:10

libdxfrw 实战手册:用 C++ 免费读写 DXF、解析 DWG 的完整路线图

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
libdxfrw 实战手册:用 C++ 免费读写 DXF、解析 DWG 的完整路线图

libdxfrw 实战手册:用 C++ 免费读写 DXF、解析 DWG 的完整路线图

【免费下载链接】libdxfrwC++ library to read and write DXF/DWG files项目地址: https://gitcode.com/gh_mirrors/li/libdxfrw

"客户发来的图纸是.dwg,我们软件只认.dxf,怎么办?"——这几乎是每个做 CAD 二次开发的工程师都撞过的墙。买商业 SDK?年费劝退;自己逆向二进制格式?DWG 的私有编码足以耗掉你几个月。其实还有第三条路:libdxfrw,一个开源的 C++ 库,能读写 ASCII 与二进制两种 DXF,并解析 R14 到 2015 的 DWG 文件。本文就是一份带你把这条路走通的完整教程。

一、它从哪来,凭什么能省下六位数预算

故事要从 2011 年说起。作者 Rallaz 在做 LibreCAD 的 DXF/DWG 支持时,发现商业方案要么贵、要么闭源,索性自己写了一个轻量库,后来由社区持续接手维护至今。它的定位很朴素:不追求替代 AutoCAD,只负责把图纸文件"读进内存、写回磁盘"这一件事做扎实

和商业 SDK 比,它的优势一眼可见:

  • 💰 免费开源,协议为 GPL v2(或更高版本),没有授权费,只有"你的代码要兼容 GPL"这一条契约需要留意;
  • 📦 依赖极简,核心只有 iconv 一个外部依赖(字符编码转换),不会拖入一整套重型框架;
  • 🧩 自带dwg2dxfdwg2text两个可执行工具,既是生产力工具,也是现成的 API 教学案例。

一句话总结:商业 SDK 是"全套装修交付",libdxfrw 是"毛坯房 + 设计图",后者需要你亲手砌墙,但便宜得几乎等于免费。

二、五分钟跑通第一个程序:安装、读、写

第一步:三行命令完成编译安装

推荐 CMake 路线,依赖只需build-essentialcmakelibiconv

git clone https://gitcode.com/gh_mirrors/li/libdxfrw cd libdxfrw && 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,链接时加上-ldxfrw -liconv即可。

第二步:写一个最小读取器

整个库的心脏是回调接口DRW_Interface:你只负责"收到实体后做什么",文件怎么切分、版本怎么识别,全由dxfRW::read()驱动。注意一个坑:DRW_Interface里几十个纯虚函数必须全部实现,否则编译直接失败,偷懒不行的。

#include <cstdio> #include "libdxfrw.h" class GeoDumper : public DRW_Interface { public: void addHeader(const DRW_Header*) override {} void addLType(const DRW_LType&) override {} void addLayer(const DRW_Layer&) override {} void addDimStyle(const DRW_Dimstyle&) override {} void addVport(const DRW_Vport&) override {} void addTextStyle(const DRW_Textstyle&) override {} void addAppId(const DRW_AppId&) override {} void addBlock(const DRW_Block&) override {} void setBlock(const int) override {} void endBlock() override {} void addLine(const DRW_Line& e) override { printf("LINE (%g, %g) -> (%g, %g)\n", e.basePoint.x, e.basePoint.y, e.secPoint.x, e.secPoint.y); } void addCircle(const DRW_Circle& e) override { printf("CIRCLE center(%g, %g) r=%g\n", e.basePoint.x, e.basePoint.y, e.radious); } // 其余 add*/linkImage/addComment 均留空实现 void addPoint(const DRW_Point&) override {} void addRay(const DRW_Ray&) override {} void addXline(const DRW_Xline&) override {} void addArc(const DRW_Arc&) override {} void addEllipse(const DRW_Ellipse&) override {} void addLWPolyline(const DRW_LWPolyline&) override {} void addPolyline(const DRW_Polyline&) override {} void addSpline(const DRW_Spline*) override {} void addKnot(const DRW_Entity&) override {} void addInsert(const DRW_Insert&) override {} void addTrace(const DRW_Trace&) override {} void add3dFace(const DRW_3Dface&) override {} void addSolid(const DRW_Solid&) override {} void addMText(const DRW_MText&) override {} void addText(const DRW_Text&) override {} void addDimAlign(const DRW_DimAligned*) override {} void addDimLinear(const DRW_DimLinear*) override {} void addDimRadial(const DRW_DimRadial*) override {} void addDimDiametric(const DRW_DimDiametric*) override {} void addDimAngular(const DRW_DimAngular*) override {} void addDimAngular3P(const DRW_DimAngular3p*) override {} void addDimOrdinate(const DRW_DimOrdinate*) override {} void addLeader(const DRW_Leader*) override {} void addHatch(const DRW_Hatch*) override {} void addViewport(const DRW_Viewport&) override {} void addImage(const DRW_Image*) override {} void linkImage(const DRW_ImageDef*) override {} void addComment(const char*) override {} void writeHeader(DRW_Header&) override {} void writeBlocks() override {} void writeBlockRecords() override {} void writeEntities() override {} void writeLTypes() override {} void writeLayers() override {} void writeTextstyles() override {} void writeVports() override {} void writeDimstyles() override {} void writeAppId() override {} }; int main(int argc, char** argv) { if (argc < 2) return 1; GeoDumper dumper; dxfRW file(argv[1]); // 第二个参数传 false:保留原始 3D 坐标,不做 2D 投影 return file.read(&dumper, false) ? 0 : 1; }

编译命令:

g++ dump.cpp -o dump -ldxfrw -liconv ./dump drawing.dxf

屏幕上滚动出每条直线和圆的坐标,你的第一个 DXF 读取程序就此诞生。

第三步:反向操作,把实体写回文件

生成 DXF 同样是回调驱动,只是方向相反——库会依次调用你的writeHeaderwriteLayerswriteBlockswriteEntities等函数,你只需在writeEntities里用dxfWriter指针把实体"塞"出去:

class DemoWriter : public GeoDumper { public: void save(const char* name) { dxfRW out(name); out.write(this, DRW::AC1015, false); // AC1015 = AutoCAD 2000 ASCII } void writeEntities() override { DRW_Line line; line.basePoint = DRW_Coord(0, 0, 0); line.secPoint = DRW_Coord(100, 100, 0); dxfWriter->writeLine(&line); DRW_Circle circle; circle.basePoint = DRW_Coord(50, 50, 0); circle.radious = 25; dxfWriter->writeCircle(&circle); } dxfRW* dxfWriter; };

生成的 DXF 可以直接丢进任意 CAD 软件打开。注意版本号要用DRW::AC1015这种 AC 开头的编码AC1009=R12、AC1015=2000、AC1018=2004、AC1021=2007、AC1024=2010、AC1027=2013),记不住没关系,src/drw_base.h里注释写得清清楚楚。

三、一张表看清它能做什么、适合谁

能力维度支持情况说明
DXF 读取✅ ASCII / 二进制版本覆盖 R12、2000、2004、2007、2010
DXF 写入✅ ASCII / 二进制write()第三个参数传true输出二进制
DWG 读取✅ 只读内置 15/18/21/24/27 五套版本化阅读器,对应 R14 至 2013/2015
DWG 写入❌ 不支持边界清晰:想生成 DWG,先写 DXF 再转换
字符编码✅ 自动转换内置日/中/韩/繁 4 张码表,回调里通常是 UTF-8

适用人群很集中:做 CAD 二次开发的工程师、需要批量提取图纸数据的制造业/BIM 软件开发者、想给产品加"图纸导入"能力的嵌入式团队。如果你只是偶尔手动转一个文件,直接跳去下一节用现成工具即可。

四、三个实战场景,从命令到原理逐层拆解

场景一:批量 DWG 转 DXF,一行命令搞定

要解决的问题:几十张老图纸要从 DWG 统一转成 DXF,逐张手工转换太痛苦。

关键命令:仓库里现成的dwg2dxf就是干这个的:

./dwg2dxf input.dwg -v2000 output.dxf # 输出 2000 版 ASCII DXF ./dwg2dxf input.dwg -v2007 -b output.dxf # -b 输出二进制 DXF ./dwg2dxf list.txt -B -y outdir/ # -B 批量;-y 静默覆盖已存在文件

版本参数可选-R12 / -v2000 / -v2004 / -v2007 / -v2010

背后原理dwg2dxf复用了同一套DRW_Interface回调——先用dwgR把 DWG 解析成内存里的实体集合,再用dxfRW::write()按你指定的版本写出去。读进来、写出去,版本迁移天然完成,这就是为什么它是绝佳的参考实现。想看细节,直接读dwg2dxf/dx_iface.h,里面把"如何把收到的实体暂存、再统一转发给 writer"演了一遍。

场景二:抽取图纸文字,搞定中文乱码

要解决的问题:要从一批图纸里把 TEXT、MTEXT 标注全部提取入库,结果打印出来全是乱码。

关键思路libdxfrw内部通过src/intern/drw_textcodec.cpp和四张码表(CP932 日文 Shift-JIS、CP936 简体 GB2312、CP949 韩文、CP950 繁体 Big5)做自动转换,你只需在addText/addMText回调里取字段即可。真正导致乱码的,九成是源文件的 code page 声明和你的输出终端编码不一致——先查这两处,再怀疑库本身。

背后原理:图纸里的文本按绘制时的系统代码页存储,库在解析时先识别文件头部的代码页声明,再经 iconv 转成统一编码交给你的回调。所以"换一个输出环境乱码就消失了"这种情况,基本可以断定是终端或数据库的字符集没对齐。

场景三:写一个"格式无关"的图纸导入器

要解决的问题:业务系统要同时接收 DXF 和 DWG,不想写两套解析逻辑。

关键代码dxfRW管 DXF、dwgR管 DWG,两者read()签名一致,封装一层即可:

#include <string> #include "libdxfrw.h" #include "libdwgr.h" bool openDrawing(const char* path, DRW_Interface* sink) { std::string p(path); if (p.size() >= 4 && p.compare(p.size() - 4, 4, ".dxf") == 0) return dxfRW(path).read(sink, false); if (p.size() >= 4 && p.compare(p.size() - 4, 4, ".dwg") == 0) return dwgR(path).read(sink, false); return false; }

上层业务只认一种回调,底层格式差异被完全屏蔽。这是整个库最值钱的设计:接口统一,换文件格式对业务代码零侵入

五、常见误区集中扫雷

  • ⚠️"只实现几个实体回调就行了"DRW_Interface的纯虚函数一个都不能少,否则编译不过。偷懒的姿势是把不关心的回调全部写成空函数体。
  • ⚠️"版本号用DRW::R2000":没有这个枚举。版本在drw_base.h里是AC1009~AC1032的 AC 系列编码,照抄即可。
  • ⚠️"read()ext参数默认传true"true会把实体按 extrusion 投影成 2D、坐标值会被改写;要原始 3D 坐标就传false
  • ⚠️"试试用库直接写 DWG":不存在的功能。先写 DXF,再用dwg2dxf反向转换,是唯一正路。
  • ⚠️"乱码就改源码":先确认图纸 code page,再查输出端编码,见场景二,别让库背锅。

六、进阶技巧与继续深入的方向

技巧一:椭圆转多段线的精度控制。下游工具若不支持椭圆(比如老版本 R12),可以先把椭圆离散成多段线再导出:

dxf.setEllipseParts(64); // 分段数越大越接近真实椭圆,代价是文件变大

技巧二:调试开关定位解析问题。解析失败时打开调试输出,配合版本与错误码能快速判断是"版本不支持"还是"文件已损坏":

dxf.setDebug(DRW::DEBUG); // 读 DWG 时可用:dwgR::getVersion() / dwgR::getError()

继续深入的三份资料src/drw_interface.h是整个库的"契约书",所有回调都在这里,先读它效率最高;dwg2dxf/dwg2text/两个目录是官方示例,看完等于学会一半 API;tests/下覆盖实体读写、多段线、文字、表格、版本兼容、错误处理等场景的单元测试,构建后在 build 目录执行ctest一键验证。

最后提醒一句协议:**GNU GPL v2(或更高版本)**意味着如果你的项目要静态链接它,整个程序就得开源——商用前请务必评估这一条。

七、现在就动手

路线图已经铺好:三条命令装好库,一个继承DRW_Interface的类完成读取,一个write()调用完成写出,再加一条dwg2dxf命令解决格式迁移。接下来十分钟,你只需要做一件事——git clone https://gitcode.com/gh_mirrors/li/libdxfrw,然后把手头那张图纸交给dwg2dxf,或者把上面的读取器粘进你的工程编译一次。等屏幕上滚出第一行坐标,你就明白:CAD 文件,从此不再是你程序的禁区。

【免费下载链接】libdxfrwC++ library to read and write DXF/DWG files项目地址: https://gitcode.com/gh_mirrors/li/libdxfrw

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/20 15:25:52

Minimal neuron ablation triggers catastrophic collapse in the language core of Large Vision-Langu...

一、文章主要内容总结 该研究聚焦大型视觉语言模型(LVLMs)的结构脆弱性,核心探索“移除少量关键神经元是否会引发模型灾难性崩溃”这一问题。通过提出CAN(Consistently Activated Neurons)方法,结合神经元激活强度与梯度敏感性对神经元重要性排序,进而通过渐进式屏蔽实…

作者头像 李华
网站建设 2026/8/20 15:25:24

100份样本一次搞定:xcms代谢组学质谱数据处理实战入门

100份样本一次搞定&#xff1a;xcms代谢组学质谱数据处理实战入门 【免费下载链接】xcms This is the git repository matching the Bioconductor package xcms: LC/MS and GC/MS Data Analysis 项目地址: https://gitcode.com/gh_mirrors/xc/xcms 想象这样一个场景&…

作者头像 李华
网站建设 2026/8/20 15:24:56

AI不再“野蛮生长”!可信赖合规,成企业数字化新底线

很多企业还在纠结低代码够不够智能、能不能快速搭系统&#xff0c;却忽略了一个核心行业新规&#xff1a;AI数字化应用&#xff0c;正式进入“可监管、可追溯、可信赖”时代。 今年8月&#xff0c;国内GB/T 47507-2026《人工智能可信赖通则》正式落地实施&#xff0c;搭配EU AI…

作者头像 李华
网站建设 2026/8/20 15:22:52

毕夏AI官网:当ChatGPT学术版遇见AI5.0,论文写作不再是一场“孤军奋战”

论文不是“写”出来的&#xff0c;是一次从问题到答案的学术探险。毕夏AI只是你随身携带的“学术导航仪”。 如果你正在为毕业论文、课程论文或期刊投稿焦头烂额&#xff0c;大概率听过类似的说法&#xff1a;“AI写的论文不能用&#xff0c;文献全是编的。”这话对了一半——通…

作者头像 李华