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关闭。
安装
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 的 dxf2dwg,
或设置环境变量 PDF2DWG_DXF2DWG=/path/to/dxf2dwg。
命令行
# 默认: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
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, # 文字真彩色(默认开)
)
分步使用:
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_mae24.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 上升时覆盖率可能反而在下降。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诊断再决定。
开发
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
Metadata
Release files for atlisp-pdf2dwg 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| atlisp_pdf2dwg-0.1.2.tar.gz | 80.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| atlisp_pdf2dwg-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 139.9 kB
Release files / atlisp_pdf2dwg-0.1.2.tar.gz
| Download URL | atlisp_pdf2dwg-0.1.2.tar.gz |
|---|---|
| Size | 80.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4d4ed42b363bccc921d4e48cae6460b0cccf145d34294d442e6e7100d003e0f4
|
|
BLAKE2b-256 checksum How to use checksums |
ecc1544f018bd16b384e2b8c32fbeded36e57d4579e0c4b93d7846406fca9a25
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / atlisp_pdf2dwg-0.1.2-py3-none-any.whl
| Download URL | atlisp_pdf2dwg-0.1.2-py3-none-any.whl |
|---|---|
| Size | 59.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
af349b1a9558e272ef63d6395a5e5a72ee2813600ed5f0cdf453f007c53d84d6
|
|
BLAKE2b-256 checksum How to use checksums |
c339f598ba77c2172ce0219a0f8d2aa50fc3b08b83c2b17fb49d30e92e02690a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|