- ocr/: Tesseract-based vendor/date/total extraction. PSM 6 (single uniform text block) instead of the default fixes receipts where item-name/price columns were otherwise split into separate blocks and dropped. Vendor line picked by median (not max) word height within the top fraction of the crop, breaking ties by topmost line - max() was fooled by single descender/ascender glyphs (commas, parens) inflating one word's bounding box. - naming/: merge OCR metadata into YYYY-MM-DD_vendor.pdf filenames, degrading gracefully to UNSORTED_<timestamp>.pdf; per-run dedup. - pdf/: img2pdf-based multi-page-capable assembly (images_to_pdf). - pipeline.run_export(): OCR + name + PDF each detected crop from a detect() report, writes pdf/ and export_summary.csv. - New `paperpod export <video>` CLI command. - Verified against synthetic Home Depot/Metro/Petro-Canada receipts: 3/3 correct vendor, date, and total after tuning. Known limitation (documented in README): no page-flip/multi-page grouping yet (that's events/, still unbuilt) - every detected document becomes its own single-page PDF.
56 lines
1.5 KiB
Python
56 lines
1.5 KiB
Python
import numpy as np
|
|
import pytest
|
|
from PIL import Image
|
|
|
|
from paperpod.pdf.build import images_to_pdf
|
|
|
|
|
|
def _write_png(path, color):
|
|
Image.new("RGB", (200, 300), color).save(path)
|
|
|
|
|
|
def test_images_to_pdf_single_page(tmp_path):
|
|
img_path = tmp_path / "page.png"
|
|
_write_png(img_path, (255, 255, 255))
|
|
out = tmp_path / "out.pdf"
|
|
|
|
result = images_to_pdf([img_path], out, title="Test Doc")
|
|
|
|
assert result == out
|
|
assert out.exists()
|
|
data = out.read_bytes()
|
|
assert data.startswith(b"%PDF")
|
|
|
|
|
|
def test_images_to_pdf_multi_page(tmp_path):
|
|
paths = []
|
|
for i, color in enumerate([(255, 0, 0), (0, 255, 0), (0, 0, 255)]):
|
|
p = tmp_path / f"page_{i}.png"
|
|
_write_png(p, color)
|
|
paths.append(p)
|
|
out = tmp_path / "multi.pdf"
|
|
|
|
images_to_pdf(paths, out)
|
|
|
|
data = out.read_bytes()
|
|
# img2pdf writes one xobject image per page; a 3-page PDF should be
|
|
# meaningfully larger than a 1-page PDF of the same-size images.
|
|
single = tmp_path / "single.pdf"
|
|
images_to_pdf([paths[0]], single)
|
|
assert out.stat().st_size > single.stat().st_size
|
|
|
|
|
|
def test_images_to_pdf_requires_at_least_one_image(tmp_path):
|
|
with pytest.raises(ValueError):
|
|
images_to_pdf([], tmp_path / "empty.pdf")
|
|
|
|
|
|
def test_images_to_pdf_creates_parent_dirs(tmp_path):
|
|
img_path = tmp_path / "page.png"
|
|
_write_png(img_path, (10, 20, 30))
|
|
out = tmp_path / "nested" / "dir" / "out.pdf"
|
|
|
|
images_to_pdf([img_path], out)
|
|
|
|
assert out.exists()
|