diff options
Diffstat (limited to 'app.py')
| -rw-r--r-- | app.py | 121 |
1 files changed, 121 insertions, 0 deletions
@@ -0,0 +1,121 @@ +from __future__ import annotations + +import subprocess +import tempfile +import time +import uuid +from pathlib import Path + +from flask import Flask, render_template, request, send_from_directory, url_for + +app = Flask(__name__) + +BASE_DIR = Path(__file__).resolve().parent +GENERATED_DIR = BASE_DIR / "generated" +LATEX_TEMPLATE = BASE_DIR / "latex" / "template.tex" + +GENERATED_DIR.mkdir(parents=True, exist_ok=True) + +PAPER_SIZES = { + "a4paper": "A4", + "letterpaper": "US Letter", + "legalpaper": "US Legal", +} + +MARGINS = { + "0.75in": "Narrow (0.75in)", + "1in": "Normal (1in)", + "1.25in": "Comfort (1.25in)", + "1.5in": "Wide (1.5in)", +} + +MAIN_FONTS = { + "serif": "TeX Gyre Pagella", + "sans": "TeX Gyre Heros", +} + + +def _pick(options: dict[str, str], key: str, default_key: str) -> str: + if key in options: + return key + return default_key + + +@app.get("/") +def index(): + return render_template( + "index.html", + paper_sizes=PAPER_SIZES, + margins=MARGINS, + ) + + +@app.post("/convert") +def convert_markdown(): + markdown = request.form.get("markdown", "").strip() + if not markdown: + return render_template("partials/error.html", message="Markdown content is required."), 400 + + paper_size_key = _pick(PAPER_SIZES, request.form.get("paper_size", ""), "a4paper") + margin_key = _pick(MARGINS, request.form.get("margin", ""), "1in") + main_family_key = request.form.get("main_font", "serif") + + if main_family_key not in MAIN_FONTS: + main_family_key = "serif" + + epoch = int(time.time()) + unique_id = uuid.uuid4().hex + output_name = f"document_{epoch}_{unique_id}.pdf" + output_path = GENERATED_DIR / output_name + + with tempfile.TemporaryDirectory() as tmp_dir: + temp_markdown = Path(tmp_dir) / "source.md" + temp_markdown.write_text(markdown, encoding="utf-8") + + command = [ + "pandoc", + str(temp_markdown), + "--from", + "markdown+emoji", + "--pdf-engine=lualatex", + "--template", + str(LATEX_TEMPLATE), + "-V", + f"papersize={paper_size_key}", + "-V", + f"margin={margin_key}", + "-V", + f"mainfont={MAIN_FONTS[main_family_key]}", + "-o", + str(output_path), + ] + + try: + subprocess.run(command, check=True, capture_output=True, text=True) + except FileNotFoundError: + return ( + render_template( + "partials/error.html", + message="Pandoc is not installed or not in PATH.", + ), + 500, + ) + except subprocess.CalledProcessError as exc: + stderr = (exc.stderr or "").strip() + error_message = stderr[-1200:] if stderr else "PDF conversion failed." + return render_template("partials/error.html", message=error_message), 400 + + return render_template( + "partials/result.html", + download_url=url_for("download_pdf", filename=output_name), + filename=output_name, + ) + + +@app.get("/download/<path:filename>") +def download_pdf(filename: str): + return send_from_directory(GENERATED_DIR, filename, as_attachment=True) + + +if __name__ == "__main__": + app.run(debug=True) |
