Spaces:
Running on Zero
Running on Zero
| """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 | |