Recipes
OCR a scanned PDF
The default pipeline — render every page, read it, and draw what was found.
The shortest useful pipeline, and the right starting point for a scan with no text layer.
import { Pipeline, ImageDrawBoxes, configure } from '@stabrise/scaledp'
import { PdfToImage } from '@stabrise/scaledp/pdf'
import { PaddleTextRecognizer } from '@stabrise/scaledp/ocr'
configure({
cache: 'indexeddb',
pdf: {
workerSrc: '/pdf.worker.min.mjs',
cMapUrl: '/cmaps/',
standardFontDataUrl: '/standard_fonts/',
},
})
const pipeline = new Pipeline([
new PdfToImage({ resolution: 300 }),
new PaddleTextRecognizer({ preset: 'v6-small', keepFormatting: true }),
new ImageDrawBoxes({
inputCols: ['image', 'text'],
outputCol: 'annotated',
color: '#3fc9f5',
lineWidth: 2,
}),
])
const rows = await pipeline.transform(file)One row per page:
import { renderInto, showImage, showText } from '@stabrise/scaledp/display'
for (const row of rows) {
if (row.text.exception) {
console.warn(`page ${row.page}:`, row.text.exception)
continue
}
renderInto('#page', showImage(row.annotated))
renderInto('#text', showText(row.text))
}Turning the dials
Too slow. Drop resolution to 150 — it quarters the pixel count. Try
preset: 'v6-tiny'. Prefer WebGPU:
import { isWebGpuAvailable } from '@stabrise/scaledp/ocr'
configure({ executionProviders: (await isWebGpuAvailable()) ? ['webgpu', 'wasm'] : ['wasm'] })Wrong script. v6-small covers Latin and CJK. For anything else, pick a v5
preset — or let the page choose:
import { detectScript, presetsForScript } from '@stabrise/scaledp/ocr'
const detected = await detectScript(canvas)
const options = detected ? await presetsForScript(detected.script) : []Skewed or rotated text. PaddleOCR detects internally and does not handle rotation especially well. Use a real detector and a separate recognizer.
Only some pages need this. If the PDF is a mix, check the text layer first — see Skip OCR when there is a text layer.
The tab freezes. Move it off the main thread: Run off the main thread.