# pdf2dwg **Repository Path**: atlisp/pdf2dwg ## Basic Information - **Project Name**: pdf2dwg - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-29 - **Last Updated**: 2026-09-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # atlisp-pdf2dwg 把 PDF 图纸/文档转换为可编辑的 **DXF**(可选 **DWG**)。 流程:**OCR 图文混排 → 原生 PDF 矢量提取 + 位图矢量化 → ezdxf 组装 DXF**。 - **OCR 图文混排**:默认为本地 RapidOCR(PP-OCR,逐行坐标、CPU、快);可选 surya VLM (整页布局 + 文本,精度高但重)。 - **原生矢量**:若 PDF 仍是矢量(含被转曲的文字),直接提取线条,比追踪位图更清晰、更小; 文字块内的字形轮廓会被剔除并替换为真实文字实体。 - **位图矢量化**:PDF 内嵌图片先按墨色感知的方式量化颜色,再用 **potrace** 拟合平滑贝塞尔 曲线(回退到 OpenCV 轮廓追踪),随后采样+简化多边形,按原始变换/旋转映射回图纸并裁剪到页面。 相同图片(页眉 logo、二维码等)跨页只追踪一次,并可多进程并行,质量与速度兼顾。 - **默认输出线条(LWPOLYLINE),不用 HATCH**。这是实测决定的:653 个 HATCH 的图经 LibreDWG 转 DWG 会报 653 条 `Skip HATCH common handles`(填充大面积丢失),同样内容用多边形则一条都没有。 代价是平铺多段线依赖绘制顺序、洞的正确性不如 HATCH,9 页实测召回 0.613 → 0.516。 若 DXF 是最终交付、且更在意洞的正确性,可加 `--vector-mode rings` 改用 HATCH。 - **页面堆叠**:多页 PDF 默认纵向堆叠到模型空间,页间留间距;可用 `--flat` 关闭。 ## 安装 ```bash pip install atlisp-pdf2dwg # 核心(矢量 + OpenCV 位图,不含 OCR) pip install "atlisp-pdf2dwg[potrace]" # + potrace 曲线追踪(推荐,图片更精细) pip install "atlisp-pdf2dwg[rapid]" # + RapidOCR(推荐的本地 OCR) pip install "atlisp-pdf2dwg[all]" # rapid + potrace pip install "atlisp-pdf2dwg[surya]" # + surya VLM(可选,重) ``` 需要 DWG 输出时另装 [GNU LibreDWG](https://www.gnu.org/software/libredwg/) 的 `dxf2dwg`, 或设置环境变量 `PDF2DWG_DXF2DWG=/path/to/dxf2dwg`。 ## 命令行 ```bash # 默认:RapidOCR + 矢量 + 位图,输出 DXF pdf2dwg convert 图纸.pdf -o 图纸.dxf # 指定页码、DPI、关闭位图矢量化 pdf2dwg convert 图纸.pdf --pages 1-5,8 --dpi 300 --no-images # 只提取矢量+位图(不 OCR,文字保留为轮廓) pdf2dwg convert 图纸.pdf --no-ocr # 输出 DWG(需 dxf2dwg) pdf2dwg convert 图纸.pdf --format dwg # 复用/缓存 OCR 结果 pdf2dwg convert 图纸.pdf --ocr-cache .ocr_cache # 图片追踪质量/性能 pdf2dwg convert 图纸.pdf --trace potrace --image-max-dim 1600 --image-colors 8 --jobs 8 pdf2dwg convert 图纸.pdf --trace opencv # 不装 potrace 时的回退 pdf2dwg convert 图纸.pdf --image-min-area 8 --image-max-polys 20000 # 压体积 # 扫描线稿:笔画中心线追踪(自带 4000 光栅与线宽) pdf2dwg convert 图纸.pdf --image-line-art on pdf2dwg convert 图纸.pdf --image-line-art on --no-line-width # 粗细交给 CAD 线宽 pdf2dwg convert 图纸.pdf --no-text-color # 文字不取真彩色 # 其它子命令 pdf2dwg ocr 图纸.pdf -o ocr_json --engine rapid pdf2dwg render 图纸.dxf -o preview.png --scale 1.5 pdf2dwg to-dwg 图纸.dxf pdf2dwg info # 查看引擎与 dxf2dwg 路径 ``` ## Python API ```python from pdf2dwg import convert_pdf convert_pdf( "图纸.pdf", "图纸.dxf", page_spec="1-10", # 1-based;或 pages=[0,1,2] dpi=200, ocr="rapid", # 或 "surya" / None images=True, # 位图矢量化 vectors=True, # 原生 PDF 矢量 stack=True, # 多页纵向堆叠 ocr_cache=".ocr_cache", image_opts={"line_art": True}, # 扫描线稿:笔画中心线追踪 text_color=True, # 文字真彩色(默认开) ) ``` 分步使用: ```python import pymupdf from pdf2dwg import get_engine, build_dxf, render_page doc = pymupdf.open("图纸.pdf") engine = get_engine("rapid") pages_ocr = {i: engine.run_image(render_page(doc, i, 200), i + 1, 200) for i in range(doc.page_count)} build_dxf("图纸.pdf", "图纸.dxf", pages_ocr=pages_ocr) ``` ## 输出图层 | 图层 | 内容 | |------|------| | `TEXT` / `TEXT-TITLE` | OCR 文字(正文 / 标题),样式 `HZ`(默认 `simsun.ttc`),带真彩色 | | `GRAPHICS` / `GRAPHICS-FILL` | PDF 原生矢量(描边 / 填充),真彩色 | | `IMAGES` | 位图矢量化结果(真彩色);线稿模式下带 `const_width` 笔画粗细 | | `PAGE` | 页面边框与页码 | ## 说明 - 坐标单位与 PDF 一致(points,1 pt = 1/72 inch)。 - 输出为 **R2004** DXF:更早的版本无法写出真彩色(420 组码),颜色会全部丢失。 - **线稿可切换为笔画中心线追踪**:`--image-line-art on` 跳过"填充区域"模型,直接追踪笔画的 中轴线,适合扫描线稿。9 页实测(`text_mode="none"`,`--image-max-dim 4000`): | 模式 | ink_iou | ink_recall | ink_colour_acc | 实体数 | |------|---------|-----------|----------------|--------| | `legacy`(默认,区域填充) | 0.267 | 0.613 | 86.57% | 716 | | `rings`(HATCH 带洞) | 0.284 | 0.516 | 90.19% | 525 | | **line-art** | **0.337** | **0.701** | 89.03% | 2,855 | 代价是实体数约 4 倍。照片会明显变糟,且它会把文字也描一遍(OCR 实体已经画过),所以 **默认关闭**;`--auto-tune` 会把它当候选实测后再决定要不要用。**测量时请用 `text_mode="none"`**——用 `"strokes"` 评分会让线稿因重复画文字而虚高。 - 默认 `--vector-mode legacy`(平铺 LWPOLYLINE)。`rings` 用 HATCH 带洞,IoU 略高、彩色更准, 但**召回更低**(上表 0.516 对 0.613:HATCH 逐环奇偶填充更准,平铺多段线依赖绘制顺序), 而且 **LibreDWG 无法承载 HATCH**:653 个 HATCH 的图会报 653 条 `Skip HATCH common handles`, 多边形一条都没有。所以只有需要 DWG 输出、且更在意洞的正确性时,才值得切到 rings。 - 中文文字样式默认字体 `simsun.ttc`,可在 `build_dxf(..., text_font=...)` 中修改; 若 CAD 中字体缺失,替换为本地已安装的中文字体即可。 - **线稿带笔画粗细**:DXF 组码 43(`const_width`)。取源图墨迹掩膜的距离变换,在每段折线 中点处读出局部线宽。页内线宽变化可达 3 倍(p10 1.38pt ~ p90 4.13pt),所以这是真实信息而非常数。 在实体数完全不变的前提下,9 页实测(`--image-max-dim 2400`)IoU 0.281 → 0.323、 召回 0.611 → 0.674、彩色一致率 88.37% → 89.19%。`--no-line-width` 可关闭。区域填充不给宽度:闭合环已围住自己的面积, 再加宽度会重复计算。 - **文字也带真彩色**:从原页面在每个 OCR 文字框内采样墨色写入实体,所以蓝色标题不会变黑。 每个框按**自身纸张底色**取阈值(取比纸色暗 60% 的像素中位数),因此灰度扫描件和有色纸都能用。 9 页实测彩色一致率 86.19% → 88.53%,`rgb_mae` 24.43 → 21.52。`--no-text-color` 可关闭。 - 位图矢量化对线条图效果最佳;照片类图片会产生较多多边形,可用 `--image-min-area` / `--image-max-polys` / `--no-images` 控制。 - **灰阶抗锯齿归并**(默认开启,`merge_neutral`):~200 DPI 扫描件里的 0.25 pt 线只有一个 抗锯齿像素,一条黑线会散成 (121,121,121)…(216,216,216) 一整条灰阶,每一级都变成独立的 填充,最浅那级在白纸上基本看不见。现在把**严格中性灰**(max-min ≤ 12)归并到最深的一档, 并合并标签消除重复环。只处理中性灰是有意为之:封面的米色 (200,197,185) 通道差为 15、 蓝色 logo 根本不是中性色,都会被保留。实测墨迹召回 70.1% → 82.0%,实体数 −19.8%。 用 `vectorize_image(..., merge_neutral=False)` 可关闭。 - 合并后的中性灰掩膜还可再做 **3×3 形态学闭运算**(`neutral_close`,**默认 0,关闭**)。 合并会交错灰阶各级、掩膜边界呈阶梯状,闭运算确实能收拾掉冗余环:9 页实测召回 82.0% → 84.2%、实体数 −9.4%。但它会让笔画变粗,而**变粗的笔画既吃掉更多墨迹、又不再细长**, 实际发布的线条模式因此反而变差(召回 72.9% → 68.3%),只换来约 2% 的体积。所以默认关闭; 要用请同时重测两种模式。 - `--image-max-dim` 是体积与保真的主要开关,**两种模式的合适值相差很大**: - **区域填充默认 600**。绝大多数内嵌图实际放置尺寸远小于其像素尺寸,600 已足够; 调到 1000 换不到 0.005 IoU 却让实体数涨 22~30%。只有当某页把扫描图**放大显示**时 才值得提高。 - **线稿默认 4000**(`cli.LINE_ART_MAX_DIM`),有两个独立的实测理由:其一,骨架化会删掉 窄于约 2px 的笔画;其二,**降采样后的光栅会报出偏大的线宽**——用真实裁图配对测量, 未降采样时线宽中位误差约 0%,一旦真降采样则 753px 的图在 600 时偏大 25%、400 时偏大 88% (距离变换不再跨越真实笔画,这个误差无法靠缩放系数补回)。4000 能让参考图纸全部 214 张 内嵌图都不降采样。 - 质量度量工具。**`ink_iou` / `pct_bad_px` 是灰度的,对颜色完全失明**——把整张图压成 单一墨色也能拿高 IoU。所以务必同时看 `ink_colour_acc` 与 `rgb_mae`: 同理,`ink_recall` 看的是"源图墨迹被复现了多少",IoU 上升时覆盖率可能反而在下降。 ```bash python tools/compare_modes.py 图纸.pdf --pages 1-5 --dpi 100 # legacy / rings / line-art python tools/skeleton_trace.py 图纸.pdf --page 20 # 逐图预览线稿追踪 python tools/auto_tune.py 图纸.pdf --pages 1-61 # 逐文档评估追踪参数 ``` - `--auto-tune N`(实验性)会抽样 N 页评估追踪参数并采纳胜出者。它只在超过保守阈值时 才改变配置;在参考图纸上它的建议在**未抽样页上反而更差**(颜色 −1.2 点、实体 +4.6%, 仅换 +0.005 IoU),因此默认值被保留。建议先用 `tools/auto_tune.py` 诊断再决定。 ## 开发 ```bash pip install -e ".[dev,rapid]" bash tools/check.sh # ruff + pytest(就是 CI 跑的那套) bash tools/check.sh --full # 再加 build + twine check,发版前用 ``` 单独跑:`python -m pytest`(82 个测试,无需网络/样本)、`python -m ruff check src tests tools` (硬闸门,`pyproject.toml` 只选真 bug 规则 `E9,F63,F7,F82,F401,F841`;更宽的规则集有约 136 条 历史风格问题,属建议性质)、`python -m build`。 ## 许可 MIT