aboutsummaryrefslogtreecommitdiffstats
path: root/app.py
blob: 991bc57e8f960bb4658b88feb06e6ea2c3ff477c (plain) (blame)
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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)