Coverage for src / quber / review / annotated.py: 29%
24 statements
« prev ^ index » next coverage.py v7.14.0, created at 2026-09-23 22:14 -0400
« prev ^ index » next coverage.py v7.14.0, created at 2026-09-23 22:14 -0400
1"""Annotated PDF: the source pages with each table's final boundary drawn on.
3Each table's ``content_region`` (the corrected end of the table at its last data
4row) is drawn onto its page with a small ordinal+title label, so a reviewer can
5see at a glance which regions were located and how they map onto the source.
6Falls back to ``som_region`` when ``content_region`` is absent (engines without
7the Set-of-Mark locator). The tables are supplied already-extracted, so this
8does no extraction of its own.
9"""
11from __future__ import annotations
13from pathlib import Path
14from typing import Sequence
16import fitz
18from quber.core.extractors import ExtractedTable
20BOX_RGB = (0.0, 0.32, 0.92)
23def render_annotated_pdf(tables: Sequence[ExtractedTable], pdf: Path, out_path: Path) -> Path:
24 """Draw each table's final boundary (``content_region``, else ``som_region``)
25 onto a copy of ``pdf``.
27 One box per located table; pages with no tables are left untouched. Tables
28 with neither region (engines that do not run the locator) are skipped.
29 Returns ``out_path``.
30 """
31 doc = fitz.open(str(pdf))
32 drawn = 0
33 for t in tables:
34 region = t.content_region or t.som_region
35 if not region:
36 continue
37 page = doc[t.page - 1]
38 w, h = page.rect.width, page.rect.height
39 x0, y0, x1, y1 = region
40 rect = fitz.Rect(min(x0, x1) * w, min(y0, y1) * h, max(x0, x1) * w, max(y0, y1) * h)
41 page.draw_rect(rect, color=BOX_RGB, width=1.4)
42 label = f"{t.page}.{drawn + 1}: {t.title}" if t.title else f"{t.page}.{drawn + 1}"
43 page.insert_text((rect.x0 + 2, max(8.0, rect.y0 - 3)), label, fontsize=6, color=BOX_RGB)
44 drawn += 1
45 out_path.parent.mkdir(parents=True, exist_ok=True)
46 doc.save(str(out_path))
47 return out_path