"""SVG -> PDF (vector render via WeasyPrint). SVG is XML by spec, so the document is first well-formedness-checked with lxml — an unbalanced file (e.g. "") is a *job* error with "Invalid SVG: ", never a silent blank render (HTML5 foreign-content parsing would otherwise swallow the mismatch and succeed with an empty page). The SVG is then embedded verbatim in a minimal A4 HTML document; WeasyPrint renders SVG natively as a vector (text stays sharp, no rasterization). A forced page break guarantees at least one PDF page even for a degenerate document. A WeasyPrint render failure is also reported as "Invalid SVG: ...". """ from __future__ import annotations from pathlib import Path from lxml import etree from . import common _DOC = ( "{svg}
" "" ) def convert(path: Path, job_id: str, progress_cb=None, output_dir=None, source_name=None) -> str: """Render an SVG file to a vector PDF.""" raw = path.read_bytes() if not raw.strip(): raise ValueError("empty file: nothing to convert") svg_text = common.decode_text(raw) try: etree.fromstring(svg_text.encode("utf-8")) except etree.XMLSyntaxError as exc: raise ValueError(f"Invalid SVG: {exc}") from exc doc = _DOC.replace("{svg}", svg_text) out_dir = common.default_output_dir(output_dir) common.report(progress_cb, 30.0, f"Rendering {path.name} (1/1)") try: pdf_bytes = common.html_to_pdf_bytes(doc) except ValueError: raise # empty-file errors keep their own message except Exception as exc: # noqa: BLE001 - render failure is a job error raise ValueError(f"Invalid SVG: {exc}") from exc filename = common.derive_export_name(source_name or path.name, out_dir) return common.finalize_pdf(pdf_bytes, out_dir, filename, job_id, progress_cb=progress_cb)