145 lines
5.7 KiB
Python
145 lines
5.7 KiB
Python
import os
|
|
import subprocess
|
|
|
|
def render_cad_file_to_image(file_path, output_png_path):
|
|
import matplotlib
|
|
matplotlib.use('Agg')
|
|
import matplotlib.pyplot as plt
|
|
import ezdxf
|
|
from ezdxf import bbox
|
|
from ezdxf.addons.drawing import RenderContext, Frontend
|
|
from ezdxf.addons.drawing.matplotlib import MatplotlibBackend
|
|
from ezdxf.addons.drawing.properties import LayoutProperties
|
|
from ezdxf.addons.drawing.config import Configuration, LineweightPolicy
|
|
|
|
ext = os.path.splitext(file_path)[1].lower()
|
|
temp_dxf_path = None
|
|
|
|
if ext == '.dwg':
|
|
temp_dxf_path = file_path + ".temp.dxf"
|
|
try:
|
|
# Run dwg2dxf subprocess to convert DWG to temporary DXF
|
|
# dwg2dxf syntax: dwg2dxf -o output.dxf input.dwg
|
|
subprocess.run(
|
|
["dwg2dxf", "-o", temp_dxf_path, file_path],
|
|
check=True,
|
|
capture_output=True,
|
|
text=True
|
|
)
|
|
except (subprocess.CalledProcessError, FileNotFoundError) as e:
|
|
# Clean up if created
|
|
if temp_dxf_path and os.path.exists(temp_dxf_path):
|
|
try:
|
|
os.remove(temp_dxf_path)
|
|
except Exception:
|
|
pass
|
|
raise RuntimeError(
|
|
"L'utilitaire dwg2dxf (package libredwg-utils) est requis pour la conversion des fichiers DWG, "
|
|
f"mais il n'a pas pu être exécuté : {str(e)}"
|
|
)
|
|
dxf_to_render = temp_dxf_path
|
|
elif ext == '.dxf':
|
|
dxf_to_render = file_path
|
|
else:
|
|
raise ValueError(f"Format de fichier CAO non supporté : {ext}")
|
|
|
|
try:
|
|
# Load and render DXF
|
|
doc = ezdxf.readfile(dxf_to_render)
|
|
msp = doc.modelspace()
|
|
|
|
# Calculate modelspace bounds
|
|
bounds = bbox.extents(msp)
|
|
min_x, min_y = bounds.extmin.x, bounds.extmin.y
|
|
max_x, max_y = bounds.extmax.x, bounds.extmax.y
|
|
|
|
# Calculate dimensions to maintain exact aspect ratio
|
|
dx = max_x - min_x
|
|
dy = max_y - min_y
|
|
if dx <= 0: dx = 100
|
|
if dy <= 0: dy = 100
|
|
|
|
aspect_ratio = dy / dx
|
|
|
|
# Scale figure size so the maximum dimension is exactly 20.0 inches
|
|
# At 300 DPI, this results in exactly 6,000 pixels for the maximum dimension.
|
|
if aspect_ratio > 1:
|
|
fig_height = 20.0
|
|
fig_width = fig_height / aspect_ratio
|
|
else:
|
|
fig_width = 20.0
|
|
fig_height = fig_width * aspect_ratio
|
|
|
|
fig = plt.figure(frameon=True, figsize=(fig_width, fig_height), facecolor='#FFFFFF')
|
|
ax = fig.add_axes([0, 0, 1, 1])
|
|
ax.set_axis_off()
|
|
|
|
ctx = RenderContext(doc)
|
|
layout_properties = LayoutProperties.from_layout(msp)
|
|
layout_properties.set_colors(bg='#FFFFFF')
|
|
|
|
# Configure drawing settings to scale lineweights down for crispness
|
|
config = Configuration(
|
|
lineweight_scaling=0.8,
|
|
lineweight_policy=LineweightPolicy.ABSOLUTE,
|
|
min_lineweight=0.06
|
|
)
|
|
|
|
class DarkeningMatplotlibBackend(MatplotlibBackend):
|
|
def _adjust_properties(self, properties):
|
|
color = properties.color
|
|
if color.startswith('#'):
|
|
h = color.lstrip('#')
|
|
if len(h) == 6:
|
|
r, g, b = int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
|
|
elif len(h) == 3:
|
|
r, g, b = int(h[0]*2, 16), int(h[1]*2, 16), int(h[2]*2, 16)
|
|
else:
|
|
return properties
|
|
|
|
lum = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255.0
|
|
if lum > 0.55:
|
|
scale = 0.25 / lum
|
|
scale = min(scale, 0.8)
|
|
r_new = max(0, min(255, int(r * scale)))
|
|
g_new = max(0, min(255, int(g * scale)))
|
|
b_new = max(0, min(255, int(b * scale)))
|
|
new_color = f"#{r_new:02x}{g_new:02x}{b_new:02x}"
|
|
return properties._replace(color=new_color)
|
|
return properties
|
|
|
|
def draw_path(self, path, properties):
|
|
super().draw_path(path, self._adjust_properties(properties))
|
|
|
|
def draw_filled_paths(self, paths, properties):
|
|
super().draw_filled_paths(paths, self._adjust_properties(properties))
|
|
|
|
def draw_filled_polygon(self, points, properties):
|
|
super().draw_filled_polygon(points, self._adjust_properties(properties))
|
|
|
|
def draw_line(self, start, end, properties):
|
|
super().draw_line(start, end, self._adjust_properties(properties))
|
|
|
|
def draw_point(self, pos, properties):
|
|
super().draw_point(pos, self._adjust_properties(properties))
|
|
|
|
def draw_solid_lines(self, lines, properties):
|
|
super().draw_solid_lines(lines, self._adjust_properties(properties))
|
|
|
|
backend = DarkeningMatplotlibBackend(ax, adjust_figure=False)
|
|
Frontend(ctx, backend, config=config).draw_layout(msp, layout_properties=layout_properties, finalize=True)
|
|
|
|
ax.set_xlim(min_x, max_x)
|
|
ax.set_ylim(min_y, max_y)
|
|
|
|
os.makedirs(os.path.dirname(output_png_path), exist_ok=True)
|
|
fig.savefig(output_png_path, dpi=300, bbox_inches='tight', pad_inches=0, facecolor='#FFFFFF')
|
|
plt.close(fig)
|
|
|
|
finally:
|
|
# Clean up temporary DXF file if created
|
|
if temp_dxf_path and os.path.exists(temp_dxf_path):
|
|
try:
|
|
os.remove(temp_dxf_path)
|
|
except Exception:
|
|
pass
|