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:
@@ -0,0 +1,55 @@
|
||||
"""Plain text (.txt) and log (.log) -> PDF.
|
||||
|
||||
Decode ladder (no mojibake on legacy files, never raises):
|
||||
1. strict UTF-8 -> valid UTF-8 is never second-guessed
|
||||
2. strict windows-1252 -> its 5 undefined byte holes (0x81/8D/8F/90/9D)
|
||||
make a successful strict decode a reliable
|
||||
cp1252 signal (covers latin-1-ish content too)
|
||||
3. charset-normalizer -> best guess for other legacy encodings
|
||||
4. UTF-8 errors=replace -> last resort, total losslessness guarantee
|
||||
|
||||
Empty / whitespace-only input is a job error ("empty file: ..."). The text
|
||||
is wrapped in <pre> with PRE_CSS (monospace, wrapped, A4) so line structure
|
||||
and indentation survive the render.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from . import common
|
||||
|
||||
|
||||
def decode_bytes(raw: bytes) -> str:
|
||||
"""Best-effort decode ladder; see module docstring."""
|
||||
try:
|
||||
return raw.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
try:
|
||||
return raw.decode("cp1252")
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
from charset_normalizer import from_bytes
|
||||
|
||||
best = from_bytes(raw).best()
|
||||
if best is not None:
|
||||
return str(best)
|
||||
return raw.decode("utf-8", errors="replace")
|
||||
|
||||
|
||||
def convert(path: Path, job_id: str, progress_cb=None, output_dir=None,
|
||||
source_name=None) -> str:
|
||||
"""Render a plain-text or log file to a monospace PDF."""
|
||||
raw = path.read_bytes()
|
||||
if not raw.strip():
|
||||
raise ValueError("empty file: nothing to convert")
|
||||
text = decode_bytes(raw)
|
||||
|
||||
def _build():
|
||||
return f"<html><body><pre>{common.escape(text)}</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,
|
||||
)
|
||||
Reference in New Issue
Block a user