Pdf Processor
Engineering Journal: The Math Behind Context-Aware PDF Extraction
Entry 42: the blind extraction problem
Standard, linear PDF extraction pipelines, extracting vector segments, finding intersecting grids, and dumping text into bounding boxes, fail catastrophically on complex documents containing interleaved paragraphs, titles, and tables. Decorative underlines generate phantom 1x1 tables, while coordinate jitter causes text slightly outside cell borders to drop out of the extracted output entirely. A working engine must stop reading PDFs sequentially and begin classifying layout spatially.Entry 43: KD-tree proximity & underline discrimination
To eliminate phantom tables, we introduced thecontextClassifier. Before running table extraction passes, the classifier categorizes all document elements into typed spatial regions (TABLE, PARAGRAPH, HEADING, LIST, IMAGE).
To differentiate decorative underlines from table borders, we implemented baseline proximity math:
// Proximity check: Is the horizontal segment an underline?
export function isBaselineUnderline(hSegment, textMetaItems) {
const hY = (hSegment.y1 + hSegment.y2) / 2;
for (const item of textMetaItems) { const yDist = hY - item.vy;
// Line sits 0 to 5px below text baseline with overlapping X span if (yDist >= -1 && yDist <= 5 && isXOverlap(item, hSegment)) { return true; } } return false; }
Filtering vector segments that meet this baseline proximity condition eliminated $99\%$ of hallucinated tables across our test corpus.
Entry 44: region scoping and nearest-cell snapping
To prevent table content from leaking into paragraph text, text items located inside table bounding boxes are assigned to the table region and marked as consumed in a centralassignedTextIndices bitset:
const tableTextIndices = [];
textMetaItems.forEach(item => {
if (isPointInsideBBox(item.vx, item.vy, tableBBox)) {
tableTextIndices.push(item.idx);
assignedTextIndices.add(item.idx); // Mark consumed
}
});
To resolve coordinate jitter data loss, we replaced strict bounding-box checks with a nearest-neighbor Euclidean proximity model. Text items snap to the nearest table cell center within a $15\text{px}$ threshold, reducing extraction data loss to zero.
Entry 45: the page assembler pipeline
ThepageAssembler receives classified, non-overlapping spatial regions, sorts them vertically by Y-coordinates, and invokes specialized block builders:
TABLEregions are formatted bybuildTable().HEADINGregions are wrapped in<h3>or<h4>elements.LISTregions are cleaned of PDF bullet characters and rendered as<ul><li>trees.PARAGRAPHregions are reflowed bytextRebuilder.
Rule of thumb: Pre-classify spatial document regions and use baseline proximity math to filter decorative line noise before reconstructing tables.
Read this post in the full Engineering Journal →