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
+66
View File
@@ -0,0 +1,66 @@
"""PDF -> PDF passthrough (.pdf uploads).
A valid, unencrypted PDF is validated with pypdf and passed through to the
output with the standard naming/collision contract, re-stamping /Title from
the uploaded filename stem. It reuses common.finalize_pdf so the atomic
write path (.tmp + os.replace) and metadata handling are identical to every
other converter.
Rejections (all ValueError -> surfaced verbatim as a job error / CLI error):
- corrupt / unparseable -> "Invalid PDF: ... could not be parsed"
- encrypted / password -> "PDF rejected: ... encrypted or password-protected"
- more than MAX_PAGES -> "PDF too large: ... pages (max N)"
"""
from __future__ import annotations
import io
from pathlib import Path
from pypdf import PdfReader
from . import common
# req 1 (pdf passthrough): upper bound on page count for a pass-through PDF.
MAX_PAGES = 500
def convert(path: Path, job_id: str, progress_cb=None, output_dir=None,
source_name=None) -> str:
"""Validate a PDF and pass it through to a same-named output PDF.
Satisfies: req 1 (pdf passthrough — validate, stamp /Title, standard
naming/collision, atomic write).
"""
name = source_name or path.name
stem = Path(name).stem or "book"
raw = path.read_bytes()
# req 1: validate with pypdf. Construction raises on header/body corruption.
try:
reader = PdfReader(io.BytesIO(raw))
except Exception as exc: # noqa: BLE001 - pypdf raises many corrupt-file types
raise ValueError(f"Invalid PDF: {name} could not be parsed ({exc})") from exc
# req 1: reject encrypted / password-protected BEFORE touching .pages
# (accessing pages on an encrypted reader raises, not a clean error).
if reader.is_encrypted:
raise ValueError(f"PDF rejected: {name} is encrypted or password-protected")
# req 1: count pages; a corrupt body beyond a valid header fails here.
try:
page_count = len(reader.pages)
except Exception as exc: # noqa: BLE001
raise ValueError(f"Invalid PDF: {name} could not be parsed ({exc})") from exc
# req 1: reject oversized documents (clear error, distinct from corrupt).
if page_count > MAX_PAGES:
raise ValueError(
f"PDF too large: {name} has {page_count} pages (max {MAX_PAGES})"
)
# req 1: stamp /Title from the uploaded filename stem; standard naming/collision.
common.report(progress_cb, 30.0, f"Passing through {name} (1/1)")
out_dir = common.default_output_dir(output_dir)
filename = common.derive_export_name(name, out_dir)
return common.finalize_pdf(raw, out_dir, filename, job_id,
progress_cb=progress_cb, title=stem)