-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
105 lines (80 loc) · 3.48 KB
/
Copy pathmain.py
File metadata and controls
105 lines (80 loc) · 3.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import os
import re
import subprocess
import tempfile
from pathlib import Path
from fastapi import FastAPI, File, Form, UploadFile
from fastapi.responses import FileResponse, Response
from fastapi.staticfiles import StaticFiles
app = FastAPI()
# Page break marker pattern - matches ---pagebreak---, <!-- pagebreak -->, \pagebreak
_PAGE_BREAK_RE = re.compile(
r"^(?:---\s*pagebreak\s*---|<!--\s*pagebreak\s*-->|\\pagebreak)\s*$",
re.MULTILINE | re.IGNORECASE,
)
_PAGE_BREAK_HTML = '<div class="pagebreak"></div>'
def convert_eisvogel(markdown_content: str, filename: str) -> tuple[bytes | None, str | None]:
"""Convert markdown to PDF using Eisvogel/LuaLaTeX."""
with tempfile.TemporaryDirectory() as tmp_dir:
md_path = os.path.join(tmp_dir, f"{filename}.md")
pdf_path = os.path.join(tmp_dir, f"{filename}.pdf")
with open(md_path, "w", encoding="utf-8") as f:
f.write(markdown_content)
result = subprocess.run(
["pandoc", "--defaults=/app/defaults.yaml", md_path, "-o", pdf_path],
capture_output=True,
text=True,
)
if result.returncode != 0:
return None, f"Conversion failed: {result.stderr}"
if os.path.exists(pdf_path):
with open(pdf_path, "rb") as f:
return f.read(), None
return None, "PDF file was not generated."
def convert_github(markdown_content: str, filename: str) -> tuple[bytes | None, str | None]:
"""Convert markdown to PDF using GitHub-style rendering."""
with tempfile.TemporaryDirectory() as tmp_dir:
md_path = os.path.join(tmp_dir, f"{filename}.md")
html_path = os.path.join(tmp_dir, f"{filename}.html")
pdf_path = os.path.join(tmp_dir, f"{filename}.pdf")
processed_content = _PAGE_BREAK_RE.sub(_PAGE_BREAK_HTML, markdown_content)
with open(md_path, "w", encoding="utf-8") as f:
f.write(processed_content)
result = subprocess.run(
["pandoc", "--defaults=/app/defaults-github.yaml", md_path, "-o", html_path],
capture_output=True,
text=True,
)
if result.returncode != 0:
return None, f"HTML conversion failed: {result.stderr}"
result = subprocess.run(
["node", "/app/scripts/html-to-pdf.js", html_path, pdf_path, "--page-numbers"],
capture_output=True,
text=True,
)
if result.returncode != 0:
return None, f"PDF conversion failed: {result.stderr}"
if os.path.exists(pdf_path):
with open(pdf_path, "rb") as f:
return f.read(), None
return None, "PDF file was not generated."
@app.post("/convert")
async def convert(file: UploadFile = File(...), style: str = Form("eisvogel")):
content = (await file.read()).decode("utf-8")
filename = Path(file.filename).stem
if style == "github":
pdf_bytes, error = convert_github(content, filename)
else:
pdf_bytes, error = convert_eisvogel(content, filename)
if error:
return Response(content=error, status_code=500, media_type="text/plain")
return Response(
content=pdf_bytes,
media_type="application/pdf",
headers={"Content-Disposition": f'attachment; filename="{filename}.pdf"'},
)
@app.get("/")
async def index():
return FileResponse("static/index.html")
# Mount static files after routes so it doesn't shadow them
app.mount("/static", StaticFiles(directory="static"), name="static")