agenda-parser / webapp /pdf_view.py
rdubwiley's picture
Deploy Agenda Parser
e94c043 verified
Raw
History Blame Contribute Delete
2.96 kB
"""Render an agenda item's packet pages to images for the Gradio gallery (fallback UI).
Uses the same confidence-gated slicer as the Agenda Item Report
(:func:`webapp.backend._slice_packet_for_item`) to pick the page range, then
rasterizes those pages with pypdfium2. Reads the uploaded packet from the local
cache (keyed by ``upload_id``); the heavy work runs only when the user clicks
"View packet pages".
"""
from __future__ import annotations
from webapp import backend
# Cap how many pages we rasterize at once so a large slice doesn't render dozens of
# images per click.
MAX_RENDER_PAGES = 12
RENDER_SCALE = 2.0 # ~144 dpi β€” legible without being huge
def render_item_pages(
upload_id: str, item_titles: list[str], item_index: int,
start: int = 0, end: int = 0,
) -> tuple[list, str]:
"""Return ``(gallery_items, caption_markdown)`` for one agenda item's pages.
``gallery_items`` is a list of ``(PIL.Image, "p.N")`` tuples for a
``gr.Gallery``; it's empty (with an explanatory caption) when the packet is
missing or the item's pages can't be confidently located. An explicit page
range (``end > start``) is used directly; otherwise the item is anchored.
"""
cp = backend.cached_packet(upload_id)
if cp is None:
return [], "_The packet is no longer on the server β€” re-upload it._"
pages = backend.cached_packet_pages(upload_id)
if not pages:
return [], "_No extractable packet text β€” the PDF may be scanned/image-only._"
s = max(0, int(start or 0))
e = min(len(pages), int(end or 0))
if e > s:
info = {"sliced": True, "pages": f"{s + 1}-{e}", "start": s, "end": e}
else:
outline = backend.cached_packet_outline(upload_id)
_, info = backend._slice_packet_for_item(pages, item_titles, item_index, outline=outline)
if not info["sliced"]:
return [], (
f"_Couldn't pinpoint this item's pages in the {len(pages)}-page packet. "
"Set the item's page range, or open the full packet._"
)
sl_start, sl_end = info["start"], info["end"]
total = sl_end - sl_start
n = min(total, MAX_RENDER_PAGES)
images = _render_pages(str(cp[0]), sl_start, sl_start + n)
caption = f"**Packet pages {info['pages']}** β€” {total} page(s) for this item"
if n < total:
caption += f"; showing the first {n}."
return images, caption
def _render_pages(src: bytes | str, start: int, end: int) -> list:
"""Rasterize pages ``[start, end)`` of a PDF to ``(PIL.Image, label)`` tuples."""
import pypdfium2 as pdfium
out: list = []
with backend._PDFIUM_LOCK: # PDFium is not thread-safe β€” serialize all access
pdf = pdfium.PdfDocument(src)
try:
for i in range(start, end):
pil = pdf[i].render(scale=RENDER_SCALE).to_pil()
out.append((pil, f"p.{i + 1}"))
finally:
pdf.close()
return out