Pdf Processor
Under the Hood: The Three-Tier PDF Extraction Model
TLDR
PDF.js exposes three distinct document reading APIs:getStructTree() (semantic ground truth), getOperatorList() (geometric paint stream ground truth), and getTextContent() (derived typographic text convenience API). Designing a browser-side extractor as a top-down cascade (Tier 1 $\rightarrow$ Tier 2 $\rightarrow$ Tier 3) enables fast exits on well-tagged documents while reserving expensive spatial heuristics for untagged fallbacks.
| Extraction API Tier | PDF Format Source | Marked Content ID (MCID) Support | Primary Structural Output |
|---|---|---|---|
Tier 1: getStructTree() | Cross-Reference Tree | Explicit Leaf MCIDs | Exact Table, TR, TD Nodes |
Tier 2: getOperatorList() | Raw Paint Stream (q/Q/cm) | BMC/BDC/EMC Stack | Full-height Column Vector Rules |
Tier 3: getTextContent() | Derived convenience API | None | Positioned Text Items & Advance Widths |
Technical three-tier cascade architecture
// Top-Down Three-Tier PDF Extraction Cascade Engine
export async function extractPdfPageCascading(page, pdfjsPage) {
// Tier 1: Attempt Semantic Structure Tree Extraction via MCID Join
const structTree = await pdfjsPage.getStructTree();
if (structTree && structTree.children?.length > 0) {
const opList = await pdfjsPage.getOperatorList();
const mcidMap = buildMcidToTextMap(opList);
const semanticDoc = parseStructTreeNodes(structTree, mcidMap);
if (semanticDoc.isComplete) return semanticDoc;
}
// Tier 2: Attempt Explicit Paint Stream Geometry Extraction const opList = await pdfjsPage.getOperatorList(); const columnRules = findFullHeightVerticalRules(opList, page.viewBox); if (columnRules.length > 0) { return assembleLayoutFromExplicitRules(page, columnRules); }
// Tier 3: Fallback to Spatial Heuristic Extraction const textContent = await pdfjsPage.getTextContent(); const pageScaleS = calculateModeFontBaseline(textContent.items); return executeBipartiteSpatialExtraction(textContent.items, pageScaleS); }
Rule of thumb: Cascade PDF extraction top-down fromgetStructTree()togetOperatorList()geometry before falling back to spatialgetTextContent()heuristics.
Read this post in the full Engineering Journal →