StagesRead

PdfToDocument

A PDF's embedded text layer into one Document row per page, with word-level boxes — no OCR.

Import
import { PdfToDocument } from '@stabrise/scaledp/pdf'
Group
Read
Reads
bytes
Writes
document
Needs
pdfjs-dist
Note
Expands one row into several.

Most PDFs already contain their text. Lifting it is instant, exact, and needs no model at all — so the first question for any PDF pipeline is whether OCR is needed.

import { Pipeline } from '@stabrise/scaledp'
import { PdfToDocument, hasUsableTextLayer } from '@stabrise/scaledp/pdf'

const rows = await new Pipeline([new PdfToDocument({ resolution: 300 })]).transform(file)

const needsOcr = rows.filter((row) => !hasUsableTextLayer(row.document))
Open in builder

See Skip OCR when the PDF already has text for the full two-pass pipeline.

Coordinates are pixels, not points

A deliberate divergence from Python

ScaleDP's PdfDataToText leaves boxes in PDF points. Here they come out in the same pixel space PdfToImage renders at, so text-layer boxes and OCR boxes are directly comparable — and drawing both on one page needs no conversion.

That is what resolution controls: it is not a render DPI here, it is the pixel space the boxes are expressed in. Match it to your PdfToImage.

Words versus runs

pdf.js reports text as runs — a line, a styled span, whatever the producer emitted. splitWords: true (the default) splits those into word boxes by measuring the run's own font, which is what makes entity boxes usable. Turning it off gives run-level boxes, which are coarser but exactly what the PDF said.

Document.text is the run texts joined by newlines either way, and Document.type is 'pdf'.

Parameters

ParameterTypeDefaultMeaning
resolutionnumber 36–600300Pixel space the boxes are expressed in. Match PdfToImage to align them.
pageLimitnumber00 reads every page.
splitWordsbooleantrueSplit pdf.js line runs into word boxes. Off yields run-level boxes.

On this page