Spaces:
Sleeping
Sleeping
Commit ·
f33d8f5
1
Parent(s): 9677463
Handle non-latin1 filename headers
Browse files- app/main.py +10 -5
- app/renderer.py +19 -1
app/main.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
| 3 |
import io
|
| 4 |
import re
|
| 5 |
import unicodedata
|
| 6 |
-
from urllib.parse import
|
| 7 |
from typing import Any, Dict, Literal, Optional
|
| 8 |
|
| 9 |
from fastapi import FastAPI, HTTPException
|
|
@@ -97,14 +97,19 @@ def compile_pdf(req: CompileRequest):
|
|
| 97 |
safe_base = safe_base.encode("ascii", "ignore").decode("ascii") or "document"
|
| 98 |
safe_base = re.sub(r"[^A-Za-z0-9_.-]+", "-", safe_base).strip("-") or "document"
|
| 99 |
filename = f"{safe_base}.pdf"
|
| 100 |
-
encoded_name =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
|
| 102 |
return StreamingResponse(
|
| 103 |
io.BytesIO(pdf_bytes),
|
| 104 |
media_type="application/pdf",
|
| 105 |
headers={
|
| 106 |
-
"Content-Disposition":
|
| 107 |
-
f'attachment; filename="{filename}"; filename*=UTF-8\'\'{encoded_name}'
|
| 108 |
-
)
|
| 109 |
},
|
| 110 |
)
|
|
|
|
| 3 |
import io
|
| 4 |
import re
|
| 5 |
import unicodedata
|
| 6 |
+
from urllib.parse import quote_from_bytes
|
| 7 |
from typing import Any, Dict, Literal, Optional
|
| 8 |
|
| 9 |
from fastapi import FastAPI, HTTPException
|
|
|
|
| 97 |
safe_base = safe_base.encode("ascii", "ignore").decode("ascii") or "document"
|
| 98 |
safe_base = re.sub(r"[^A-Za-z0-9_.-]+", "-", safe_base).strip("-") or "document"
|
| 99 |
filename = f"{safe_base}.pdf"
|
| 100 |
+
encoded_name = quote_from_bytes(f"{raw_name}.pdf".encode("utf-8"))
|
| 101 |
+
content_disposition = (
|
| 102 |
+
f'attachment; filename="{filename}"; filename*=UTF-8\'\'{encoded_name}'
|
| 103 |
+
)
|
| 104 |
+
try:
|
| 105 |
+
content_disposition.encode("latin-1")
|
| 106 |
+
except UnicodeEncodeError:
|
| 107 |
+
content_disposition = f'attachment; filename="{filename}"'
|
| 108 |
|
| 109 |
return StreamingResponse(
|
| 110 |
io.BytesIO(pdf_bytes),
|
| 111 |
media_type="application/pdf",
|
| 112 |
headers={
|
| 113 |
+
"Content-Disposition": content_disposition
|
|
|
|
|
|
|
| 114 |
},
|
| 115 |
)
|
app/renderer.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
|
|
|
| 3 |
import json
|
| 4 |
import re
|
| 5 |
from datetime import datetime
|
| 6 |
from typing import Any, Dict, Literal, Union
|
| 7 |
|
| 8 |
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
|
|
|
| 9 |
from weasyprint import HTML
|
| 10 |
|
| 11 |
from .config import BASE_DIR
|
|
@@ -172,6 +174,22 @@ def _resolve_template(doc: Dict[str, Any]) -> str:
|
|
| 172 |
return "InternalModule.html.j2"
|
| 173 |
|
| 174 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
# =========================
|
| 176 |
# RENDER HTML
|
| 177 |
# =========================
|
|
@@ -179,7 +197,7 @@ RenderMode = Literal["html", "pdf"]
|
|
| 179 |
|
| 180 |
|
| 181 |
def render_html(doc: Union[str, Dict[str, Any]], mode: RenderMode = "html") -> str:
|
| 182 |
-
doc = normalize_doc(doc)
|
| 183 |
|
| 184 |
template_name = _resolve_template(doc)
|
| 185 |
template = env.get_template(template_name)
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
+
import html
|
| 4 |
import json
|
| 5 |
import re
|
| 6 |
from datetime import datetime
|
| 7 |
from typing import Any, Dict, Literal, Union
|
| 8 |
|
| 9 |
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
| 10 |
+
from markupsafe import Markup
|
| 11 |
from weasyprint import HTML
|
| 12 |
|
| 13 |
from .config import BASE_DIR
|
|
|
|
| 174 |
return "InternalModule.html.j2"
|
| 175 |
|
| 176 |
|
| 177 |
+
# =========================
|
| 178 |
+
# SANITIZACIÓN DE TEXTOS
|
| 179 |
+
# =========================
|
| 180 |
+
def _sanitize_doc(value: Any) -> Any:
|
| 181 |
+
if isinstance(value, dict):
|
| 182 |
+
return {key: _sanitize_doc(val) for key, val in value.items()}
|
| 183 |
+
if isinstance(value, list):
|
| 184 |
+
return [_sanitize_doc(val) for val in value]
|
| 185 |
+
if isinstance(value, tuple):
|
| 186 |
+
return tuple(_sanitize_doc(val) for val in value)
|
| 187 |
+
if isinstance(value, str):
|
| 188 |
+
# Escapar etiquetas HTML para evitar que el contenido altere el render.
|
| 189 |
+
return Markup(html.escape(value))
|
| 190 |
+
return value
|
| 191 |
+
|
| 192 |
+
|
| 193 |
# =========================
|
| 194 |
# RENDER HTML
|
| 195 |
# =========================
|
|
|
|
| 197 |
|
| 198 |
|
| 199 |
def render_html(doc: Union[str, Dict[str, Any]], mode: RenderMode = "html") -> str:
|
| 200 |
+
doc = _sanitize_doc(normalize_doc(doc))
|
| 201 |
|
| 202 |
template_name = _resolve_template(doc)
|
| 203 |
template = env.get_template(template_name)
|