|
1 | 1 | import ezdxf |
2 | 2 | import re |
3 | | -from ezdxf.enums import TextEntityAlignment, MTextParagraphAlignment |
4 | | -import matplotlib |
5 | | -matplotlib.use("Agg") # no GUI |
6 | | -import matplotlib.pyplot as plt |
7 | | -from ezdxf.addons.drawing import Frontend, RenderContext |
8 | | -from ezdxf.addons.drawing.matplotlib import MatplotlibBackend |
| 3 | +from ezdxf.enums import TextEntityAlignment |
| 4 | +from ezdxf.addons.drawing import Frontend, RenderContext, pymupdf, layout, config |
9 | 5 | from ezdxf.tools.text import MTextEditor |
| 6 | +from ezdxf.addons import odafc |
| 7 | +import tempfile |
| 8 | +import os |
| 9 | +from datetime import datetime |
| 10 | +import uuid |
| 11 | +import zipfile |
| 12 | +import shutil |
10 | 13 |
|
11 | 14 | class SurveyDXFManager: |
12 | 15 | def __init__(self, plan_name: str = "Survey Plan", scale: float = 1.0): |
@@ -167,53 +170,57 @@ def get_filename(self): |
167 | 170 | plan_name = re.sub(r"\s+", "_",plan_name) |
168 | 171 | plan_name = re.sub(r"[^a-z0-9._-]", "", plan_name) |
169 | 172 | plan_name = re.sub(r"_+", "_", plan_name) |
170 | | - return plan_name |
| 173 | + return f"{plan_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:6]}" |
171 | 174 |
|
172 | | - def save_dxf(self): |
| 175 | + def save_dxf(self, filepath: str = None): |
173 | 176 | """Save the DXF document to a file""" |
174 | | - self.doc.saveas(f"{self.get_filename()}.dxf") |
175 | | - |
| 177 | + if filepath: |
| 178 | + self.doc.saveas(filepath) |
| 179 | + return |
| 180 | + dxf_path = f"{self.get_filename()}.dxf" |
| 181 | + self.doc.saveas(dxf_path) |
| 182 | + |
| 183 | + def save_pdf(self, filepath: str = None): |
| 184 | + context = RenderContext(self.doc) |
| 185 | + backend = pymupdf.PyMuPdfBackend() |
| 186 | + cfg = config.Configuration(background_policy=config.BackgroundPolicy.WHITE) |
| 187 | + frontend = Frontend(context, backend, config=cfg) |
| 188 | + frontend.draw_layout(self.msp) |
| 189 | + page = layout.Page(210, 297, layout.Units.mm, margins=layout.Margins.all(20)) |
| 190 | + if not filepath: |
| 191 | + filepath = f"{self.get_filename()}.pdf" |
| 192 | + pdf_bytes = backend.get_pdf_bytes(page) |
| 193 | + with open(filepath, "wb") as f: |
| 194 | + f.write(pdf_bytes) |
| 195 | + |
| 196 | + def save_dwg(self, dxf_filepath: str, filepath: str = None): |
| 197 | + if not filepath: |
| 198 | + filepath = f"{self.get_filename()}.dwg" |
| 199 | + odafc.convert(dxf_filepath, filepath) |
| 200 | + |
| 201 | + def save(self): |
| 202 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 203 | + filename = self.get_filename() |
| 204 | + dxf_path = os.path.join(tmpdir, f"{filename}.dxf") |
| 205 | + dwg_path = os.path.join(tmpdir, f"{filename}.dwg") |
| 206 | + pdf_path = os.path.join(tmpdir, f"{filename}.pdf") |
| 207 | + zip_path = os.path.join(tmpdir, f"{filename}.zip") |
| 208 | + |
| 209 | + self.save_dxf(dxf_path) |
| 210 | + self.save_dwg(dxf_path, dwg_path) |
| 211 | + self.save_pdf(pdf_path) |
| 212 | + |
| 213 | + # Create a ZIP file containing all three formats |
| 214 | + with zipfile.ZipFile(zip_path, "w") as zipf: |
| 215 | + zipf.write(dxf_path, os.path.basename(dxf_path)) |
| 216 | + zipf.write(dwg_path, os.path.basename(dwg_path)) |
| 217 | + zipf.write(pdf_path, os.path.basename(pdf_path)) |
| 218 | + |
| 219 | + |
| 220 | + # Copy DWG to a permanent location |
| 221 | + shutil.copy(zip_path, f"{filename}.zip") |
176 | 222 |
|
177 | | - def dxf_to_pdf(self, margin_ratio: float = 0.05): |
178 | | - self.save_dxf() |
179 | 223 |
|
180 | | - dxf_path = f"{self.get_filename()}.dxf" |
181 | | - doc = ezdxf.readfile(dxf_path) |
182 | | - msp = doc.modelspace() |
183 | | - |
184 | | - # compute bounding box |
185 | | - xs, ys = [], [] |
186 | | - for e in msp: |
187 | | - if e.dxftype() == "LINE": |
188 | | - xs += [e.dxf.start[0], e.dxf.end[0]] |
189 | | - ys += [e.dxf.start[1], e.dxf.end[1]] |
190 | | - elif e.dxftype() == "CIRCLE": |
191 | | - xs += [e.dxf.center[0] - e.dxf.radius, e.dxf.center[0] + e.dxf.radius] |
192 | | - ys += [e.dxf.center[1] - e.dxf.radius, e.dxf.center[1] + e.dxf.radius] |
193 | | - |
194 | | - if not xs or not ys: |
195 | | - xs = [0, 10]; ys = [0, 10] # fallback box |
196 | | - |
197 | | - min_x, max_x = min(xs), max(xs) |
198 | | - min_y, max_y = min(ys), max(ys) |
199 | | - |
200 | | - width = max(max_x - min_x, 1) # avoid zero |
201 | | - height = max(max_y - min_y, 1) |
202 | | - |
203 | | - fig, ax = plt.subplots(figsize=(8, 8 * height / width)) |
204 | | - ax.set_xlim(min_x, max_x) |
205 | | - ax.set_ylim(min_y, max_y) |
206 | | - ax.axis("off") |
207 | | - |
208 | | - ctx = RenderContext(doc) |
209 | | - out = MatplotlibBackend(ax) |
210 | | - out.set_background("black") |
211 | | - # out.set_color_mapper(lambda entity: 'black' if entity.dxf.color == 7 else None) |
212 | | - Frontend(ctx, out).draw_layout(msp, finalize=True) |
213 | | - |
214 | | - pdf_path = f"{self.get_filename()}.pdf" |
215 | | - fig.savefig(pdf_path, bbox_inches="tight", pad_inches=0) |
216 | | - plt.close(fig) |
217 | 224 |
|
218 | 225 |
|
219 | 226 |
|
0 commit comments