dxfedit/03_Python_OpenSource_DXF/export_dxf.py
puzzlesion 5e87bd7c33 refactor(dxf): optimize table drawing and add diagnostic scripts
This commit includes several updates to the Python_OpenSource_DXF project:

- The core script draw_table_from_template.py has been refactored for better logic and clarity.

- New utility scripts such as diagnose_blocks.py, export_dxf.py, and plot_by_block_name.py have been added to enhance diagnostic and plotting capabilities.

- The convert_dxf_to_pdf.py script was removed as its functionality is now covered by other modules.

- README.md and .specstory documentation have been updated to reflect these changes.
2025-09-12 17:21:08 +08:00

171 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import ezdxf
import sys
from ezdxf.addons.drawing import RenderContext, Frontend
from ezdxf.addons.drawing.matplotlib import MatplotlibBackend
from ezdxf.addons.drawing.properties import Configuration
from ezdxf import bbox # 导入边界框计算模块
import matplotlib.pyplot as plt
import os
import json # 导入JSON模块
import argparse # 导入命令行参数处理模块
# --- 配置 ---
# 将脚本的父目录项目根目录添加到Python路径中
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_ROOT = os.path.dirname(SCRIPT_DIR)
sys.path.append(PROJECT_ROOT)
# 指定字体目录
FONT_DIR = r"C:\Users\83500\久翌\CAD编辑同步excel\测试文件区\03_Python_OpenSource_DXF\fonts"
def main():
"""
主函数,用于解析命令行参数并调用核心处理函数。
"""
# --- 1. 设置命令行参数解析 ---
parser = argparse.ArgumentParser(
description="将 DXF 文件导出为 PDF, PNG, 并提取其坐标保存为 JSON。",
formatter_class=argparse.RawTextHelpFormatter # 保持帮助文本的格式
)
parser.add_argument(
"input_dxf_path",
type=str,
help="要处理的源 DXF 文件的完整路径。"
)
# 设置默认输出目录
default_output_dir = os.path.join(PROJECT_ROOT, "04_Test_Files", "结果")
parser.add_argument(
"--output_dir",
type=str,
default=default_output_dir,
help="指定输出文件PDF, PNG, JSON的保存目录。\n"
f"如果未提供,将默认使用:\n{default_output_dir}"
)
parser.add_argument(
'--crop',
nargs=4,
type=float,
metavar=('X1', 'Y1', 'X2', 'Y2'),
help="提供一个矩形区域的两个对角点坐标 (X1 Y1 X2 Y2) 来裁剪输出的图像。"
)
args = parser.parse_args()
# --- 2. 确保输出目录存在 ---
os.makedirs(args.output_dir, exist_ok=True)
# --- 3. 调用核心功能 ---
export_dxf_with_coords(args.input_dxf_path, args.output_dir, crop_coords=args.crop)
def export_dxf_with_coords(input_path, output_dir, crop_coords=None):
"""
将指定的 DXF 文件中的 "布局1" 渲染为 PDF 和 PNG并将其边界坐标保存为 JSON 文件。
如果提供了 crop_coords则会对输出的图像进行裁剪。
"""
# --- 1. 检查输入文件是否存在 ---
if not os.path.exists(input_path):
print(f"错误: 输入的 DXF 文件未找到: '{input_path}'")
return
# --- 2. 设置输出文件名 ---
base_name = os.path.splitext(os.path.basename(input_path))[0]
json_path = os.path.join(output_dir, f"{base_name}_coords.json")
pdf_path = os.path.join(output_dir, f"{base_name}.pdf")
png_path = os.path.join(output_dir, f"{base_name}.png")
try:
# --- 3. 加载并解析 DXF 文件 ---
print(f"--- 正在加载 DXF 文件: {os.path.basename(input_path)} ---")
doc = ezdxf.readfile(input_path)
# --- 新增: 查找并使用 "布局1" ---
layout_name = '布局1'
try:
layout = doc.layouts.get(layout_name)
print(f"--- 已成功找到并切换到布局: '{layout_name}' ---")
except KeyError:
available_layouts = ", ".join(doc.layouts.names())
print(f"错误: 在 DXF 文件中找不到名为 '{layout_name}' 的布局。")
print(f"该文件中可用的布局有: [{available_layouts}]")
return
# --- 4. 计算图纸边界并保存为 JSON (基于布局) ---
print(f"--- 正在计算 '{layout_name}' 的边界坐标 ---")
extents = bbox.extents(layout, fast=True)
if not extents.has_data:
print(f"警告: 布局 '{layout_name}' 中不包含任何实体,无法计算坐标。")
# 即使没有坐标,也可能需要渲染一个空白页面,所以这里我们不直接返回
coords = {}
else:
coords = {
"bottom_left": {"x": round(extents.extmin.x, 2), "y": round(extents.extmin.y, 2)},
"bottom_right": {"x": round(extents.extmax.x, 2), "y": round(extents.extmin.y, 2)},
"top_left": {"x": round(extents.extmin.x, 2), "y": round(extents.extmax.y, 2)},
"top_right": {"x": round(extents.extmax.x, 2), "y": round(extents.extmax.y, 2)},
}
with open(json_path, 'w', encoding='utf-8') as f:
json.dump(coords, f, ensure_ascii=False, indent=4)
print(f"坐标已成功保存到: '{json_path}'")
# --- 5. 配置渲染器(包括字体) ---
# 鉴于当前环境存在无法解释的版本冲突问题,
# 我们回退到使用全局 support_dirs 的方法,这种方法兼容性更强,可以绕过问题。
if FONT_DIR not in ezdxf.options.support_dirs:
ezdxf.options.support_dirs.append(FONT_DIR)
# 使用默认配置, 并添加解决方法来处理3D点实体
config = Configuration.defaults().with_changes(
# 将点的大小设置为0这会让渲染器将点绘制为单个像素
# 从而绕过在处理3D点(POINT)的复杂几何图形时可能出现的Z坐标问题。
pdsize=0,
)
# --- 6. 渲染 DXF 内容 ---
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])
ctx = RenderContext(doc)
out = MatplotlibBackend(ax)
print("--- 开始渲染 DXF 内容 ---")
Frontend(ctx, out, config=config).draw_layout(layout, finalize=True)
# --- 新增: 应用裁剪区域 ---
if crop_coords:
x1, y1, x2, y2 = crop_coords
min_x, max_x = min(x1, x2), max(x1, x2)
min_y, max_y = min(y1, y2), max(y1, y2)
print(f"--- 正在应用裁剪区域: X({min_x:.2f} - {max_x:.2f}), Y({min_y:.2f} - {max_y:.2f}) ---")
ax.set_xlim(min_x, max_x)
ax.set_ylim(min_y, max_y)
# --- 7. 保存为 PDF ---
print(f"--- 正在保存为 PDF: {os.path.basename(pdf_path)} ---")
fig.savefig(pdf_path, dpi=300, bbox_inches='tight', pad_inches=0.1)
print(f"PDF 文件已保存到: '{pdf_path}'")
# --- 8. 保存为 PNG ---
print(f"--- 正在保存为 PNG: {os.path.basename(png_path)} ---")
fig.savefig(png_path, dpi=300, bbox_inches='tight', pad_inches=0.1)
print(f"PNG 文件已保存到: '{png_path}'")
plt.close(fig)
print("\n操作成功完成!")
except IOError:
print(f"错误: 无法读取文件: '{input_path}'")
except Exception as e:
print(f"处理并转换 DXF 文件时发生未知错误: {e}")
if __name__ == "__main__":
main()