☰
libdwg与libredwg编译实战:从RAR包到DWG解析与SHP转换
2026/9/26 11:52:34 网站建设 项目流程

简介:本资源为libdwg-0.4源码包,面向需要读写AutoCAD DWG文件、又不愿承担商业授权费用的C语言开发者与图形软件爱好者。DWG是AutoCAD主流二维三维图形格式,而libDWG以LGPL协议开源,提供解析与生成DWG的能力,libredwg则可视为其实现或分支,适合有一定C语言与构建工具基础、希望将DWG支持集成进自有项目的中高级开发者。压缩包共71个文件,约578KB,以20个c源文件与9个h头文件为核心,另含m4、in等构建配置脚本,以及dwg、dxf示例图形、pdf文档、readme、install、changelog等说明文件,覆盖从解码、对象处理到变量管理的完整模块。目前已有870人学习下载。通过阅读源码与构建脚本,读者可理解DWG文件结构、编译流程与依赖配置,掌握在目标系统上生成库文件并链接使用的方法,为自建DWG处理工具或集成方案提供可复用的参考。

1. libdwg 与 libredwg:从一份 RAR 包到能读 DWG 的编译链路

你手上大概率有这么个东西:一个叫libdwg-0.4.RAR的压缩包,解出来是一堆 C 源码,目标是让程序能直接读 AutoCAD 的 DWG 文件。DWG 是闭源二进制格式,官方 SDK 要授权,所以开源圈子里真正能打的是libredwg——GNU 项目,能解析 R12 到 2018 的 DWG,还能转 DXF、JSON。而libdwg这个名字,历史上出现过好几个同名小项目,有的是早期实验性解析器,有的只是 libredwg 的旧称或分支。所以第一步不是急着./configure,而是先搞清楚你手里这份 0.4 到底是哪一支、依赖什么、能不能编过。这篇就按「先认清包 → 再搭编译环境 → 跑通最小解析 → 排掉典型坑 → 落到批量转换」的顺序走,目标读者是需要在 Linux 或 Windows 上把 DWG 读进自己程序、又不想买商业库的工程师。编译原理那套词这里用不上,我们要的是能跑起来的二进制。

2. 认清 libdwg-0.4 这个包:它和 libredwg 到底什么关系

2.1 先解包看目录结构,别急着编译

拿到 RAR 先解出来,Linux 下用unrar x,Windows 下用 7-Zip 或 WinRAR 都行。解完先别动编译脚本,花两分钟看目录,这一步能省掉后面一半的翻车。

unrar x libdwg-0.4.RAR cd libdwg-0.4 ls -la find . -maxdepth 2 -name "configure*" -o -name "CMakeLists.txt" -o -name "Makefile*" | head -20

逻辑说明:find这一行是在判断这个包用的是 autotools(有configure)、CMake(有CMakeLists.txt)还是手写 Makefile。三种构建系统的编译命令完全不同,先认出来再动手。参数上-maxdepth 2限制搜索深度,避免在大包里翻太久;head -20只是防止输出刷屏。

如果目录里有src/、include/、examples/这种结构,基本是正经库;如果只有几个.c文件加一个README,那多半是早期实验版本,功能有限,别指望它能解析 2018 格式。

2.2 判断它是不是 libredwg 的旧分支

打开README、NEWS、ChangeLog或者configure.ac,搜几个关键词:

grep -ri "libredwg\|GNU" README* NEWS* ChangeLog* configure.ac 2>/dev/null | head grep -ri "version" configure.ac 2>/dev/null | head

如果出现libredwg字样,说明这份 0.4 很可能是 libredwg 早期版本的打包,那你可以直接去用新版 libredwg,功能更全、bug 更少。如果完全没提 libredwg,只写libdwg,那它是一个独立小项目,解析能力通常只覆盖 R12/R13 这种老格式,遇到新版 DWG 会直接报错。

这里有个血泪经验:网上流传的libdwg-0.4很多是十几年前的快照,依赖的 autotools 版本很老,在新系统上autoreconf会报一堆宏未定义。遇到这种情况,优先考虑换 libredwg 主线版本,而不是硬啃老包。

2.3 依赖清单:编译前必须装齐的东西

不管哪一支,读 DWG 的库通常依赖这几样:

依赖作用缺失时的典型报错
gcc / g++编译器command not found: gcc
make构建make: command not found
autoconf / automake / libtoolautotools 构建configure.ac: no proper invocation of AM_INIT_AUTOMAKE
zlib-devel压缩数据解压cannot find -lz
libxml2-devel部分版本解析 XML 元数据libxml/tree.h: No such file
pkg-config查找依赖路径Package zlib was not found

Ubuntu/Debian 一把装:

sudo apt update sudo apt install -y build-essential autoconf automake libtool pkg-config zlib1g-dev libxml2-dev

CentOS/Kylin 系用yum install -y gcc gcc-c++ make autoconf automake libtool pkgconfig zlib-devel libxml2-devel。Kylin V10 上如果 gcc 版本太老(低于 7),后面编译 C99 代码会报错,需要先升级工具链,这是国产化环境里最常见的拦路虎。

3. 把 libdwg 编出来:autotools 与 CMake 两条路

3.1 autotools 路线:从 autoreconf 到 make install

如果第 2 章确认是 autotools 项目,标准流程是先生成 configure,再配置、编译、安装。

# 生成构建脚本,-i 表示自动补装缺失的辅助文件 autoreconf -fi # 配置:指定安装前缀,关掉不需要的组件加快编译 ./configure --prefix=/usr/local/libdwg \ --disable-shared \ --enable-static \ CFLAGS="-O2 -g" # 编译,-j 用满 CPU 核数 make -j$(nproc) # 安装到前缀目录 sudo make install

逻辑说明:autoreconf -fi里的-f强制重新生成、-i自动安装缺失的install-sh、missing等辅助脚本,老包缺这些文件时特别有用。--disable-shared --enable-static是只编静态库,方便后面嵌进自己的程序,不需要处理运行时动态库路径。CFLAGS="-O2 -g"保留调试符号,出问题时能用 gdb 跟进去。

参数怎么改:如果你要编动态库给多个程序共用,把--disable-shared换成--enable-shared;如果只想快速验证能不能编过,加--disable-dependency-tracking跳过依赖追踪,能快不少。

编译失败时先看config.log,90% 的依赖问题都记在里面,搜error和not found两个词基本能定位。

3.2 CMake 路线:老包新用更省心

有些 libdwg 分支带CMakeLists.txt,那就走 CMake,比 autotools 干净。

mkdir build && cd build cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local/libdwg \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_SHARED_LIBS=OFF cmake --build . -j$(nproc) sudo cmake --install .

逻辑说明:-DCMAKE_BUILD_TYPE=Release开优化,-DBUILD_SHARED_LIBS=OFF编静态库。CMake 的好处是依赖查找更明确,缺 zlib 会直接告诉你Could NOT find ZLIB,不像 autotools 那样藏在 config.log 里。

如果 CMake 报Could NOT find ZLIB,先确认zlib1g-dev装了,再不行就手动指定-DZLIB_ROOT=/usr。这类路径问题在交叉编译或国产系统上很常见。

3.3 验证编译产物:别装完就以为成了

编完先别急着写业务代码,用自带的示例或工具验证一下。

# 看装出来的库和头文件 ls -l /usr/local/libdwg/lib ls -l /usr/local/libdwg/include # 如果有命令行工具,直接拿一个 DWG 试 /usr/local/libdwg/bin/dwgread test.dwg 2>&1 | head -30

逻辑说明:dwgread是 libredwg 系常见的读取工具,能把 DWG 结构打印出来。如果你的包没有这个工具,就自己写个最小程序链接库,下一章给代码。2>&1 | head -30是把错误也抓出来,只看前 30 行,避免大文件刷屏。

如果dwgread报unknown DWG version,说明这个库不支持你测试文件的版本,换一个 R12 或 R2000 的老文件再试,先确认库本身能跑通,再谈格式覆盖。

4. 写最小解析程序:把 DWG 里的实体读出来

4.1 链接库并读取文件头

先写一个最小 C 程序,只做一件事:打开 DWG,读出文件头里的版本号和实体数量。这一步能跑通,说明库链接没问题。

#include <stdio.h> #include <dwg.h> /* 头文件路径按实际安装调整 */ int main(int argc, char **argv) { if (argc < 2) { fprintf(stderr, "usage: %s file.dwg\n", argv[0]); return 1; } Dwg_Data dwg; memset(&dwg, 0, sizeof(dwg)); /* 读取文件,返回 0 表示成功 */ int err = dwg_read_file(argv[1], &dwg); if (err != 0) { fprintf(stderr, "read failed, code=%d\n", err); return 2; } /* 打印版本和实体数量 */ printf("version: %s\n", dwg.header.version); printf("num objects: %d\n", dwg.num_objects); dwg_free(&dwg); return 0; }

逻辑说明:dwg_read_file是核心入口,把整个 DWG 解析进Dwg_Data结构。dwg.header.version是版本字符串,dwg.num_objects是对象总数。最后必须调dwg_free释放内存,否则大文件会泄漏。

编译命令:

gcc -o read_dwg read_dwg.c \ -I/usr/local/libdwg/include \ -L/usr/local/libdwg/lib \ -ldwg -lz -lm

参数说明:-I指定头文件路径,-L指定库路径,-ldwg链接主库,-lz是 zlib(DWG 内部有压缩段),-lm是数学库。如果报undefined reference,多半是漏了-lz或库顺序不对,把-ldwg放最前面、依赖库放后面。

4.2 遍历实体,提取线段和图层

读文件头只是热身,真正要用的是把实体遍历出来。下面这段遍历所有对象,筛出线段(LINE)并打印起点终点。

#include <stdio.h> #include <string.h> #include <dwg.h> int main(int argc, char **argv) { Dwg_Data dwg; memset(&dwg, 0, sizeof(dwg)); if (dwg_read_file(argv[1], &dwg) != 0) return 1; for (int i = 0; i < dwg.num_objects; i++) { Dwg_Object *obj = &dwg.object[i]; /* 只处理 LINE 类型实体 */ if (obj->type != DWG_TYPE_LINE) continue; Dwg_Entity_LINE *line = obj->tio.entity->tio.LINE; if (!line) continue; printf("LINE layer=%s start=(%.3f,%.3f) end=(%.3f,%.3f)\n", obj->tio.entity->layer ? obj->tio.entity->layer->name : "0", line->start.x, line->start.y, line->end.x, line->end.y); } dwg_free(&dwg); return 0; }

逻辑说明:dwg.object是对象数组,obj->type判断实体类型,DWG_TYPE_LINE是线段常量。obj->tio.entity->tio.LINE拿到线段的具体数据,start和end是三维坐标。图层名从obj->tio.entity->layer->name取。

参数说明:坐标是 double,打印用%.3f保留三位小数,够工程用。如果要做 DWG 转 SHP,这里拿到的就是几何源数据,图层名对应 SHP 的属性字段。注意有些实体layer指针可能为空,代码里做了判空,这是实际跑数据时必加的防护,否则遇到脏文件直接段错误。

4.3 把结果输出成可用的中间格式

直接打印只适合调试,要落地得输出成 JSON 或 CSV,方便后面转 SHP 或入库。

/* 在遍历循环里,把 LINE 输出成一行 CSV */ printf("%s,%.6f,%.6f,%.6f,%.6f\n", obj->tio.entity->layer ? obj->tio.entity->layer->name : "0", line->start.x, line->start.y, line->end.x, line->end.y);

逻辑说明:CSV 每行是「图层,起点X,起点Y,终点X,终点Y」,用%.6f保证精度。重定向到文件就是./read_dwg test.dwg > lines.csv。后面用 Python 的geopandas读这个 CSV 就能生成 SHP,这就是热搜里「dwg转shp」最朴素的实现路径——不依赖任何商业转换器。

参数说明:如果 DWG 里有圆弧、多段线,需要额外处理DWG_TYPE_ARC、DWG_TYPE_LWPOLYLINE,多段线要遍历顶点数组。先跑通线段,再逐步加类型,别一上来就想全支持。

5. 编译与运行中的避坑清单

5.1 现象:configure 报 AM_INIT_AUTOMAKE 未定义

原因:老包的configure.ac用的 automake 宏版本和新装的 automake 不匹配,或者aclocal.m4缺失。

解决:先跑aclocal,再autoreconf -fi。如果还报,检查configure.ac里的AM_INIT_AUTOMAKE参数,老版本可能写成AM_INIT_AUTOMAKE(libdwg, 0.4),新版要求至少两个参数,手动补上AM_INIT_AUTOMAKE([foreign])再重新生成。

5.2 现象:链接时报 cannot find -lz

原因:zlib 开发包没装,或者装在非标准路径。

解决:先dpkg -l | grep zlib确认zlib1g-dev在。如果装在/usr/local/zlib,编译时加-L/usr/local/zlib/lib -I/usr/local/zlib/include。国产系统上有时库名带版本后缀,用ls /usr/lib64/libz*看实际文件名,必要时建软链接。

5.3 现象:读文件返回非 0,但文件明明能打开

原因:DWG 版本超出库支持范围,或者文件是加密/损坏的。

解决:先用file test.dwg看文件类型,再用十六进制看头六个字节,AC1015是 2000 版,AC1032是 2018 版。如果版本太新,换 libredwg 主线版本。如果文件头正常但读失败,可能是文件被写保护或截断,用官方工具另存一份再试。

5.4 现象:遍历实体时段错误

原因:obj->tio.entity或tio.LINE为空指针,脏数据或非图形对象被当成实体处理。

解决:每次取指针前判空,并且用obj->type先过滤。另外dwg.num_objects在某些版本里包含非实体对象,遍历时要跳过type < 500的内部对象。加if (!obj->tio.entity) continue;这一行能挡掉大部分崩溃。

5.5 现象:编译过了但运行提示找不到动态库

原因:装的是动态库,但ldconfig没更新,或者LD_LIBRARY_PATH没设。

解决:sudo ldconfig刷新缓存,或者运行时临时export LD_LIBRARY_PATH=/usr/local/libdwg/lib:$LD_LIBRARY_PATH。要长期生效就写进/etc/ld.so.conf.d/libdwg.conf再ldconfig。静态库没这个问题,所以前面推荐--disable-shared。

6. 从单文件到批量:把 DWG 转 SHP 的落地技巧

单文件跑通只是起点,实际项目往往是一个目录几百个 DWG,要批量转成 SHP 入库。我的习惯是分两层:C 程序负责把 DWG 解析成 CSV,Python 负责把 CSV 转 SHP,各司其职,出问题好定位。

先写个批量脚本:

#!/bin/bash # batch_dwg2csv.sh IN_DIR="$1" OUT_DIR="$2" mkdir -p "$OUT_DIR" for f in "$IN_DIR"/*.dwg; do base=$(basename "$f" .dwg) ./read_dwg "$f" > "$OUT_DIR/$base.csv" 2>"$OUT_DIR/$base.err" if [ $? -ne 0 ]; then echo "FAIL: $f, see $base.err" fi done

逻辑说明:遍历输入目录所有.dwg,逐个调read_dwg,CSV 输出到目标目录,错误单独存.err文件。$?判断上一条命令退出码,非 0 就打印失败文件名。这样跑完一眼能看出哪些文件没转成功,不用一个个翻。

再用 Python 把 CSV 转 SHP:

import pandas as pd import geopandas as gpd from shapely.geometry import LineString df = pd.read_csv("lines.csv", header=None, names=["layer", "sx", "sy", "ex", "ey"]) # 每行构造一条线段几何 geom = [LineString([(r.sx, r.sy), (r.ex, r.ey)]) for r in df.itertuples()] gdf = gpd.GeoDataFrame(df[["layer"]], geometry=geom, crs="EPSG:4326") gdf.to_file("output.shp", encoding="utf-8")

逻辑说明:pandas读 CSV,shapely构造线段,geopandas写 SHP。crs="EPSG:4326"是坐标系,实际项目要按 DWG 原始坐标系改,比如国内常用EPSG:4490或带号的高斯投影。encoding="utf-8"防止中文图层名乱码,这是转 SHP 时最常见的坑。

参数怎么调:如果 DWG 坐标是毫米单位,转 SHP 前要除以 1000 换成米,否则地图上尺度全错。如果图层名有中文,SHP 的 DBF 字段名有 10 字符限制,超长会被截断,建议在 CSV 阶段就映射成短名。

验证方法:转完用 QGIS 打开 SHP,和原 DWG 在 CAD 里叠一下,看线段位置对不对。数量上,CSV 行数应该等于 SHP 要素数,对不上就是有实体被漏掉或过滤了。

这套链路我跑过几万张图纸,最深的教训是:别信「一次编译永久可用」。老库在新系统上迟早出问题,与其反复修老包,不如把解析层封装成独立进程,库换了只改这一层,上层 CSV 和 SHP 流程不动。另外,DWG 版本兼容是玄学,遇到读不了的文件先别怀疑代码,八成是版本问题,换 libredwg 新版或让上游另存为老版本,比死磕快得多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询