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.
171 lines
6.7 KiB
Python
171 lines
6.7 KiB
Python
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()
|
||
|