Skip to main content

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_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 上升时覆盖率可能反而在下降。

    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)

Source distribution for atlisp-pdf2dwg 0.1.2
File Size Uploaded
atlisp_pdf2dwg-0.1.2.tar.gz 80.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for atlisp-pdf2dwg 0.1.2
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page