Export: add /p/<uid>.pdf and /p/<uid>.docx (xhtml2pdf + htmldocx); overlay export buttons for all users; minor HTML wrapping
This commit is contained in:
58
app.py
58
app.py
@@ -15,6 +15,10 @@ from flask import (
|
|||||||
flash,
|
flash,
|
||||||
Response,
|
Response,
|
||||||
)
|
)
|
||||||
|
from io import BytesIO
|
||||||
|
from xhtml2pdf import pisa # type: ignore
|
||||||
|
from docx import Document # type: ignore
|
||||||
|
from htmldocx import HtmlToDocx # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
@@ -238,14 +242,64 @@ def create_app():
|
|||||||
"Made by Ruslan"
|
"Made by Ruslan"
|
||||||
"</div>"
|
"</div>"
|
||||||
)
|
)
|
||||||
|
# Export buttons (visible to everyone). If admin overlay exists, offset a bit.
|
||||||
|
top_offset = "60px" if is_logged_in() else "16px"
|
||||||
|
pdf_url = url_for("export_pdf", uid=uid)
|
||||||
|
docx_url = url_for("export_docx", uid=uid)
|
||||||
|
exports = (
|
||||||
|
f'<div style="position:fixed;top:{top_offset};right:16px;z-index:2147483647;display:flex;gap:8px;">'
|
||||||
|
f'<a href="{pdf_url}" style="background:#2b2f3b;color:#fff;padding:8px 10px;border-radius:8px;text-decoration:none;font-weight:600;">PDF</a>'
|
||||||
|
f'<a href="{docx_url}" style="background:#2b2f3b;color:#fff;padding:8px 10px;border-radius:8px;text-decoration:none;font-weight:600;">Word</a>'
|
||||||
|
'</div>'
|
||||||
|
)
|
||||||
|
overlays = wm + exports
|
||||||
lower_all = html.lower()
|
lower_all = html.lower()
|
||||||
if "</body>" in lower_all:
|
if "</body>" in lower_all:
|
||||||
i2 = lower_all.rfind("</body>")
|
i2 = lower_all.rfind("</body>")
|
||||||
html = html[:i2] + wm + html[i2:]
|
html = html[:i2] + overlays + html[i2:]
|
||||||
else:
|
else:
|
||||||
html = html + wm
|
html = html + overlays
|
||||||
return Response(html, mimetype="text/html; charset=utf-8")
|
return Response(html, mimetype="text/html; charset=utf-8")
|
||||||
|
|
||||||
|
def _fetch_page(uid: str):
|
||||||
|
db = get_db()
|
||||||
|
row = db.execute("SELECT title, html FROM pages WHERE uuid = ?", (uid,)).fetchone()
|
||||||
|
if row is None:
|
||||||
|
abort(404)
|
||||||
|
return row
|
||||||
|
|
||||||
|
def _wrap_html_for_export(title: str, html: str) -> str:
|
||||||
|
head_title = f"<title>{title}</title>" if title else ""
|
||||||
|
return (
|
||||||
|
"<!doctype html><html lang='ru'><head><meta charset='utf-8'>" + head_title +
|
||||||
|
"<style>body{font-family:Arial,Helvetica,sans-serif;}</style></head><body>" +
|
||||||
|
html + "</body></html>"
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.route("/p/<string:uid>.pdf")
|
||||||
|
def export_pdf(uid: str):
|
||||||
|
row = _fetch_page(uid)
|
||||||
|
title = row["title"] or f"page-{uid[:8]}"
|
||||||
|
html_doc = _wrap_html_for_export(title, row["html"])
|
||||||
|
out = BytesIO()
|
||||||
|
pisa.CreatePDF(src=html_doc, dest=out)
|
||||||
|
out.seek(0)
|
||||||
|
headers = {"Content-Disposition": f"attachment; filename={title}.pdf"}
|
||||||
|
return Response(out.read(), mimetype="application/pdf", headers=headers)
|
||||||
|
|
||||||
|
@app.route("/p/<string:uid>.docx")
|
||||||
|
def export_docx(uid: str):
|
||||||
|
row = _fetch_page(uid)
|
||||||
|
title = row["title"] or f"page-{uid[:8]}"
|
||||||
|
html_doc = _wrap_html_for_export(title, row["html"])
|
||||||
|
doc = Document()
|
||||||
|
HtmlToDocx().add_html_to_document(html_doc, doc)
|
||||||
|
out = BytesIO()
|
||||||
|
doc.save(out)
|
||||||
|
out.seek(0)
|
||||||
|
headers = {"Content-Disposition": f"attachment; filename={title}.docx"}
|
||||||
|
return Response(out.read(), mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document", headers=headers)
|
||||||
|
|
||||||
# Optional: simple 404 page to keep things minimal
|
# Optional: simple 404 page to keep things minimal
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def not_found(_):
|
def not_found(_):
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
Flask>=2.3,<3.0
|
Flask>=2.3,<3.0
|
||||||
gunicorn>=21.2
|
gunicorn>=21.2
|
||||||
|
python-docx>=1.1
|
||||||
|
htmldocx>=0.0.6
|
||||||
|
xhtml2pdf>=0.2.15
|
||||||
|
|||||||
Reference in New Issue
Block a user