any-file to PDF (Phase 1): converters/ dispatcher, images/text/md/csv/json/xml/svg/pdf-passthrough, 112 tests green

This commit is contained in:
sirdave73
2026-09-02 01:53:24 -04:00
parent 0b575a3e1f
commit 345c39417b
17 changed files with 1306 additions and 17 deletions
+51
View File
@@ -0,0 +1,51 @@
"""XML -> PDF (pretty text dump, monospace).
Validation is explicit: lxml must parse the document, otherwise the job
fails with "Invalid XML: <reason>" (never a render failure). A valid
document is rendered as its text content — every text node and tail in
document order, one per line, whitespace-only nodes dropped — inside
<pre> with PRE_CSS. Attribute values are intentionally not dumped: the
contract is that element text survives extraction.
"""
from __future__ import annotations
from pathlib import Path
from lxml import etree
from . import common
from .text_converter import decode_bytes
def convert(path: Path, job_id: str, progress_cb=None, output_dir=None,
source_name=None) -> str:
"""Render an XML file to a monospace text-dump PDF."""
raw = path.read_bytes()
if not raw.strip():
raise ValueError("empty file: nothing to convert")
text = decode_bytes(raw)
try:
root = etree.fromstring(text.encode("utf-8"))
except etree.XMLSyntaxError as exc:
raise ValueError(f"Invalid XML: {exc}") from exc
lines: list[str] = []
for el in root.iter():
for chunk in (el.text, el.tail):
if not chunk:
continue
piece = " ".join(chunk.split())
if piece:
lines.append(piece)
if not lines:
raise ValueError("empty file: XML has no text content")
body = "\n".join(lines)
def _build():
return f"<html><body><pre>{common.escape(body)}</pre></body></html>", path.name
return common.convert_single(
path, job_id, build_html=_build, css=common.PRE_CSS,
progress_cb=progress_cb, output_dir=output_dir,
name_src=source_name or path.name,
)