Implement deterministic born-digital PDF text extraction #58
No reviewers
Labels
No labels
area/deployment
area/governance
area/metadata
area/pipeline
area/search
type/data
type/design
type/implementation
type/testing
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Arkive/arkive!58
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feature/issue-13-pdf-extraction"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implements Issue #13 by adding deterministic, page-aware text extraction for approved born-digital PDF originals.
The implementation builds directly on the Issue #11 verified-byte boundary and Issue #12 processing-adapter contract. PDF processing consumes the exact bytes retained by successful integrity verification and never reopens or modifies the original source file.
Closes #13
What changed
Added
arkive.pdf_textborn-digital PDF processing adapter.Added exact runtime dependency pin:
pypdf==6.15.0Added immutable page-aware processing results using 1-based physical PDF page numbers.
Extended
ProcessingResultwith optional ordered page information while preserving the existing serialized shape for non-paginated/plain-text results.Added deterministic aggregate text serialization using a single form-feed (
U+000C) between physical pages.Added stable Arkive processing failures for:
Preserved Issue #11 verification failures unchanged.
Added documentation for:
Updated the changelog.
Extraction behavior
The adapter:
application/pdforiginals;VerifiedDocumentBytes;PdfReader(..., strict=True);A PDF containing some empty/image-only pages is accepted when at least one page contains usable extractable text. Empty pages retain their physical positions for later citation resolution.
A PDF with no usable non-whitespace extractable text fails clearly rather than silently producing an empty successful result.
Dependency choice
pypdf==6.15.0is pinned exactly because PDF extraction output is reproducibility-sensitive.The pin is a tested baseline, not a permanent restriction. Future pypdf upgrades should be deliberate compatibility changes validated against the pilot corpus and failure fixtures. Material extraction-behavior changes should also update the Arkive PDF adapter version so transformation lineage remains meaningful.
qpdf and Poppler remain independent development/reference tools and are not production runtime dependencies.
Pilot corpus validation
All three approved born-digital pilot PDFs were processed successfully:
doc_attributions-use-tasl-originaldoc_summary-licensing-best-practices-for-sharing-s-original-be2e5116doc_ten-simple-rules-for-taking-advantage-of-git-and-github-originalTests verify:
Failure coverage
Purpose-built tests cover:
Negative PDFs are generated deterministically in tests rather than committing opaque third-party binary fixtures.
Manual PDF validation
The three original pilot PDFs were independently checked using qpdf and Poppler.
qpdf --checkconfirmed for all three:pdfinfoindependently confirmed the expected page counts:Representative text and page boundaries were independently compared with
pdftotext.The comparison also confirmed a documented PDF-extraction limitation: text ordering within complex layouts such as the Canva metadata/table page can differ between extractors while remaining associated with the correct physical page. Issue #13 deliberately preserves extractor output rather than attempting layout reconstruction or normalization.
Validation
Final pre-staging validation:
python -m unittest discover -s tests -vmetadata validator
Ruff lint
Ruff formatting check
git diff --checkpython -m pip checkpinned extractor sanity check
pypdf 6.15.0repeated extraction of all three pilot PDFs
qpdf structural validation
Poppler manual passage/page-boundary comparison
Scope
No changes were made to:
No OCR, normalization, chunk generation, indexing, embeddings, layout reconstruction, or plugin discovery is introduced.