cad2image 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cad2image-0.1.0/LICENSE +21 -0
- cad2image-0.1.0/PKG-INFO +287 -0
- cad2image-0.1.0/README.md +256 -0
- cad2image-0.1.0/pyproject.toml +77 -0
- cad2image-0.1.0/setup.cfg +4 -0
- cad2image-0.1.0/src/cad2image/__init__.py +34 -0
- cad2image-0.1.0/src/cad2image/__main__.py +6 -0
- cad2image-0.1.0/src/cad2image/batch.py +158 -0
- cad2image-0.1.0/src/cad2image/cli.py +152 -0
- cad2image-0.1.0/src/cad2image/config.py +164 -0
- cad2image-0.1.0/src/cad2image/dwg2dxf.py +175 -0
- cad2image-0.1.0/src/cad2image/fonts/NOTICE +6 -0
- cad2image-0.1.0/src/cad2image/fonts/NotoSansMono-Regular.ttf +0 -0
- cad2image-0.1.0/src/cad2image/fonts/NotoSansSC-Regular.otf +0 -0
- cad2image-0.1.0/src/cad2image/fonts/OFL.txt +93 -0
- cad2image-0.1.0/src/cad2image/plotstyle.py +52 -0
- cad2image-0.1.0/src/cad2image/py.typed +0 -0
- cad2image-0.1.0/src/cad2image/render.py +358 -0
- cad2image-0.1.0/src/cad2image.egg-info/PKG-INFO +287 -0
- cad2image-0.1.0/src/cad2image.egg-info/SOURCES.txt +29 -0
- cad2image-0.1.0/src/cad2image.egg-info/dependency_links.txt +1 -0
- cad2image-0.1.0/src/cad2image.egg-info/entry_points.txt +2 -0
- cad2image-0.1.0/src/cad2image.egg-info/requires.txt +11 -0
- cad2image-0.1.0/src/cad2image.egg-info/top_level.txt +1 -0
- cad2image-0.1.0/tests/test_batch.py +86 -0
- cad2image-0.1.0/tests/test_cli.py +31 -0
- cad2image-0.1.0/tests/test_config.py +80 -0
- cad2image-0.1.0/tests/test_dwg2dxf.py +73 -0
- cad2image-0.1.0/tests/test_integration.py +37 -0
- cad2image-0.1.0/tests/test_plotstyle.py +51 -0
- cad2image-0.1.0/tests/test_render.py +185 -0
cad2image-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 zhy201810576
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cad2image-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: cad2image
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: DWG/DXF → PNG/SVG 渲染管线,用 ODA File Converter + ezdxf 替换 Acme CAD Converter
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/zhy201810576/cad2image
|
|
7
|
+
Project-URL: Repository, https://github.com/zhy201810576/cad2image
|
|
8
|
+
Project-URL: Issues, https://github.com/zhy201810576/cad2image/issues
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: ezdxf<1.2,>=1.1.3
|
|
22
|
+
Requires-Dist: PyMuPDF<1.25,>=1.23
|
|
23
|
+
Requires-Dist: numpy>=1.22
|
|
24
|
+
Requires-Dist: typer>=0.9
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
27
|
+
Requires-Dist: ruff>=0.1; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
29
|
+
Requires-Dist: Pillow>=9.0; extra == "dev"
|
|
30
|
+
Requires-Dist: fonttools>=4.30; extra == "dev"
|
|
31
|
+
|
|
32
|
+
# cad2image · 让 CAD 图纸渲染告别残余杂线
|
|
33
|
+
|
|
34
|
+
[](https://pypi.org/project/cad2image/)
|
|
35
|
+
[](./LICENSE)
|
|
36
|
+
|
|
37
|
+
用「ODA File Converter + ezdxf」重建 DWG/DXF → PNG/SVG 渲染管线,替换 Acme CAD Converter。
|
|
38
|
+
|
|
39
|
+
消除 Acme 底层 GDI 栅格化路径产生的「残余杂线」问题——圆弧走真圆弧,无多边形折痕与毛须。
|
|
40
|
+
|
|
41
|
+
## 数据流
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
DWG ──(ODA File Converter, DWG→DXF)──▶ DXF ──(ezdxf.addons.drawing)──▶ PNG / SVG
|
|
45
|
+
▲ ▲
|
|
46
|
+
稳定、可读文本 PyMuPDF 后端(无 GDI bug)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- ODA File Converter 负责解析 DWG 二进制 → 标准 ASCII DXF(与 Acme 同 Teigha 内核)。
|
|
50
|
+
- 其余读取、渲染、样式、批处理全部在 Python 内完成。
|
|
51
|
+
|
|
52
|
+
## 安装
|
|
53
|
+
|
|
54
|
+
要求 Python 3.8+。
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# 从 PyPI 安装(推荐)
|
|
58
|
+
pip install cad2image
|
|
59
|
+
|
|
60
|
+
# 开发安装(含 pytest / ruff / mypy 等)
|
|
61
|
+
pip install -e ".[dev]"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
需要单独安装 ODA File Converter(免费,https://www.opendesign.com/guestfiles/oda_file_converter)。
|
|
65
|
+
|
|
66
|
+
ODA 可执行文件路径解析优先级:
|
|
67
|
+
|
|
68
|
+
1. 环境变量 `ODA_FILE_CONVERTER_PATH`
|
|
69
|
+
2. 默认路径 `D:\ODA\ODAFileConverter_title 21.5.0\ODAFileConverter.exe`
|
|
70
|
+
|
|
71
|
+
## 快速开始
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# 单个 DWG → PNG
|
|
75
|
+
python -m cad2image 轴套.dwg -o 轴套.png --dpi 1200
|
|
76
|
+
|
|
77
|
+
# 单个 DWG → SVG
|
|
78
|
+
python -m cad2image 轴套.dwg -o 轴套.svg
|
|
79
|
+
|
|
80
|
+
# 直接渲染已转好的 DXF(跳过 ODA)
|
|
81
|
+
python -m cad2image 轴套.dxf -o 轴套.png
|
|
82
|
+
|
|
83
|
+
# 批量处理目录
|
|
84
|
+
python -m cad2image "CAD Test/" -o out/ --recursive
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
安装后也可用控制台命令 `cad2image`。
|
|
88
|
+
|
|
89
|
+
## CLI 参数
|
|
90
|
+
|
|
91
|
+
| 参数 | 说明 | 默认 |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `--output, -o` | 输出文件/目录 | 与输入同目录 |
|
|
94
|
+
| `--dpi` | PNG 分辨率 | 300 |
|
|
95
|
+
| `--format, -f` | 输出格式 `png` / `svg` | png |
|
|
96
|
+
| `--background` | `default` / `white` / `black` / `off` | white |
|
|
97
|
+
| `--color` | `color` / `monochrome` / `grayscale` / `black` / `white` | color |
|
|
98
|
+
| `--lineweight` | `absolute` / `relative` | absolute |
|
|
99
|
+
| `--lineweight-scaling` | 线宽整体缩放系数(仅绝对线宽生效) | 1.0 |
|
|
100
|
+
| `--min-lineweight` | 最小打印线宽(mm) | 无 |
|
|
101
|
+
| `--relative-max-stroke-width` | 相对线宽:最粗线宽占页面较小边比例 | 0.001(0.1%) |
|
|
102
|
+
| `--relative-min-stroke-width` | 相对线宽:最细线宽占最粗线宽比例 | 0.05(5%) |
|
|
103
|
+
| `--ctb` | CTB 打印样式表路径 | 无 |
|
|
104
|
+
| `--font-dir` | 附加的 SHX/TTF 字体目录 | 无 |
|
|
105
|
+
| `--layout` | 布局名(缺省模型空间) | 模型空间 |
|
|
106
|
+
| `--width` / `--height` | 页面尺寸(mm) | 自适应 |
|
|
107
|
+
| `--fit` | 按内容包围盒自适应页面 | False |
|
|
108
|
+
| `--margin` | 内容自适应页面时的四周余量(%,相对内容较小边) | 3.0 |
|
|
109
|
+
| `--recursive` | 目录批量时递归子目录 | False |
|
|
110
|
+
| `--oda-path` | ODA 可执行文件路径 | 环境变量/默认路径 |
|
|
111
|
+
|
|
112
|
+
退出码:`0` 成功;`1` 转换或渲染失败(批量时存在任一失败即非零)。
|
|
113
|
+
|
|
114
|
+
## Python API 使用
|
|
115
|
+
|
|
116
|
+
除 CLI 外,也可作为 Python 库导入,提供三层能力:**渲染**(DXF→PNG/SVG)、**转换**(DWG→DXF)、**批处理**。
|
|
117
|
+
|
|
118
|
+
> 前置条件:仅 DWG→DXF 转换需要 ODA File Converter;纯 DXF 渲染不需要。
|
|
119
|
+
|
|
120
|
+
### 渲染 DXF → PNG/SVG
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from cad2image import RenderOptions, render_dxf
|
|
124
|
+
|
|
125
|
+
# 按输出扩展名自动选 PNG/SVG 后端
|
|
126
|
+
render_dxf("轴套.dxf", "轴套.png", RenderOptions(dpi=300))
|
|
127
|
+
render_dxf("轴套.dxf", "轴套.svg", RenderOptions())
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### DWG → 图片(端到端)
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from cad2image import RenderOptions, process_dwg
|
|
134
|
+
|
|
135
|
+
out = process_dwg("轴套.dwg", "out/", RenderOptions(dpi=600)) # 返回 Path
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 分步转换(先转 DXF 再渲染,便于缓存)
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
from cad2image import convert_dwg_to_dxf, render_to_svg
|
|
142
|
+
|
|
143
|
+
dxf = convert_dwg_to_dxf("轴套.dwg", "dxf_cache/") # 返回 Path
|
|
144
|
+
render_to_svg(dxf, "轴套.svg", RenderOptions())
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 批量处理目录
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from cad2image import RenderOptions, process_directory
|
|
151
|
+
|
|
152
|
+
result = process_directory("CAD Test/", "out/", RenderOptions(),
|
|
153
|
+
output_format="png", recursive=True)
|
|
154
|
+
print(f"成功 {result.success_count},失败 {result.failure_count}")
|
|
155
|
+
for f in result.failures:
|
|
156
|
+
print(f"[{f.stage}] {f.source.name}: {f.error}") # stage: convert / render
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### CTB 打印样式
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from cad2image import RenderOptions, load_ctb, get_lineweight, render_dxf
|
|
163
|
+
|
|
164
|
+
styles = load_ctb("黑白线型.ctb")
|
|
165
|
+
lw = get_lineweight(styles, aci=1) # 颜色索引 1 的线宽(mm),未覆盖时为 None
|
|
166
|
+
|
|
167
|
+
render_dxf("图.dxf", "图.png", RenderOptions(ctb="黑白线型.ctb"))
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### RenderOptions 常用参数
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
RenderOptions(
|
|
174
|
+
dpi=300, # PNG 分辨率
|
|
175
|
+
background="white", # default/white/black/off
|
|
176
|
+
color_policy="color", # color/monochrome/grayscale/black/white
|
|
177
|
+
lineweight_policy="absolute", # absolute/relative
|
|
178
|
+
lineweight_scaling=1.0, # 线宽整体缩放(仅绝对线宽)
|
|
179
|
+
min_lineweight=None, # 最小打印线宽(mm)
|
|
180
|
+
relative_max_stroke_width=0.001, # 相对线宽最粗比例
|
|
181
|
+
relative_min_stroke_width=0.05, # 相对线宽最细比例
|
|
182
|
+
ctb="", # CTB 样式表路径
|
|
183
|
+
font_dir="", # 附加字体目录(SHX/TTF)
|
|
184
|
+
layout_name=None, # 布局名,None=模型空间
|
|
185
|
+
width_mm=None, height_mm=None, # 显式页面尺寸(mm)
|
|
186
|
+
fit_to_extents=False, # 按内容包围盒自适应
|
|
187
|
+
margin=3.0, # 自适应时四周余量(%)
|
|
188
|
+
)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
错误统一抛 `FileNotFoundError` / `ValueError` / `RuntimeError`,可放心捕获;中文与 ASCII 标注随包内置开源字体(见下文「字体与中文」)。
|
|
192
|
+
|
|
193
|
+
## Acme 参数映射
|
|
194
|
+
|
|
195
|
+
| Acme 参数 | 本工具映射 |
|
|
196
|
+
|---|---|
|
|
197
|
+
| `/res N` | `--dpi N` |
|
|
198
|
+
| `/w` `/h`(mm) | `--width` / `--height` |
|
|
199
|
+
| `/e` `/ad`(缩放扩展) | `--fit`(包围盒自适应) |
|
|
200
|
+
| `/b` 背景色 | `--background` |
|
|
201
|
+
| `/lw 0/1/2` | `--lineweight` + `--lineweight-scaling` |
|
|
202
|
+
| `/p 1/2/3`(1bit/灰度/256色) | `--color`(灰度暂以 monochrome 近似,待 Phase 2) |
|
|
203
|
+
| `/pw myset` | `plotstyle.load_ctb`(尚未实现,见已知限制) |
|
|
204
|
+
| `/a 0/-1/-2`(布局选择) | `--layout` |
|
|
205
|
+
| `/l` 报告 | 批处理汇总 + 退出码 |
|
|
206
|
+
|
|
207
|
+
## 项目结构
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
src/cad2image/
|
|
211
|
+
├── cli.py # typer CLI 入口
|
|
212
|
+
├── dwg2dxf.py # ODA File Converter 封装(目录级转换编排)
|
|
213
|
+
├── render.py # ezdxf → PNG/SVG 渲染核心
|
|
214
|
+
├── config.py # 参数 → ezdxf Configuration 映射
|
|
215
|
+
├── plotstyle.py # CTB 打印样式(占位)
|
|
216
|
+
└── batch.py # 批量 + 部分失败结果对象
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## 测试
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
pytest # 单元测试(无外部依赖)
|
|
223
|
+
pytest -m integration # 集成测试(需真实 DWG + ODA)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
集成测试通过环境变量提供真实图纸:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
CAD_TEST_DWG=E:/path/to/轴套.dwg pytest tests/test_integration.py -m integration
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## 打包发布
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# 构建 wheel(本机需加 --no-build-isolation 规避构建隔离环境联网拉 setuptools 的 SSL 劫持)
|
|
236
|
+
python -m pip wheel . --no-deps --no-build-isolation -w dist
|
|
237
|
+
|
|
238
|
+
# 构建 sdist(可选)
|
|
239
|
+
python -c "import setuptools.build_meta as b; b.build_sdist('dist')"
|
|
240
|
+
|
|
241
|
+
# 安装到干净环境并验证中文渲染
|
|
242
|
+
python -m pip install dist/cad2image-0.1.0-py3-none-any.whl
|
|
243
|
+
python -m cad2image 轴套.dwg -o 轴套.png
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
产物 `dist/cad2image-0.1.0-py3-none-any.whl` 自带开源字体(Noto Sans SC / Noto Sans Mono)与 `py.typed`,无 `--no-build-isolation` 需要时可用标准 `python -m build` 生成 wheel + sdist。
|
|
247
|
+
|
|
248
|
+
## 质量门禁
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
ruff check .
|
|
252
|
+
ruff format .
|
|
253
|
+
mypy src/
|
|
254
|
+
pytest
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## 字体与中文
|
|
258
|
+
|
|
259
|
+
渲染时**默认自动扫描**随包内置的 `fonts/` 目录,也可通过 `--font-dir` 追加其它目录。内置字体均为**开源字体(SIL OFL 1.1)**,随仓库与 wheel 一起分发,中文与 ASCII 渲染开箱即用:
|
|
260
|
+
|
|
261
|
+
| 字体文件 | 用途 | 许可 |
|
|
262
|
+
|---|---|---|
|
|
263
|
+
| `NotoSansSC-Regular.otf` | 中文(含拉丁字符) | SIL OFL 1.1 |
|
|
264
|
+
| `NotoSansMono-Regular.ttf` | ASCII 等宽,近似 CAD 单线字体 | SIL OFL 1.1 |
|
|
265
|
+
|
|
266
|
+
> 完整许可与署名见 `src/cad2image/fonts/OFL.txt` 与 `NOTICE`。
|
|
267
|
+
|
|
268
|
+
CAD 图纸的文字样式常引用专有字体(微软 `SimSun`/`NSimSun`、Autodesk `romans.shx`/`txt.shx` 等)。渲染时会自动把这些字体名**重写为内置开源字体**:
|
|
269
|
+
|
|
270
|
+
- `SimSun` / `NSimSun` / `宋体` / `新宋体` / 中文 bigfont(`hztxt` / `gbcbig` 等)→ `NotoSansSC-Regular.otf`
|
|
271
|
+
- 其余 SHX 字形字体(`romans.shx` / `txt.shx` / `simplex` 等)→ `NotoSansMono-Regular.ttf`
|
|
272
|
+
|
|
273
|
+
> 替换会改变文字外观(SimSun → Noto Sans SC、SHX 单线体 → Noto Sans Mono 等宽),但保证纯开源、无专有字体再分发风险。若需严格保留原字体观感,可自行将对应字体放入 `fonts/` 或通过 `--font-dir` 指定。
|
|
274
|
+
|
|
275
|
+
**重要限制**:ezdxf 1.1.x 的 `ShapeFile` 解析器**不支持 bigfont 中文大字体**(`HZDX.SHX`/`gbcbig.shx` 等,源码中明确抛出 `UnsupportedShapeFile("BIGFONT shapes are not supported yet")`),因此本项目不内置 SHX 大字体;引用这类字体的图纸会被自动重写为 `NotoSansSC-Regular.otf`,中文可正常渲染。
|
|
276
|
+
|
|
277
|
+
## 已知限制
|
|
278
|
+
|
|
279
|
+
- **灰度输出**(Acme `/p 2`):暂以 monochrome 近似,真正的 256 级灰度需像素级后处理。
|
|
280
|
+
- **SHX 中文 bigfont**:ezdxf 1.1.x 不支持,中文走 TTF 字体(见上)。
|
|
281
|
+
- **3D 消隐**(Acme `/hide`):未实现,假设输入为 2D 工程图。
|
|
282
|
+
- **Xref 外部引用**:转换前需确保 xref 文件与主图同目录(与 Acme 行为一致)。
|
|
283
|
+
- **页面单位**:模型空间包围盒自适应时假定绘图单位为 mm(`$INSUNITS` 未做换算)。
|
|
284
|
+
|
|
285
|
+
## License
|
|
286
|
+
|
|
287
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# cad2image · 让 CAD 图纸渲染告别残余杂线
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/cad2image/)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
6
|
+
用「ODA File Converter + ezdxf」重建 DWG/DXF → PNG/SVG 渲染管线,替换 Acme CAD Converter。
|
|
7
|
+
|
|
8
|
+
消除 Acme 底层 GDI 栅格化路径产生的「残余杂线」问题——圆弧走真圆弧,无多边形折痕与毛须。
|
|
9
|
+
|
|
10
|
+
## 数据流
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
DWG ──(ODA File Converter, DWG→DXF)──▶ DXF ──(ezdxf.addons.drawing)──▶ PNG / SVG
|
|
14
|
+
▲ ▲
|
|
15
|
+
稳定、可读文本 PyMuPDF 后端(无 GDI bug)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- ODA File Converter 负责解析 DWG 二进制 → 标准 ASCII DXF(与 Acme 同 Teigha 内核)。
|
|
19
|
+
- 其余读取、渲染、样式、批处理全部在 Python 内完成。
|
|
20
|
+
|
|
21
|
+
## 安装
|
|
22
|
+
|
|
23
|
+
要求 Python 3.8+。
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# 从 PyPI 安装(推荐)
|
|
27
|
+
pip install cad2image
|
|
28
|
+
|
|
29
|
+
# 开发安装(含 pytest / ruff / mypy 等)
|
|
30
|
+
pip install -e ".[dev]"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
需要单独安装 ODA File Converter(免费,https://www.opendesign.com/guestfiles/oda_file_converter)。
|
|
34
|
+
|
|
35
|
+
ODA 可执行文件路径解析优先级:
|
|
36
|
+
|
|
37
|
+
1. 环境变量 `ODA_FILE_CONVERTER_PATH`
|
|
38
|
+
2. 默认路径 `D:\ODA\ODAFileConverter_title 21.5.0\ODAFileConverter.exe`
|
|
39
|
+
|
|
40
|
+
## 快速开始
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# 单个 DWG → PNG
|
|
44
|
+
python -m cad2image 轴套.dwg -o 轴套.png --dpi 1200
|
|
45
|
+
|
|
46
|
+
# 单个 DWG → SVG
|
|
47
|
+
python -m cad2image 轴套.dwg -o 轴套.svg
|
|
48
|
+
|
|
49
|
+
# 直接渲染已转好的 DXF(跳过 ODA)
|
|
50
|
+
python -m cad2image 轴套.dxf -o 轴套.png
|
|
51
|
+
|
|
52
|
+
# 批量处理目录
|
|
53
|
+
python -m cad2image "CAD Test/" -o out/ --recursive
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
安装后也可用控制台命令 `cad2image`。
|
|
57
|
+
|
|
58
|
+
## CLI 参数
|
|
59
|
+
|
|
60
|
+
| 参数 | 说明 | 默认 |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `--output, -o` | 输出文件/目录 | 与输入同目录 |
|
|
63
|
+
| `--dpi` | PNG 分辨率 | 300 |
|
|
64
|
+
| `--format, -f` | 输出格式 `png` / `svg` | png |
|
|
65
|
+
| `--background` | `default` / `white` / `black` / `off` | white |
|
|
66
|
+
| `--color` | `color` / `monochrome` / `grayscale` / `black` / `white` | color |
|
|
67
|
+
| `--lineweight` | `absolute` / `relative` | absolute |
|
|
68
|
+
| `--lineweight-scaling` | 线宽整体缩放系数(仅绝对线宽生效) | 1.0 |
|
|
69
|
+
| `--min-lineweight` | 最小打印线宽(mm) | 无 |
|
|
70
|
+
| `--relative-max-stroke-width` | 相对线宽:最粗线宽占页面较小边比例 | 0.001(0.1%) |
|
|
71
|
+
| `--relative-min-stroke-width` | 相对线宽:最细线宽占最粗线宽比例 | 0.05(5%) |
|
|
72
|
+
| `--ctb` | CTB 打印样式表路径 | 无 |
|
|
73
|
+
| `--font-dir` | 附加的 SHX/TTF 字体目录 | 无 |
|
|
74
|
+
| `--layout` | 布局名(缺省模型空间) | 模型空间 |
|
|
75
|
+
| `--width` / `--height` | 页面尺寸(mm) | 自适应 |
|
|
76
|
+
| `--fit` | 按内容包围盒自适应页面 | False |
|
|
77
|
+
| `--margin` | 内容自适应页面时的四周余量(%,相对内容较小边) | 3.0 |
|
|
78
|
+
| `--recursive` | 目录批量时递归子目录 | False |
|
|
79
|
+
| `--oda-path` | ODA 可执行文件路径 | 环境变量/默认路径 |
|
|
80
|
+
|
|
81
|
+
退出码:`0` 成功;`1` 转换或渲染失败(批量时存在任一失败即非零)。
|
|
82
|
+
|
|
83
|
+
## Python API 使用
|
|
84
|
+
|
|
85
|
+
除 CLI 外,也可作为 Python 库导入,提供三层能力:**渲染**(DXF→PNG/SVG)、**转换**(DWG→DXF)、**批处理**。
|
|
86
|
+
|
|
87
|
+
> 前置条件:仅 DWG→DXF 转换需要 ODA File Converter;纯 DXF 渲染不需要。
|
|
88
|
+
|
|
89
|
+
### 渲染 DXF → PNG/SVG
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from cad2image import RenderOptions, render_dxf
|
|
93
|
+
|
|
94
|
+
# 按输出扩展名自动选 PNG/SVG 后端
|
|
95
|
+
render_dxf("轴套.dxf", "轴套.png", RenderOptions(dpi=300))
|
|
96
|
+
render_dxf("轴套.dxf", "轴套.svg", RenderOptions())
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### DWG → 图片(端到端)
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from cad2image import RenderOptions, process_dwg
|
|
103
|
+
|
|
104
|
+
out = process_dwg("轴套.dwg", "out/", RenderOptions(dpi=600)) # 返回 Path
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 分步转换(先转 DXF 再渲染,便于缓存)
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from cad2image import convert_dwg_to_dxf, render_to_svg
|
|
111
|
+
|
|
112
|
+
dxf = convert_dwg_to_dxf("轴套.dwg", "dxf_cache/") # 返回 Path
|
|
113
|
+
render_to_svg(dxf, "轴套.svg", RenderOptions())
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 批量处理目录
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from cad2image import RenderOptions, process_directory
|
|
120
|
+
|
|
121
|
+
result = process_directory("CAD Test/", "out/", RenderOptions(),
|
|
122
|
+
output_format="png", recursive=True)
|
|
123
|
+
print(f"成功 {result.success_count},失败 {result.failure_count}")
|
|
124
|
+
for f in result.failures:
|
|
125
|
+
print(f"[{f.stage}] {f.source.name}: {f.error}") # stage: convert / render
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### CTB 打印样式
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from cad2image import RenderOptions, load_ctb, get_lineweight, render_dxf
|
|
132
|
+
|
|
133
|
+
styles = load_ctb("黑白线型.ctb")
|
|
134
|
+
lw = get_lineweight(styles, aci=1) # 颜色索引 1 的线宽(mm),未覆盖时为 None
|
|
135
|
+
|
|
136
|
+
render_dxf("图.dxf", "图.png", RenderOptions(ctb="黑白线型.ctb"))
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### RenderOptions 常用参数
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
RenderOptions(
|
|
143
|
+
dpi=300, # PNG 分辨率
|
|
144
|
+
background="white", # default/white/black/off
|
|
145
|
+
color_policy="color", # color/monochrome/grayscale/black/white
|
|
146
|
+
lineweight_policy="absolute", # absolute/relative
|
|
147
|
+
lineweight_scaling=1.0, # 线宽整体缩放(仅绝对线宽)
|
|
148
|
+
min_lineweight=None, # 最小打印线宽(mm)
|
|
149
|
+
relative_max_stroke_width=0.001, # 相对线宽最粗比例
|
|
150
|
+
relative_min_stroke_width=0.05, # 相对线宽最细比例
|
|
151
|
+
ctb="", # CTB 样式表路径
|
|
152
|
+
font_dir="", # 附加字体目录(SHX/TTF)
|
|
153
|
+
layout_name=None, # 布局名,None=模型空间
|
|
154
|
+
width_mm=None, height_mm=None, # 显式页面尺寸(mm)
|
|
155
|
+
fit_to_extents=False, # 按内容包围盒自适应
|
|
156
|
+
margin=3.0, # 自适应时四周余量(%)
|
|
157
|
+
)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
错误统一抛 `FileNotFoundError` / `ValueError` / `RuntimeError`,可放心捕获;中文与 ASCII 标注随包内置开源字体(见下文「字体与中文」)。
|
|
161
|
+
|
|
162
|
+
## Acme 参数映射
|
|
163
|
+
|
|
164
|
+
| Acme 参数 | 本工具映射 |
|
|
165
|
+
|---|---|
|
|
166
|
+
| `/res N` | `--dpi N` |
|
|
167
|
+
| `/w` `/h`(mm) | `--width` / `--height` |
|
|
168
|
+
| `/e` `/ad`(缩放扩展) | `--fit`(包围盒自适应) |
|
|
169
|
+
| `/b` 背景色 | `--background` |
|
|
170
|
+
| `/lw 0/1/2` | `--lineweight` + `--lineweight-scaling` |
|
|
171
|
+
| `/p 1/2/3`(1bit/灰度/256色) | `--color`(灰度暂以 monochrome 近似,待 Phase 2) |
|
|
172
|
+
| `/pw myset` | `plotstyle.load_ctb`(尚未实现,见已知限制) |
|
|
173
|
+
| `/a 0/-1/-2`(布局选择) | `--layout` |
|
|
174
|
+
| `/l` 报告 | 批处理汇总 + 退出码 |
|
|
175
|
+
|
|
176
|
+
## 项目结构
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
src/cad2image/
|
|
180
|
+
├── cli.py # typer CLI 入口
|
|
181
|
+
├── dwg2dxf.py # ODA File Converter 封装(目录级转换编排)
|
|
182
|
+
├── render.py # ezdxf → PNG/SVG 渲染核心
|
|
183
|
+
├── config.py # 参数 → ezdxf Configuration 映射
|
|
184
|
+
├── plotstyle.py # CTB 打印样式(占位)
|
|
185
|
+
└── batch.py # 批量 + 部分失败结果对象
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## 测试
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
pytest # 单元测试(无外部依赖)
|
|
192
|
+
pytest -m integration # 集成测试(需真实 DWG + ODA)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
集成测试通过环境变量提供真实图纸:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
CAD_TEST_DWG=E:/path/to/轴套.dwg pytest tests/test_integration.py -m integration
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## 打包发布
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# 构建 wheel(本机需加 --no-build-isolation 规避构建隔离环境联网拉 setuptools 的 SSL 劫持)
|
|
205
|
+
python -m pip wheel . --no-deps --no-build-isolation -w dist
|
|
206
|
+
|
|
207
|
+
# 构建 sdist(可选)
|
|
208
|
+
python -c "import setuptools.build_meta as b; b.build_sdist('dist')"
|
|
209
|
+
|
|
210
|
+
# 安装到干净环境并验证中文渲染
|
|
211
|
+
python -m pip install dist/cad2image-0.1.0-py3-none-any.whl
|
|
212
|
+
python -m cad2image 轴套.dwg -o 轴套.png
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
产物 `dist/cad2image-0.1.0-py3-none-any.whl` 自带开源字体(Noto Sans SC / Noto Sans Mono)与 `py.typed`,无 `--no-build-isolation` 需要时可用标准 `python -m build` 生成 wheel + sdist。
|
|
216
|
+
|
|
217
|
+
## 质量门禁
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
ruff check .
|
|
221
|
+
ruff format .
|
|
222
|
+
mypy src/
|
|
223
|
+
pytest
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## 字体与中文
|
|
227
|
+
|
|
228
|
+
渲染时**默认自动扫描**随包内置的 `fonts/` 目录,也可通过 `--font-dir` 追加其它目录。内置字体均为**开源字体(SIL OFL 1.1)**,随仓库与 wheel 一起分发,中文与 ASCII 渲染开箱即用:
|
|
229
|
+
|
|
230
|
+
| 字体文件 | 用途 | 许可 |
|
|
231
|
+
|---|---|---|
|
|
232
|
+
| `NotoSansSC-Regular.otf` | 中文(含拉丁字符) | SIL OFL 1.1 |
|
|
233
|
+
| `NotoSansMono-Regular.ttf` | ASCII 等宽,近似 CAD 单线字体 | SIL OFL 1.1 |
|
|
234
|
+
|
|
235
|
+
> 完整许可与署名见 `src/cad2image/fonts/OFL.txt` 与 `NOTICE`。
|
|
236
|
+
|
|
237
|
+
CAD 图纸的文字样式常引用专有字体(微软 `SimSun`/`NSimSun`、Autodesk `romans.shx`/`txt.shx` 等)。渲染时会自动把这些字体名**重写为内置开源字体**:
|
|
238
|
+
|
|
239
|
+
- `SimSun` / `NSimSun` / `宋体` / `新宋体` / 中文 bigfont(`hztxt` / `gbcbig` 等)→ `NotoSansSC-Regular.otf`
|
|
240
|
+
- 其余 SHX 字形字体(`romans.shx` / `txt.shx` / `simplex` 等)→ `NotoSansMono-Regular.ttf`
|
|
241
|
+
|
|
242
|
+
> 替换会改变文字外观(SimSun → Noto Sans SC、SHX 单线体 → Noto Sans Mono 等宽),但保证纯开源、无专有字体再分发风险。若需严格保留原字体观感,可自行将对应字体放入 `fonts/` 或通过 `--font-dir` 指定。
|
|
243
|
+
|
|
244
|
+
**重要限制**:ezdxf 1.1.x 的 `ShapeFile` 解析器**不支持 bigfont 中文大字体**(`HZDX.SHX`/`gbcbig.shx` 等,源码中明确抛出 `UnsupportedShapeFile("BIGFONT shapes are not supported yet")`),因此本项目不内置 SHX 大字体;引用这类字体的图纸会被自动重写为 `NotoSansSC-Regular.otf`,中文可正常渲染。
|
|
245
|
+
|
|
246
|
+
## 已知限制
|
|
247
|
+
|
|
248
|
+
- **灰度输出**(Acme `/p 2`):暂以 monochrome 近似,真正的 256 级灰度需像素级后处理。
|
|
249
|
+
- **SHX 中文 bigfont**:ezdxf 1.1.x 不支持,中文走 TTF 字体(见上)。
|
|
250
|
+
- **3D 消隐**(Acme `/hide`):未实现,假设输入为 2D 工程图。
|
|
251
|
+
- **Xref 外部引用**:转换前需确保 xref 文件与主图同目录(与 Acme 行为一致)。
|
|
252
|
+
- **页面单位**:模型空间包围盒自适应时假定绘图单位为 mm(`$INSUNITS` 未做换算)。
|
|
253
|
+
|
|
254
|
+
## License
|
|
255
|
+
|
|
256
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cad2image"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "DWG/DXF → PNG/SVG 渲染管线,用 ODA File Converter + ezdxf 替换 Acme CAD Converter"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
classifiers = [
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.8",
|
|
17
|
+
"Programming Language :: Python :: 3.9",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Topic :: Multimedia :: Graphics :: Graphics Conversion",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"ezdxf>=1.1.3,<1.2",
|
|
25
|
+
"PyMuPDF>=1.23,<1.25",
|
|
26
|
+
"numpy>=1.22",
|
|
27
|
+
"typer>=0.9",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
dev = [
|
|
32
|
+
"pytest>=7.0",
|
|
33
|
+
"ruff>=0.1",
|
|
34
|
+
"mypy>=1.0",
|
|
35
|
+
"Pillow>=9.0", # 测试中读取 PNG 像素校验渲染结果
|
|
36
|
+
"fonttools>=4.30", # ezdxf 渲染字体时的运行时依赖(ezdxf 自身也会安装)
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
cad2image = "cad2image.cli:app"
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://github.com/zhy201810576/cad2image"
|
|
44
|
+
Repository = "https://github.com/zhy201810576/cad2image"
|
|
45
|
+
Issues = "https://github.com/zhy201810576/cad2image/issues"
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.packages.find]
|
|
48
|
+
where = ["src"]
|
|
49
|
+
|
|
50
|
+
# 开源字体(Noto Sans SC / Noto Sans Mono,SIL OFL 1.1)与 py.typed 作为包数据随 wheel 分发,
|
|
51
|
+
# 确保安装后中文与 ASCII 渲染开箱即用。
|
|
52
|
+
[tool.setuptools.package-data]
|
|
53
|
+
cad2image = ["py.typed", "fonts/*.otf", "fonts/*.ttf", "fonts/OFL.txt", "fonts/NOTICE"]
|
|
54
|
+
|
|
55
|
+
[tool.ruff]
|
|
56
|
+
line-length = 120
|
|
57
|
+
target-version = "py38"
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint]
|
|
60
|
+
select = ["E", "W", "F", "I", "B", "C4", "UP", "SIM"]
|
|
61
|
+
# E501 由 formatter 处理;B008 是 typer/fastapi 将 Option 作为默认值的框架惯用法。
|
|
62
|
+
ignore = ["E501", "B008"]
|
|
63
|
+
|
|
64
|
+
[tool.mypy]
|
|
65
|
+
python_version = "3.8"
|
|
66
|
+
strict = true
|
|
67
|
+
# ezdxf 未声明 __all__,关闭隐式再导出检查以避免误报(运行时正确)。
|
|
68
|
+
implicit_reexport = true
|
|
69
|
+
|
|
70
|
+
[[tool.mypy.overrides]]
|
|
71
|
+
module = "tests.*"
|
|
72
|
+
disallow_untyped_defs = false
|
|
73
|
+
|
|
74
|
+
[tool.pytest.ini_options]
|
|
75
|
+
markers = [
|
|
76
|
+
"integration: 需要真实 DWG 与 ODA File Converter 的集成测试",
|
|
77
|
+
]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""cad2image — DWG/DXF → PNG/SVG 渲染管线。
|
|
2
|
+
|
|
3
|
+
用 ODA File Converter(DWG→DXF)+ ezdxf(DXF→PNG/SVG)重建 Acme CAD Converter
|
|
4
|
+
的渲染能力,消除 GDI 栅格化路径产生的「残余杂线」问题。
|
|
5
|
+
|
|
6
|
+
公开接口:
|
|
7
|
+
- convert_dwg_to_dxf / convert_directory:DWG → DXF(ODA File Converter 封装)
|
|
8
|
+
- render_dxf / render_to_png / render_to_svg:DXF → PNG/SVG(ezdxf 渲染核心)
|
|
9
|
+
- process_dwg / process_directory:单文件 / 目录批处理
|
|
10
|
+
- RenderOptions:渲染参数
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from cad2image.batch import BatchResult, process_directory, process_dwg
|
|
14
|
+
from cad2image.config import RenderOptions, get_oda_converter_path
|
|
15
|
+
from cad2image.dwg2dxf import convert_directory, convert_dwg_to_dxf
|
|
16
|
+
from cad2image.plotstyle import get_lineweight, load_ctb
|
|
17
|
+
from cad2image.render import render_dxf, render_to_png, render_to_svg
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"BatchResult",
|
|
21
|
+
"RenderOptions",
|
|
22
|
+
"convert_directory",
|
|
23
|
+
"convert_dwg_to_dxf",
|
|
24
|
+
"get_lineweight",
|
|
25
|
+
"get_oda_converter_path",
|
|
26
|
+
"load_ctb",
|
|
27
|
+
"process_directory",
|
|
28
|
+
"process_dwg",
|
|
29
|
+
"render_dxf",
|
|
30
|
+
"render_to_png",
|
|
31
|
+
"render_to_svg",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
__version__ = "0.1.0"
|