From 1eec75fee2751233472bba6ca5a9a2b28d9a8a7c Mon Sep 17 00:00:00 2001 From: OrdoAbChao <58732249+OrdoAbChao7@users.noreply.github.com> Date: Mon, 5 Oct 2026 00:39:57 +0800 Subject: [PATCH] fix(render): retain source tile indices for PDF subsets --- render/src/pixelrag_render/backends/pdf.py | 6 ++- tests/test_pdf_page_indices.py | 43 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/test_pdf_page_indices.py diff --git a/render/src/pixelrag_render/backends/pdf.py b/render/src/pixelrag_render/backends/pdf.py index 7241105..0db8594 100644 --- a/render/src/pixelrag_render/backends/pdf.py +++ b/render/src/pixelrag_render/backends/pdf.py @@ -32,6 +32,7 @@ def render_pdf( output_dir: Directory to write the tile subdirectory into. dpi: Resolution for rendering (default 200 gives ~1650×2200px for A4). pages: 1-based list of page numbers to render. ``None`` renders all pages. + Tile filenames and indices retain their zero-based source page index. quality: JPEG quality 1-100 (default 85). stem: Override for the tile directory name. Defaults to the PDF filename stem. The pipeline passes the article_id here so directory names @@ -82,10 +83,11 @@ def render_pdf( saved_tiles: list[str] = [] chunks_info: list[dict] = [] - for idx, img in enumerate(images): + first_index = convert_kwargs.get("first_page", 1) - 1 + for idx, img in enumerate(images, start=first_index): # If caller provided a sparse page list, skip pages not in the list if pages is not None: - page_num = min(pages) + idx + page_num = idx + 1 if page_num not in pages: continue diff --git a/tests/test_pdf_page_indices.py b/tests/test_pdf_page_indices.py new file mode 100644 index 0000000..0be77b0 --- /dev/null +++ b/tests/test_pdf_page_indices.py @@ -0,0 +1,43 @@ +"""Selected PDF pages retain the same zero-based IDs as a full render.""" + +import json +import sys +from types import ModuleType + +import pytest +from PIL import Image +from pixelrag_render import render_pdf + + +@pytest.mark.parametrize("pages", [None, [1], [3], [4], [2, 3], [1, 3], [2, 4], [4, 2]]) +def test_pdf_page_indices_match_full_render(tmp_path, monkeypatch, pages): + source = tmp_path / "document.pdf" + source.touch() + colors = ["red", "green", "blue", "yellow"] + converter = ModuleType("pdf2image") + + def convert_from_path(**kwargs): + first = kwargs.get("first_page", 1) + last = kwargs.get("last_page", 4) + return [ + Image.new("RGB", (16, 16), colors[i - 1]) for i in range(first, last + 1) + ] + + converter.convert_from_path = convert_from_path + monkeypatch.setitem(sys.modules, "pdf2image", converter) + full_dir = render_pdf(source, tmp_path / "full")[0] + selected_dir = render_pdf(source, tmp_path / "selected", pages=pages)[0] + expected_ids = [0, 1, 2, 3] if pages is None else sorted(p - 1 for p in pages) + expected_files = [f"tile_{i:04d}.jpg" for i in expected_ids] + manifest = json.loads((selected_dir / "tiles.json").read_text()) + chunks = json.loads((selected_dir / "chunks.json").read_text())["chunks"] + + assert manifest["tiles"] == expected_files + assert [c["tile_index"] for c in chunks] == expected_ids + assert [c["file"] for c in chunks] == expected_files + assert [c["tile"] for c in chunks] == expected_files + assert sorted(p.name for p in selected_dir.glob("tile_*.jpg")) == expected_files + for filename in expected_files: + assert (selected_dir / filename).read_bytes() == ( + full_dir / filename + ).read_bytes()