# DWG / DXF 网页查看器 浏览器端直接打开 DWG / DXF 图纸,**不经过任何转换服务**。界面沿用新迪 2D 查看器的模板 (顶栏 + 左侧图层面板 + 底部大图标工具栏 + 浮动子面板 + 状态栏)。 配套的 3D 版本在 `../STEPViewer/`;两者的统一入口在 `../index.html`(按文件后缀自动分发, 入口也支持拖拽打开,文件只在本机流转、不落地)。 --- ## 一、能力 | | | |---|---| | **格式** | `.dwg`(LibreDWG 覆盖 R13 ~ R2018,越新的版本支持度越低)、`.dxf`(ASCII,UTF-8 / GBK 自动识别) | | **实体** | LINE / CIRCLE / ARC / ELLIPSE / LWPOLYLINE / POLYLINE / SPLINE(NURBS+拟合点) / POINT / SOLID / 3DFACE / HATCH / TEXT / MTEXT / ATTRIB / DIMENSION / LEADER / MULTILEADER / INSERT(含阵列、嵌套、块内 0 层继承) | | **显示** | 图层开关与单层隔离、真实线宽、线型(虚线/中心线)、ACI 与真彩色、**三种背景(浅灰/米白/黑,同新迪 2D)**、过暗/过亮线色按背景自动调整(可关)、剖面线(含 ANSI31~38 内置图案)、文字(中文、MTEXT 格式码/堆叠分数/按框宽折行) | | **交互** | 滚轮定点缩放、拖动平移、框选放大、双击全图、**视图旋转(设置里左转/右转 90° + 角度滑杆,旋转后拾取/测量/平移仍然准确)**、点选查属性、对象捕捉(端点/中点/圆心/象限点/最近点) | | **测量** | 坐标、点到点、连续、线段长、弧长、点到线、线到线(平行间距 / 夹角)、线性(ΔX/ΔY)、角度、半径直径、面积周长、结果统计;单位/小数位可调,已有结果跟着变 | | **批注** | 文字、自由线、直线、箭头、矩形、圆、云线;颜色与线宽可调、可撤销(批注存世界坐标,跟着图纸缩放旋转) | | **其它** | 文字全图查找并定位、模型/布局切换、PNG 截图、打印/导出 PDF、全屏(顶栏 + 底部工具栏各一个)、iframe 嵌入模式、上传加密存服务器并生成随机文件名的嵌入链接 | 实测(Edge headless,1500×950): | 图纸 | 实体数 | 解析+展平 | 每帧重绘 | |---|---|---|---| | XQD601线图.dwg(542 KB,CAXA) | 3 865 | 340 ms | 3.1 ms | | 行星滚柱丝杠专利图.dwg(2.0 MB) | 29 097 | 991 ms | 6.3 ms | | 气垫调整机构.dxf(4.3 MB) | 21 732 | ~1.5 s | 8.8 ms | --- ## 二、目录 ``` DWGViewer/ ├── index.html 界面骨架(新迪 2D 模板) ├── style.css ├── app.js UI 装配:加载、图层面板、工具切换、面板、导出 ├── js/ │ ├── source.js 文件 → 统一文档模型(分发 dwg / dxf,角度统一成弧度) │ ├── dxfHatch.js DXF 的 HATCH 补丁解析(dxf-parser 不支持 HATCH) │ ├── flatten.js 文档 → 显示列表(INSERT 递归展开、变换烘焙进坐标) │ ├── curves.js 圆弧/椭圆/NURBS/bulge 离散化、二维仿射、紧包围盒 │ ├── hatch.js 填充边界提取 + 图案线生成(奇偶规则裁剪)+ 内置 acad.pat 图案表 │ ├── mtext.js MTEXT 格式码解析(\P \H \S \F {} 等) │ ├── render.js Canvas2D 渲染器、空间索引、拾取 │ └── tools.js 对象捕捉、测量、批注 ├── libs/ │ ├── dxf-parser/ MIT,DXF 解析(已改成浏览器可直接 import 的相对路径) │ ├── shims/loglevel.js dxf-parser 依赖的 loglevel 替身(importmap 映射) │ └── libredwg/ GPL-3.0,DWG 解析(dist 包装 + 10 MB wasm),只在打开 .dwg 时才加载 ├── icons/ 取自新迪 2D/3D 案例的图标 └── samples/ 测试图纸 ``` **数据流**:文件 → `source.js` → `flatten.js` → `render.js`。 展平只在加载时做一次,把 INSERT 的变换全部烘焙进世界坐标;之后缩放平移只重算屏幕坐标, 所以三万实体也能保持 6 ms 一帧。 --- ## 三、本地运行 ```powershell python $env:TEMP\nds_server_dwg.py # 端口 8920,服务 dev/ 根目录,日志 %TEMP%\req_log_dwg.txt ``` 浏览器打开统一入口 `http://127.0.0.1:8920/index.html`,支持: - 直接带参数(按后缀自动进 2D / 3D 查看器): `?file=DWGViewer/samples/XQD601.dwg`、`?file=STEPViewer/samples/as1-oc-214.stp` - 落地页选文件 / 拖拽:文件内容经 postMessage 直接送进查看器,只在本机流转 - 单独打开查看器也行:`/DWGViewer/index.html?file=samples/XQD601.dwg` 把 dwg/dxf 直接拖进页面也可以。 --- ## 四、部署 纯静态站点,把整个目录传上去即可,**没有服务端依赖**。两个必须确认的点: 1. **`.wasm` 的 MIME 必须是 `application/wasm`**,否则 DWG 解析加载不起来。 Apache 用同目录的 `.htaccess`;nginx 在 `mime.types` 里加 `application/wasm wasm;`。 2. **开 gzip/br 压缩**。`libredwg-web.wasm` 原始 10 MB,压缩后约 3 MB,差别很大。 嵌入到别的页面: ```html ``` `embed=1` 会隐藏顶栏。页面里的「嵌入」按钮可以直接生成这段代码。 > 启用防下载(api/*.php + .htaccess)后,图纸文件必须放在 DWGViewer 目录内(如 > `samples/xxx.dwg`),路径写 `file=samples/xxx.dwg`;`../` 逃出查看器目录的路径 > 会被令牌通道拒绝(防目录穿越)。 --- ## 五、许可证(重要) | 模块 | 许可证 | 何时加载 | |---|---|---| | 本项目自身代码 | 自有 | 总是 | | `dxf-parser` | **MIT** | 打开 .dxf 时 | | `libredwg-web`(LibreDWG) | **GPL-3.0** | **只有打开 .dwg 时**才动态 import | GPL-3.0 的传染性对前端是成立的:wasm 和 js 会下发到浏览器,这构成「分发」。 商用前请评估。三条可选路线: 1. **只对内 / 接受 GPL** —— 按 GPL-3.0 公开本查看器源码,最省事。 2. **只支持 DXF** —— 删掉 `libs/libredwg/`,`source.js` 里的 `loadDwg` 直接抛错,整套依赖就只剩 MIT。 代价是用户得先把 DWG 另存成 DXF。 3. **DWG 解析挪到服务端** —— 用 LibreDWG 的 `dwg2dxf` 命令行做 DWG→DXF(以独立进程调用, 通常被认为是「聚合」而非衍生作品),前端只留 MIT 的 DXF 路径;或换成 ODA SDK(商业授权)。 代码结构已经为此做了准备:`source.js` 里 DWG 只有 `loadDwg()` 一个入口,换掉它不影响其余任何模块。 --- ## 六、已知限制 - **SHX 字体不渲染真实字形**。DWG 里的 `.shx` 是矢量字库,浏览器读不了,这里按字体名映射到 系统字体(`gbcbig`/`hztxt` → 宋体,`txt`/`romans`/`isocp` → Arial)。字形和 AutoCAD 里有差异, 但内容、大小、位置、对齐都是对的。 - **DIMENSION 画的是它的匿名块**,也就是文件里存的那份图形,不会按标注样式重新排。 绝大多数情况这就是 AutoCAD 里看到的样子;只有别的软件生成的、没写块的标注会缺失。 - **DXF 只支持 ASCII 格式**。二进制 DXF 会给出明确提示,让用户另存。 - **非等比缩放 + 旋转的 INSERT 里的圆弧**会退化成折线(等比或无旋转时保留解析曲线, 任意放大都不出锯齿)。这种组合在机械图里极少见。 - **OLE、光栅图像(IMAGE)、代理实体(PROXY)不显示**,会计入「跳过」统计。 - `$INSUNITS` 在国内图纸里常年是默认值,所以测量单位默认按毫米,可在「设置」里改。 --- ## 七、防下载(已实现) 查看器读站内图纸时走「令牌 → 解密接口」,同时 `.htaccess` 封掉 `.dwg/.dxf/.enc` 的直接 GET: 1. 上传(嵌入面板的「上传当前图纸」):`api/upload.php` 把图纸 gzip 后 XOR 加密落盘, **落盘名是纯随机 hex**(`uploads/ab12cd34….enc`),原文件名藏在加密体里—— 链接里看不出是哪张图、属于哪个项目,也没法按名字猜其它图纸; 2. 读取:`api/token.php` 同源校验 + HMAC 短时令牌(5 分钟),`api/model.php` 校验后 解密流式返回,原文件名通过 `X-Filename` 头带回来显示; 3. `.htaccess`(站点根 + `api/` 目录双层)禁止源文件直链。 `app.js` 的 `fetchDrawing()` 会自动走令牌流程;接口没部署(纯静态托管/本地 python) 时自动退回直连,不影响开发。部署 PHP 后无需任何前端改动。 **部署注意**:`token.php` / `model.php` / `upload.php` 里的 `SECRET` 三个文件必须一致, 并且要改掉默认值。`uploads/` 目录要有写权限。 **群晖/nginx(.htaccess 不生效)**:`upload.php` 和 `model.php` 已预设 `$PRIVATE_DIR = '/volume1/dwgviewer_private';`——在 File Station 里于 Web 目录外建这个 文件夹并给 `http` 用户组读写权限(路径不对就改成实际路径)。图纸存私有目录,嵌入链接写 `?file=纯文件名.dwg`;留在 Web 目录里的样例按原路径仍可打开,但可被直链下载, 要保护的文件必须移进私有目录。 网页端无法绝对防抓包(数据最终要进浏览器内存,懂行的人总能从调试台把解密后的字节导出来), 这套方案挡的是直接下载、批量爬取和链接外传后的猜名。