Skip to content

Commit 49e66c3

Browse files
committed
download file
1 parent 0eae988 commit 49e66c3

6 files changed

Lines changed: 87 additions & 109 deletions

File tree

Dockerfile

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,37 @@
11
# Use a lightweight Python base image
2-
FROM python:3.13.7
2+
FROM python:3.13-slim
33

4-
# Set working directory
4+
RUN apt-get update && apt-get install -y \
5+
wget \
6+
libfuse2 \
7+
xvfb \
8+
libc6 \
9+
libfontconfig1 \
10+
&& rm -rf /var/lib/apt/lists/*
11+
12+
# Download ODA File Converter AppImage (replace with the latest version)
13+
RUN wget -O /tmp/ODAFileConverter.AppImage \
14+
https://www.opendesign.com/guestfiles/get?filename=ODAFileConverter_QT6_lnxX64_8.3dll_26.8.AppImage
15+
16+
# Extract AppImage (avoids FUSE)
17+
RUN chmod +x /tmp/ODAFileConverter.AppImage \
18+
&& /tmp/ODAFileConverter.AppImage --appimage-extract \
19+
&& mv squashfs-root /opt/ODAFileConverter \
20+
&& ln -s /opt/ODAFileConverter/AppRun /usr/local/bin/ODAFileConverter \
21+
&& rm /tmp/ODAFileConverter.AppImage
22+
23+
24+
ENV ODAFILECONVERTER=/usr/local/bin/ODAFileConverter
25+
ENV XDG_RUNTIME_DIR=/tmp/runtime-root
26+
RUN mkdir -p /tmp/runtime-root && chmod 700 /tmp/runtime-root
27+
28+
# Set the working directory
529
WORKDIR /app
630

731
# Copy requirement files first (better caching)
832
COPY requirements.txt requirements.txt
933

10-
# Install dependencies
34+
# Install Python dependencies
1135
RUN pip install --no-cache-dir -r requirements.txt
1236

1337
# Copy app files

app.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,9 @@ def generate_cadastral_plan():
121121

122122
drawer.add_title(html_to_mtext(plan.build_title()), frame_center_x, title_y, title_width, title_height)
123123

124-
125-
drawer.save_dxf()
124+
# drawer.save_dxf()
125+
# drawer.dxf_to_dwg()
126+
drawer.save()
126127
return jsonify({"message": "Cadastral plan generated", "filename": plan.name}), 200
127128

128129
if __name__ == '__main__':

dxf.py

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import ezdxf
22
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
95
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
1013

1114
class SurveyDXFManager:
1215
def __init__(self, plan_name: str = "Survey Plan", scale: float = 1.0):
@@ -167,53 +170,57 @@ def get_filename(self):
167170
plan_name = re.sub(r"\s+", "_",plan_name)
168171
plan_name = re.sub(r"[^a-z0-9._-]", "", plan_name)
169172
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]}"
171174

172-
def save_dxf(self):
175+
def save_dxf(self, filepath: str = None):
173176
"""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")
176222

177-
def dxf_to_pdf(self, margin_ratio: float = 0.05):
178-
self.save_dxf()
179223

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)
217224

218225

219226

pl.py

Lines changed: 0 additions & 41 deletions
This file was deleted.

requirements.txt

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,20 @@ beautifulsoup4==4.13.5
33
blinker==1.9.0
44
bs4==0.0.2
55
click==8.2.1
6-
colorama==0.4.6
7-
comtypes==1.4.12
8-
contourpy==1.3.3
9-
cycler==0.12.1
106
ezdxf==1.4.2
117
Flask==3.1.2
128
fonttools==4.59.2
139
gunicorn==23.0.0
1410
itsdangerous==2.2.0
1511
Jinja2==3.1.6
16-
kiwisolver==1.4.9
1712
MarkupSafe==3.0.2
18-
matplotlib==3.10.6
1913
numpy==2.3.3
2014
packaging==25.0
2115
pillow==11.3.0
22-
pyautocad==0.2.0
2316
pydantic==2.11.9
2417
pydantic_core==2.33.2
18+
PyMuPDF==1.26.4
2519
pyparsing==3.2.4
26-
python-dateutil==2.9.0.post0
27-
six==1.17.0
2820
soupsieve==2.8
2921
typing-inspection==0.4.1
3022
typing_extensions==4.15.0

utils.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ def html_to_mtext(html_text: str):
3232
return ""
3333

3434
html_text = html_text.replace('\n', '')
35-
print(html_text)
3635
soup = BeautifulSoup(html_text, "html.parser")
3736
editor = MTextEditor()
3837

@@ -71,7 +70,3 @@ def parse_tag(tag):
7170
result = str(editor)
7271
result = result.replace('\n', '')
7372
return result
74-
75-
76-
if __name__ == '__main__':
77-
print(repr(html_to_mtext()))

0 commit comments

Comments
 (0)