Metadata-Version: 2.1
Name: cad2image
Version: 0.1.1
Summary: DWG/DXF → PNG/SVG 渲染管线，用 ODA File Converter + ezdxf 替换 Acme CAD Converter
License: MIT
Project-URL: Homepage, https://github.com/zhy201810576/cad2image
Project-URL: Repository, https://github.com/zhy201810576/cad2image
Project-URL: Issues, https://github.com/zhy201810576/cad2image/issues
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: ezdxf <1.2,>=1.1.3
Requires-Dist: PyMuPDF <1.25,>=1.23
Requires-Dist: numpy >=1.22
Requires-Dist: typer >=0.9
Provides-Extra: dev
Requires-Dist: pytest >=7.0 ; extra == 'dev'
Requires-Dist: ruff >=0.1 ; extra == 'dev'
Requires-Dist: mypy >=1.0 ; extra == 'dev'
Requires-Dist: Pillow >=9.0 ; extra == 'dev'
Requires-Dist: fonttools >=4.30 ; extra == 'dev'

# cad2image · 让 CAD 图纸渲染告别残余杂线

[![PyPI version](https://img.shields.io/pypi/v/cad2image.svg)](https://pypi.org/project/cad2image/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)

`cad2image` 是一个把 **DWG / DXF 图纸渲染成 PNG / SVG** 的命令行工具与 Python 库。它用「ODA File Converter + ezdxf」重建渲染管线，替换 Acme CAD Converter，并消除其底层 GDI 栅格化产生的「残余杂线」——圆弧走真圆弧，无多边形折痕与毛须。

## 核心功能

- **高保真渲染**：DWG/DXF → PNG（任意 DPI）或 SVG（矢量），圆弧渲染为真圆弧。
- **告别残余杂线**：替换 Acme 的 GDI 栅格化路径，无毛须、无折痕。
- **中文开箱即用**：内置开源中文字体，自动替换图纸里引用的 SimSun / 宋体等专有字体。
- **完整打印样式**：支持 CTB 打印样式表、线宽控制、颜色 / 灰度 / 单色输出。
- **批量处理**：目录递归处理，逐条报告成功/失败，不因单个失败中断整批。
- **双形态**：既可用作命令行工具，也可作为 Python 库嵌入。

## 安装

要求 Python 3.8+。

```bash
# 从 PyPI 安装（推荐）
pip install cad2image
```

> 将 **DWG 转为 DXF** 需要另外安装免费的 ODA File Converter（https://www.opendesign.com/guestfiles/oda_file_converter）。若只渲染已有的 DXF，则无需安装。

ODA 可执行文件路径解析优先级：

1. 环境变量 `ODA_FILE_CONVERTER_PATH`
2. 默认路径 `D:\ODA\ODAFileConverter_title 21.5.0\ODAFileConverter.exe`

## 快速开始

安装后，使用控制台命令 `cad2image`（也可用 `python -m cad2image`）：

```bash
# 单个 DWG → PNG（默认 300 DPI）
cad2image 轴套.dwg -o 轴套.png

# 高清 PNG
cad2image 轴套.dwg -o 轴套.png --dpi 1200

# DWG → SVG（矢量）
cad2image 轴套.dwg -o 轴套.svg

# 直接渲染已有的 DXF（跳过 ODA）
cad2image 轴套.dxf -o 轴套.png

# 批量处理目录（递归子目录）
cad2image "CAD Test/" -o out/ --recursive
```

## 常用场景

```bash
# 白底 / 黑底 / 透明底
cad2image 图.dwg -o 图.png --background white
cad2image 图.dwg -o 图.png --background black
cad2image 图.dwg -o 图.png --background off

# 应用 CTB 打印样式表（如黑白线型）
cad2image 图.dwg -o 图.png --ctb 黑白线型.ctb

# 按内容自适应页面，四周留 5% 余量
cad2image 图.dxf -o 图.png --fit --margin 5.0

# 单色 / 灰度输出
cad2image 图.dwg -o 图.png --color monochrome
cad2image 图.dwg -o 图.png --color grayscale

# 指定页面尺寸（mm）
cad2image 图.dwg -o 图.png --width 420 --height 297
```

## 命令行参数

| 参数 | 说明 | 默认 |
|---|---|---|
| `--output, -o` | 输出文件/目录 | 与输入同目录 |
| `--dpi` | PNG 分辨率 | 300 |
| `--format, -f` | 输出格式 `png` / `svg` | png |
| `--background` | `default` / `white` / `black` / `off` | white |
| `--color` | `color` / `monochrome` / `grayscale` / `black` / `white` | color |
| `--lineweight` | `absolute` / `relative` | absolute |
| `--lineweight-scaling` | 线宽整体缩放系数（仅绝对线宽生效） | 1.0 |
| `--min-lineweight` | 最小打印线宽（mm） | 无 |
| `--relative-max-stroke-width` | 相对线宽：最粗线宽占页面较小边比例 | 0.001（0.1%） |
| `--relative-min-stroke-width` | 相对线宽：最细线宽占最粗线宽比例 | 0.05（5%） |
| `--ctb` | CTB 打印样式表路径 | 无 |
| `--font-dir` | 附加字体目录（SHX / TTF / OTF） | 无 |
| `--layout` | 布局名（缺省模型空间） | 模型空间 |
| `--width` / `--height` | 页面尺寸（mm） | 自适应 |
| `--fit` | 按内容包围盒自适应页面 | False |
| `--margin` | 内容自适应页面时的四周余量（%，相对内容较小边） | 3.0 |
| `--recursive` | 目录批量时递归子目录 | False |
| `--oda-path` | ODA 可执行文件路径 | 环境变量/默认路径 |

退出码：`0` 成功；`1` 转换或渲染失败（批量时存在任一失败即非零）。

## 从 Acme CAD Converter 迁移

| Acme 参数 | 本工具映射 |
|---|---|
| `/res N` | `--dpi N` |
| `/w` `/h`（mm） | `--width` / `--height` |
| `/e` `/ad`（缩放扩展） | `--fit`（包围盒自适应） |
| `/b` 背景色 | `--background` |
| `/lw 0/1/2` | `--lineweight` + `--lineweight-scaling` |
| `/p 1/2/3`（1bit/灰度/256色） | `--color`（灰度暂以 monochrome 近似，待 Phase 2） |
| `/pw myset` | `plotstyle.load_ctb`（尚未实现，见已知限制） |
| `/a 0/-1/-2`（布局选择） | `--layout` |
| `/l` 报告 | 批处理汇总 + 退出码 |

## 字体与中文

中文与 ASCII 标注**开箱即用**——包内已内置开源字体（SIL OFL 1.1），无需额外配置：

| 字体文件 | 用途 |
|---|---|
| `NotoSansSC-Regular.otf` | 中文（含拉丁字符） |
| `NotoSansMono-Regular.ttf` | ASCII 等宽，近似 CAD 单线字体 |

CAD 图纸常引用专有字体（微软 `SimSun`/`NSimSun`、Autodesk `romans.shx`/`txt.shx` 等），渲染时会**自动重写为内置开源字体**：

- `SimSun` / `NSimSun` / `宋体` / 中文 bigfont → `NotoSansSC-Regular.otf`
- 其余 SHX 字形字体 → `NotoSansMono-Regular.ttf`

> 替换会改变文字外观，但保证纯开源、无再分发风险。若需严格保留原字体观感，可自行将对应字体放入 `fonts/`，或用 `--font-dir` 指定附加字体目录。

完整许可与署名见 `src/cad2image/fonts/OFL.txt` 与 `NOTICE`。

## 已知限制

- **灰度输出**（Acme `/p 2`）：暂以 monochrome 近似，真正的 256 级灰度需像素级后处理。
- **3D 消隐**（Acme `/hide`）：未实现，假设输入为 2D 工程图。
- **Xref 外部引用**：转换前需确保 xref 文件与主图同目录（与 Acme 行为一致）。
- **页面单位**：模型空间包围盒自适应时假定绘图单位为 mm（`$INSUNITS` 未做换算）。
- **SHX 中文 bigfont**：ezdxf 1.1.x 不支持 bigfont 中文大字体，引用这类字体的图纸会被自动重写为 Noto Sans SC（见「字体与中文」）。

## License

[MIT](./LICENSE)

## 开发者指南

### Python API

除 CLI 外，也可作为 Python 库导入，提供三层能力：**渲染**（DXF→PNG/SVG）、**转换**（DWG→DXF）、**批处理**。仅 DWG→DXF 转换需要 ODA File Converter，纯 DXF 渲染不需要。

```python
from cad2image import RenderOptions, render_dxf, process_dwg, process_directory

# 渲染 DXF → PNG/SVG（按输出扩展名自动选后端）
render_dxf("轴套.dxf", "轴套.png", RenderOptions(dpi=300))
render_dxf("轴套.dxf", "轴套.svg", RenderOptions())

# DWG → 图片（端到端）
out = process_dwg("轴套.dwg", "out/", RenderOptions(dpi=600))  # 返回 Path

# 分步转换（先转 DXF 再渲染，便于缓存）
from cad2image import convert_dwg_to_dxf, render_to_svg
dxf = convert_dwg_to_dxf("轴套.dwg", "dxf_cache/")
render_to_svg(dxf, "轴套.svg", RenderOptions())

# 批量处理目录
result = process_directory("CAD Test/", "out/", RenderOptions(),
                           output_format="png", recursive=True)
print(f"成功 {result.success_count}，失败 {result.failure_count}")

# CTB 打印样式
from cad2image import load_ctb, get_lineweight
styles = load_ctb("黑白线型.ctb")
lw = get_lineweight(styles, aci=1)  # 颜色索引 1 的线宽(mm)，未覆盖时为 None
render_dxf("图.dxf", "图.png", RenderOptions(ctb="黑白线型.ctb"))
```

`RenderOptions` 常用参数：

```python
RenderOptions(
    dpi=300,                          # PNG 分辨率
    background="white",               # default/white/black/off
    color_policy="color",             # color/monochrome/grayscale/black/white
    lineweight_policy="absolute",     # absolute/relative
    lineweight_scaling=1.0,           # 线宽整体缩放（仅绝对线宽）
    min_lineweight=None,              # 最小打印线宽(mm)
    relative_max_stroke_width=0.001,  # 相对线宽最粗比例
    relative_min_stroke_width=0.05,   # 相对线宽最细比例
    ctb="",                           # CTB 样式表路径
    font_dir="",                      # 附加字体目录（SHX/TTF/OTF）
    layout_name=None,                 # 布局名，None=模型空间
    width_mm=None, height_mm=None,    # 显式页面尺寸(mm)
    fit_to_extents=False,             # 按内容包围盒自适应
    margin=3.0,                       # 自适应时四周余量(%)
)
```

错误统一抛 `FileNotFoundError` / `ValueError` / `RuntimeError`，可放心捕获。

### 项目结构

```
src/cad2image/
├── cli.py         # typer CLI 入口
├── dwg2dxf.py     # ODA File Converter 封装（目录级转换编排）
├── render.py      # ezdxf → PNG/SVG 渲染核心
├── config.py      # 参数 → ezdxf Configuration 映射
├── plotstyle.py   # CTB 打印样式（占位）
└── batch.py       # 批量 + 部分失败结果对象
```

### 测试

```bash
pip install -e ".[dev]"    # 开发依赖（pytest / ruff / mypy / Pillow / fonttools）

pytest                     # 单元测试（无外部依赖）
pytest -m integration      # 集成测试（需真实 DWG + ODA）
```

集成测试通过环境变量提供真实图纸：

```bash
CAD_TEST_DWG=E:/path/to/轴套.dwg pytest tests/test_integration.py -m integration
```

### 打包发布

```bash
# 构建 wheel + sdist
python -m build

# 上传 PyPI
python -m twine upload dist/*
```

产物 `dist/cad2image-0.1.0-py3-none-any.whl` 自带开源字体（Noto Sans SC / Noto Sans Mono）与 `py.typed`。

### 质量门禁

```bash
ruff check .
mypy src/
pytest
```
