ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

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

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 一个外部依赖字符编码转换不会拖入一整套重型框架 自带dwg2dxf与dwg2text两个可执行工具既是生产力工具也是现成的 API 教学案例。一句话总结商业 SDK 是全套装修交付libdxfrw 是毛坯房 设计图后者需要你亲手砌墙但便宜得几乎等于免费。二、五分钟跑通第一个程序安装、读、写第一步三行命令完成编译安装推荐 CMake 路线依赖只需build-essential、cmake和libiconvgit clone https://gitcode.com/gh_mirrors/li/libdxfrw cd libdxfrw mkdir build cd build cmake .. -DCMAKE_BUILD_TYPERelease 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 同样是回调驱动只是方向相反——库会依次调用你的writeHeader、writeLayers、writeBlocks、writeEntities等函数你只需在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 开头的编码AC1009R12、AC10152000、AC10182004、AC10212007、AC10242010、AC10272013记不住没关系src/drw_base.h里注释写得清清楚楚。三、一张表看清它能做什么、适合谁能力维度支持情况说明DXF 读取✅ ASCII / 二进制版本覆盖 R12、2000、2004、2007、2010DXF 写入✅ ASCII / 二进制write()第三个参数传true输出二进制DWG 读取✅ 只读内置 15/18/21/24/27 五套版本化阅读器对应 R14 至 2013/2015DWG 写入❌ 不支持边界清晰想生成 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参数默认传truetrue会把实体按 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/两个目录是官方示例看完等于学会一半 APItests/下覆盖实体读写、多段线、文字、表格、版本兼容、错误处理等场景的单元测试构建后在 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),仅供参考
返回列表