@stabrise/scaledp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +218 -0
  3. package/dist/box-DAfzwfhA.d.ts +119 -0
  4. package/dist/config-g6IrKlDC.d.ts +80 -0
  5. package/dist/data-to-image-DoZ4jQ3R.js +54 -0
  6. package/dist/data-to-image-DoZ4jQ3R.js.map +1 -0
  7. package/dist/detect/index.d.ts +71 -0
  8. package/dist/detect/index.js +2 -0
  9. package/dist/detect-q8AI_Jdj.js +274 -0
  10. package/dist/detect-q8AI_Jdj.js.map +1 -0
  11. package/dist/detector-output-C0Qt-jEq.d.ts +13 -0
  12. package/dist/detector-output-lyF1Mqb8.js +13 -0
  13. package/dist/detector-output-lyF1Mqb8.js.map +1 -0
  14. package/dist/display/index.d.ts +66 -0
  15. package/dist/display/index.js +237 -0
  16. package/dist/display/index.js.map +1 -0
  17. package/dist/document-B8I61TiY.d.ts +16 -0
  18. package/dist/entity-CedtRhU1.d.ts +22 -0
  19. package/dist/entity-D6Hxaugj.js +13 -0
  20. package/dist/entity-D6Hxaugj.js.map +1 -0
  21. package/dist/image-CAH2rLv9.js +511 -0
  22. package/dist/image-CAH2rLv9.js.map +1 -0
  23. package/dist/image-Dc5TSg46.d.ts +18 -0
  24. package/dist/image-DoZDJkcR.js +37 -0
  25. package/dist/image-DoZDJkcR.js.map +1 -0
  26. package/dist/image-draw-boxes-De0QbFv9.js +285 -0
  27. package/dist/image-draw-boxes-De0QbFv9.js.map +1 -0
  28. package/dist/index.d.ts +269 -0
  29. package/dist/index.js +11 -0
  30. package/dist/model-cache-BEaqqRZ9.js +182 -0
  31. package/dist/model-cache-BEaqqRZ9.js.map +1 -0
  32. package/dist/model-cache-BhFYpfZz.d.ts +36 -0
  33. package/dist/ner/index.d.ts +293 -0
  34. package/dist/ner/index.js +2 -0
  35. package/dist/ner-SsZLZ6ed.js +1028 -0
  36. package/dist/ner-SsZLZ6ed.js.map +1 -0
  37. package/dist/ocr/index.d.ts +440 -0
  38. package/dist/ocr/index.js +3 -0
  39. package/dist/ocr-OHX2WM3e.js +1294 -0
  40. package/dist/ocr-OHX2WM3e.js.map +1 -0
  41. package/dist/ort-CXDoPrtw.js +73 -0
  42. package/dist/ort-CXDoPrtw.js.map +1 -0
  43. package/dist/params-DapwK9Ns.js +37 -0
  44. package/dist/params-DapwK9Ns.js.map +1 -0
  45. package/dist/pdf/index.d.ts +123 -0
  46. package/dist/pdf/index.js +2 -0
  47. package/dist/pdf-BQl0dneD.js +417 -0
  48. package/dist/pdf-BQl0dneD.js.map +1 -0
  49. package/dist/pipeline-DACqGkpN.js +240 -0
  50. package/dist/pipeline-DACqGkpN.js.map +1 -0
  51. package/dist/pipeline-DeLO-OCE.d.ts +139 -0
  52. package/dist/registry/index.d.ts +169 -0
  53. package/dist/registry/index.js +1061 -0
  54. package/dist/registry/index.js.map +1 -0
  55. package/dist/text-ahMLpxN9.js +109 -0
  56. package/dist/text-ahMLpxN9.js.map +1 -0
  57. package/dist/worker/index.d.ts +105 -0
  58. package/dist/worker/index.js +180 -0
  59. package/dist/worker/index.js.map +1 -0
  60. package/package.json +135 -0
@@ -0,0 +1,417 @@
1
+ import { c as ImageError, h as getConfig, i as Stage } from "./pipeline-DACqGkpN.js";
2
+ import { c as encodeImage, i as createCanvas, r as context2d } from "./image-CAH2rLv9.js";
3
+ import { i as resolveParams, t as BASE_STAGE_DEFAULTS } from "./params-DapwK9Ns.js";
4
+ import { n as createDocument, t as createImage } from "./image-DoZDJkcR.js";
5
+ import { r as toBytes } from "./data-to-image-DoZ4jQ3R.js";
6
+ //#region src/pdf/extract-text.ts
7
+ /** Glyphs sit above the baseline by roughly three quarters of the line height. */
8
+ const ASCENT_RATIO = .75;
9
+ /** Confidence assigned to text read from a PDF's own text layer. */
10
+ const TEXT_LAYER_SCORE = .99;
11
+ function isTextItem(item) {
12
+ return typeof item === "object" && item !== null && "str" in item && "transform" in item && Array.isArray(item.transform);
13
+ }
14
+ /** Convert one pdf.js text item into a viewport-space box. */
15
+ function textItemToBox(item, viewport) {
16
+ if (item.str.length === 0) return null;
17
+ const [a = 0, b = 0, c = 0, d = 0, e = 0, f = 0] = item.transform;
18
+ const abMag = Math.hypot(a, b) || 1;
19
+ const cdMag = Math.hypot(c, d) || 1;
20
+ const dirX = a / abMag;
21
+ const dirY = b / abMag;
22
+ const upX = c / cdMag;
23
+ const upY = d / cdMag;
24
+ const ascent = item.height * ASCENT_RATIO;
25
+ const startX = e + upX * ascent;
26
+ const startY = f + upY * ascent;
27
+ const corners = [
28
+ viewport.convertToViewportPoint(startX, startY),
29
+ viewport.convertToViewportPoint(startX + dirX * item.width, startY + dirY * item.width),
30
+ viewport.convertToViewportPoint(startX - upX * item.height, startY - upY * item.height),
31
+ viewport.convertToViewportPoint(startX + dirX * item.width - upX * item.height, startY + dirY * item.width - upY * item.height)
32
+ ];
33
+ const xs = corners.map((p) => p[0]);
34
+ const ys = corners.map((p) => p[1]);
35
+ const x = Math.min(...xs);
36
+ const y = Math.min(...ys);
37
+ const [startScreenX = 0, startScreenY = 0] = corners[0] ?? [];
38
+ const [endScreenX = 0, endScreenY = 0] = corners[1] ?? [];
39
+ const readMag = Math.hypot(endScreenX - startScreenX, endScreenY - startScreenY) || 1;
40
+ return {
41
+ text: item.str,
42
+ score: TEXT_LAYER_SCORE,
43
+ x: Math.floor(x),
44
+ y: Math.floor(y),
45
+ width: Math.max(1, Math.ceil(Math.max(...xs) - x)),
46
+ height: Math.max(1, Math.ceil(Math.max(...ys) - y)),
47
+ angle: 0,
48
+ readDirX: (endScreenX - startScreenX) / readMag,
49
+ readDirY: (endScreenY - startScreenY) / readMag,
50
+ fontName: item.fontName ?? ""
51
+ };
52
+ }
53
+ /** Extract every text item on a page as a viewport-space box. */
54
+ async function extractTextBoxes(page, viewport) {
55
+ const content = await page.getTextContent();
56
+ const boxes = [];
57
+ for (const item of content.items) {
58
+ if (!isTextItem(item)) continue;
59
+ const box = textItemToBox(item, viewport);
60
+ if (box) boxes.push(box);
61
+ }
62
+ return boxes;
63
+ }
64
+ //#endregion
65
+ //#region src/pdf/pdfjs.ts
66
+ /**
67
+ * Lazy pdf.js loader.
68
+ *
69
+ * pdfjs-dist is an optional peer dependency, so it is imported only when a PDF
70
+ * stage actually runs. Every asset path comes from `configure()` -- unlike the
71
+ * pdftools prototype, which hardcoded `/pdf.worker.min.mjs`, a path only its
72
+ * own Next app could serve.
73
+ */
74
+ let modulePromise = null;
75
+ async function loadPdfjs() {
76
+ if (modulePromise) return modulePromise;
77
+ modulePromise = (async () => {
78
+ let pdfjs;
79
+ try {
80
+ pdfjs = await import("pdfjs-dist");
81
+ } catch (cause) {
82
+ throw new Error("pdfjs-dist is required for PDF support. Install it: npm i pdfjs-dist", { cause });
83
+ }
84
+ const { workerSrc } = getConfig().pdf;
85
+ if (workerSrc) pdfjs.GlobalWorkerOptions.workerSrc = workerSrc;
86
+ return pdfjs;
87
+ })();
88
+ return modulePromise;
89
+ }
90
+ /** Reset the cached module. Tests only. */
91
+ function resetPdfjs() {
92
+ modulePromise = null;
93
+ }
94
+ /**
95
+ * Turn pdf.js's worker-setup failure into something actionable.
96
+ *
97
+ * When `workerSrc` is unset or 404s, pdf.js reports "Setting up fake worker
98
+ * failed" with a bare module URL, which says nothing about what to do. The
99
+ * worker is not bundled with this library on purpose -- it has to be served by
100
+ * the consuming application -- so the fix is always the same two steps.
101
+ */
102
+ function describePdfError(error) {
103
+ const message = error instanceof Error ? error.message : String(error);
104
+ if (!/fake worker|worker/i.test(message)) return error instanceof Error ? error : new Error(message);
105
+ const { workerSrc } = getConfig().pdf;
106
+ const cause = workerSrc ? `pdf.js could not load its worker from "${workerSrc}".` : "pdf.js has no worker configured.";
107
+ return new Error(`${cause}\nCopy it out of the package and point the config at it:
108
+ cp node_modules/pdfjs-dist/build/pdf.worker.min.mjs public/
109
+ configure({ pdf: { workerSrc: '/pdf.worker.min.mjs' } })
110
+ Original error: ${message}`, { cause: error });
111
+ }
112
+ /** Document-level options assembled from the global config. */
113
+ function documentOptions(data) {
114
+ const { cMapUrl, standardFontDataUrl, wasmUrl } = getConfig().pdf;
115
+ const owned = new Uint8Array(data.byteLength);
116
+ owned.set(data);
117
+ const options = { data: owned };
118
+ if (cMapUrl) {
119
+ options.cMapUrl = cMapUrl;
120
+ options.cMapPacked = true;
121
+ }
122
+ if (standardFontDataUrl) options.standardFontDataUrl = standardFontDataUrl;
123
+ if (wasmUrl) options.wasmUrl = wasmUrl;
124
+ return options;
125
+ }
126
+ //#endregion
127
+ //#region src/pdf/pdf-to-image.ts
128
+ /**
129
+ * Port of `scaledp/pdf/PdfDataToImage.py`: a PDF into one `Image` row per page.
130
+ *
131
+ * Python renders with PyMuPDF at a DPI; pdf.js works in scale factors, so the
132
+ * DPI converts through the PDF unit of 72 points per inch.
133
+ */
134
+ /** PDF user space is defined in points; 72 of them make an inch. */
135
+ const POINTS_PER_INCH = 72;
136
+ const PDF_TO_IMAGE_DEFAULTS = Object.freeze({
137
+ ...BASE_STAGE_DEFAULTS,
138
+ inputCol: "content",
139
+ outputCol: "image",
140
+ resolution: 300,
141
+ pageLimit: 0,
142
+ imageType: "png"
143
+ });
144
+ const MIME = {
145
+ png: "image/png",
146
+ webp: "image/webp",
147
+ jpeg: "image/jpeg"
148
+ };
149
+ var PdfToImage = class extends Stage {
150
+ name = "PdfToImage";
151
+ constructor(options = {}) {
152
+ super(resolveParams(PDF_TO_IMAGE_DEFAULTS, options, {
153
+ resolution: (value) => {
154
+ if (!Number.isFinite(value) || value <= 0) throw new RangeError(`resolution must be positive, received ${value}`);
155
+ },
156
+ pageLimit: (value) => {
157
+ if (!Number.isInteger(value) || value < 0) throw new RangeError(`pageLimit must be a non-negative integer, received ${value}`);
158
+ }
159
+ }));
160
+ }
161
+ /** One input PDF becomes N rows, each carrying its page index. */
162
+ async expand(input, row, ctx) {
163
+ const { outputCol, pageCol, pathCol, resolution, pageLimit, imageType } = this.params;
164
+ const path = String(row[pathCol] ?? "memory");
165
+ const task = (await loadPdfjs()).getDocument(documentOptions(toBytes(input)));
166
+ try {
167
+ const document = await task.promise;
168
+ const pageCount = pageLimit > 0 ? Math.min(pageLimit, document.numPages) : document.numPages;
169
+ const rows = [];
170
+ for (let index = 0; index < pageCount; index++) {
171
+ ctx.signal?.throwIfAborted();
172
+ const image = await renderPage(document, index + 1, {
173
+ resolution,
174
+ imageType,
175
+ path
176
+ });
177
+ rows.push({
178
+ ...row,
179
+ [pageCol]: index,
180
+ [outputCol]: image
181
+ });
182
+ }
183
+ return rows;
184
+ } catch (error) {
185
+ throw describePdfError(error);
186
+ } finally {
187
+ await task.destroy();
188
+ }
189
+ }
190
+ async apply() {
191
+ throw new ImageError("unreachable: expand handles every row", this.name);
192
+ }
193
+ onError(message, row) {
194
+ return createImage({
195
+ path: String(row[this.params.pathCol] ?? "memory"),
196
+ exception: message
197
+ });
198
+ }
199
+ };
200
+ /** Rasterise a single 1-based page to encoded image bytes. */
201
+ async function renderPage(document, pageNumber, opts) {
202
+ const page = await document.getPage(pageNumber);
203
+ try {
204
+ const viewport = page.getViewport({ scale: opts.resolution / 72 });
205
+ const canvas = createCanvas(viewport.width, viewport.height);
206
+ await page.render({
207
+ canvas,
208
+ viewport
209
+ }).promise;
210
+ return createImage({
211
+ path: opts.path,
212
+ resolution: opts.resolution,
213
+ data: await encodeImage(canvas, MIME[opts.imageType]),
214
+ imageType: opts.imageType,
215
+ width: canvas.width,
216
+ height: canvas.height
217
+ });
218
+ } finally {
219
+ page.cleanup();
220
+ }
221
+ }
222
+ //#endregion
223
+ //#region src/pdf/split-words.ts
224
+ /**
225
+ * Split a pdf.js text run into word-level boxes.
226
+ *
227
+ * pdf.js emits runs at line granularity ("Client: Raja Raman"), but detection
228
+ * boxes and NER offsets both want words. Each word is measured with Canvas 2D
229
+ * `measureText` using a font reconstructed from the pdf.js font name, then the
230
+ * measured widths are scaled so they sum to the run's actual width -- the
231
+ * substitute font is never metrically identical to the embedded one, so the
232
+ * measurements are only useful as *proportions*.
233
+ *
234
+ * Walking along `readDirX`/`readDirY` rather than assuming left-to-right is what
235
+ * makes rotated, bottom-to-top and right-to-left runs come out correctly.
236
+ */
237
+ /** Widen each word slightly so glyph overhang is not clipped. */
238
+ const WORD_PADDING_RATIO = .1;
239
+ let measureCtx = null;
240
+ function measurementContext() {
241
+ if (measureCtx) return measureCtx;
242
+ try {
243
+ measureCtx = context2d(createCanvas(1, 1));
244
+ return measureCtx;
245
+ } catch {
246
+ return null;
247
+ }
248
+ }
249
+ /**
250
+ * Rebuild a CSS font string from a pdf.js font name.
251
+ *
252
+ * Names look like `AAAAAA+Helvetica-BoldOblique`: a six-letter subset prefix,
253
+ * then the real family and style suffixes.
254
+ */
255
+ function cssFontFromPdfName(fontName, size) {
256
+ const lower = fontName.replace(/^[A-Z]{6}\+/, "").toLowerCase();
257
+ const weight = /bold|black|heavy|semibold/.test(lower) ? "bold" : "normal";
258
+ const style = /italic|oblique/.test(lower) ? "italic" : "normal";
259
+ const family = /serif|times|georgia|garamond|roman/.test(lower) ? "serif" : /mono|courier|consol/.test(lower) ? "monospace" : "sans-serif";
260
+ return `${style} ${weight} ${Math.max(1, Math.round(size))}px ${family}`;
261
+ }
262
+ /**
263
+ * Relative advance width per character, used when no canvas is available.
264
+ * Buckets rather than real metrics -- enough to keep proportions sane.
265
+ */
266
+ function relativeCharWidth(char) {
267
+ if ("iljI|.,:;'`!".includes(char)) return .6;
268
+ if ("ftr()[]{}-".includes(char)) return .8;
269
+ if ("MWmw@%".includes(char)) return 1.6;
270
+ if (char === " ") return .6;
271
+ if (char >= "A" && char <= "Z") return 1.3;
272
+ return 1;
273
+ }
274
+ function measureWord(word, font) {
275
+ const ctx = measurementContext();
276
+ if (ctx) {
277
+ ctx.font = font;
278
+ return ctx.measureText(word).width;
279
+ }
280
+ let total = 0;
281
+ for (const char of word) total += relativeCharWidth(char);
282
+ return total;
283
+ }
284
+ /**
285
+ * Split one run into word boxes.
286
+ *
287
+ * Returns the run itself when it holds a single word, so the common case costs
288
+ * nothing.
289
+ */
290
+ function splitRunIntoWords(run) {
291
+ const trimmed = run.text.trim();
292
+ if (trimmed.length === 0) return [];
293
+ const tokens = trimmed.split(/(\s+)/).filter((t) => t.length > 0);
294
+ if (tokens.filter((t) => !/^\s+$/.test(t)).length <= 1) return [{
295
+ ...run,
296
+ text: trimmed
297
+ }];
298
+ const font = cssFontFromPdfName(run.fontName, run.height);
299
+ const measured = tokens.map((token) => measureWord(token, font));
300
+ const totalMeasured = measured.reduce((sum, w) => sum + w, 0) || 1;
301
+ const scale = (Math.hypot(run.width * run.readDirX, run.height * run.readDirY) || run.width) / totalMeasured;
302
+ let cursorX = run.readDirX >= 0 ? run.x : run.x + run.width;
303
+ let cursorY = run.readDirY >= 0 ? run.y : run.y + run.height;
304
+ const out = [];
305
+ for (const [i, token] of tokens.entries()) {
306
+ const advance = measured[i] * scale;
307
+ if (!/^\s+$/.test(token)) {
308
+ const pad = run.height * WORD_PADDING_RATIO;
309
+ const spanX = Math.abs(run.readDirX) > Math.abs(run.readDirY) ? advance : run.width;
310
+ const spanY = Math.abs(run.readDirY) > Math.abs(run.readDirX) ? advance : run.height;
311
+ const left = run.readDirX >= 0 ? cursorX : cursorX - spanX;
312
+ const top = run.readDirY >= 0 ? cursorY : cursorY - spanY;
313
+ out.push({
314
+ text: token,
315
+ score: run.score,
316
+ x: Math.floor(left - pad),
317
+ y: Math.floor(top - pad),
318
+ width: Math.max(1, Math.ceil(spanX + pad * 2)),
319
+ height: Math.max(1, Math.ceil(spanY + pad * 2)),
320
+ angle: run.angle
321
+ });
322
+ }
323
+ cursorX += run.readDirX * advance;
324
+ cursorY += run.readDirY * advance;
325
+ }
326
+ return out;
327
+ }
328
+ /** Split every run on a page into word boxes. */
329
+ function splitRunsIntoWords(runs) {
330
+ return runs.flatMap(splitRunIntoWords);
331
+ }
332
+ /** Reset the cached measurement canvas. Tests only. */
333
+ function resetMeasurementContext() {
334
+ measureCtx = null;
335
+ }
336
+ //#endregion
337
+ //#region src/pdf/pdf-to-document.ts
338
+ /**
339
+ * Port of `scaledp/pdf/PdfDataToText.py`: a PDF's embedded text layer into one
340
+ * `Document` row per page, with word-level boxes.
341
+ *
342
+ * Coordinates are emitted in the same pixel space `PdfToImage` renders at, so
343
+ * boxes from this stage and boxes from OCR are directly comparable. Python
344
+ * leaves PdfDataToText in PDF points and scales only in PdfDataToDocument; a
345
+ * single consistent space is more useful and avoids a class of silent mismatch.
346
+ *
347
+ * The output feeds the `bypassCol` optimisation: a page that already has a text
348
+ * layer does not need OCR.
349
+ */
350
+ const PDF_TO_DOCUMENT_DEFAULTS = Object.freeze({
351
+ ...BASE_STAGE_DEFAULTS,
352
+ inputCol: "content",
353
+ outputCol: "document",
354
+ keepInputData: true,
355
+ resolution: 300,
356
+ pageLimit: 0,
357
+ splitWords: true
358
+ });
359
+ var PdfToDocument = class extends Stage {
360
+ name = "PdfToDocument";
361
+ constructor(options = {}) {
362
+ super(resolveParams(PDF_TO_DOCUMENT_DEFAULTS, options));
363
+ }
364
+ async expand(input, row, ctx) {
365
+ const { outputCol, pageCol, pathCol, resolution, pageLimit, splitWords } = this.params;
366
+ const path = String(row[pathCol] ?? "memory");
367
+ const task = (await loadPdfjs()).getDocument(documentOptions(toBytes(input)));
368
+ try {
369
+ const pdf = await task.promise;
370
+ const pageCount = pageLimit > 0 ? Math.min(pageLimit, pdf.numPages) : pdf.numPages;
371
+ const rows = [];
372
+ for (let index = 0; index < pageCount; index++) {
373
+ ctx.signal?.throwIfAborted();
374
+ const page = await pdf.getPage(index + 1);
375
+ try {
376
+ const runs = await extractTextBoxes(page, page.getViewport({ scale: resolution / 72 }));
377
+ const bboxes = splitWords ? splitRunsIntoWords(runs) : runs;
378
+ rows.push({
379
+ ...row,
380
+ [pageCol]: index,
381
+ [outputCol]: createDocument({
382
+ path,
383
+ type: "pdf",
384
+ text: runs.map((r) => r.text).join("\n"),
385
+ bboxes
386
+ })
387
+ });
388
+ } finally {
389
+ page.cleanup();
390
+ }
391
+ }
392
+ return rows;
393
+ } catch (error) {
394
+ throw describePdfError(error);
395
+ } finally {
396
+ await task.destroy();
397
+ }
398
+ }
399
+ async apply() {
400
+ throw new ImageError("unreachable: expand handles every row", this.name);
401
+ }
402
+ onError(message, row) {
403
+ return createDocument({
404
+ path: String(row[this.params.pathCol] ?? "memory"),
405
+ type: "pdf",
406
+ exception: message
407
+ });
408
+ }
409
+ };
410
+ /** True when a page's text layer is substantive enough to skip OCR. */
411
+ function hasUsableTextLayer(document, minimumBoxes = 1) {
412
+ return document.exception === "" && document.bboxes.length >= minimumBoxes;
413
+ }
414
+ //#endregion
415
+ export { extractTextBoxes as _, relativeCharWidth as a, splitRunsIntoWords as c, PdfToImage as d, renderPage as f, TEXT_LAYER_SCORE as g, resetPdfjs as h, cssFontFromPdfName as i, PDF_TO_IMAGE_DEFAULTS as l, loadPdfjs as m, PdfToDocument as n, resetMeasurementContext as o, documentOptions as p, hasUsableTextLayer as r, splitRunIntoWords as s, PDF_TO_DOCUMENT_DEFAULTS as t, POINTS_PER_INCH as u, isTextItem as v, textItemToBox as y };
416
+
417
+ //# sourceMappingURL=pdf-BQl0dneD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-BQl0dneD.js","names":[],"sources":["../src/pdf/extract-text.ts","../src/pdf/pdfjs.ts","../src/pdf/pdf-to-image.ts","../src/pdf/split-words.ts","../src/pdf/pdf-to-document.ts"],"sourcesContent":["/**\n * Word-level text extraction from a PDF's embedded text layer.\n *\n * Ported from the pdftools prototype's `extractTextFromPage`, whose rotation\n * handling is the hard-won part and is reproduced here with its reasoning.\n *\n * A pdf.js text item carries a full transform matrix [a, b, c, d, e, f]. Naively\n * reading `a` as a width scale breaks on rotated text, so the reading direction\n * and the \"up\" direction are recovered as *unit* vectors -- otherwise the font\n * size gets counted twice. The four corners are then pushed through\n * `convertToViewportPoint` and reduced to an axis-aligned box.\n *\n * No semantic `angle` is emitted, deliberately. A rotated glyph matrix is\n * indistinguishable from page-level /Rotate compensation or an embedded\n * FontMatrix, so composing the corners geometrically and taking their bounding\n * box is correct regardless of which caused it. `readDirX`/`readDirY` carry the\n * direction that word-splitting needs.\n */\n\nimport type { Box } from '../schemas/box.js'\n\n/** A box plus the reading direction, which splitting needs but ScaleDP's Box lacks. */\nexport interface TextBox extends Box {\n /** Unit vector, in viewport space, along which reading progresses. */\n readDirX: number\n readDirY: number\n /** pdf.js font identifier, e.g. 'g_d0_f1'. */\n fontName: string\n}\n\n/** Glyphs sit above the baseline by roughly three quarters of the line height. */\nconst ASCENT_RATIO = 0.75\n\n/** Confidence assigned to text read from a PDF's own text layer. */\nexport const TEXT_LAYER_SCORE = 0.99\n\ninterface TextItemLike {\n str: string\n transform: number[]\n width: number\n height: number\n fontName?: string\n}\n\ninterface ViewportLike {\n convertToViewportPoint(x: number, y: number): number[]\n}\n\nexport function isTextItem(item: unknown): item is TextItemLike {\n return (\n typeof item === 'object' &&\n item !== null &&\n 'str' in item &&\n 'transform' in item &&\n Array.isArray((item as TextItemLike).transform)\n )\n}\n\n/** Convert one pdf.js text item into a viewport-space box. */\nexport function textItemToBox(item: TextItemLike, viewport: ViewportLike): TextBox | null {\n if (item.str.length === 0) return null\n\n const [a = 0, b = 0, c = 0, d = 0, e = 0, f = 0] = item.transform\n\n // Unit direction vectors. Dividing out the magnitudes is what stops the\n // font size being applied twice, since item.width/height already include it.\n const abMag = Math.hypot(a, b) || 1\n const cdMag = Math.hypot(c, d) || 1\n const dirX = a / abMag\n const dirY = b / abMag\n const upX = c / cdMag\n const upY = d / cdMag\n\n // The transform's origin is the baseline; shift up to the glyph tops.\n const ascent = item.height * ASCENT_RATIO\n const startX = e + upX * ascent\n const startY = f + upY * ascent\n\n const corners = [\n viewport.convertToViewportPoint(startX, startY),\n viewport.convertToViewportPoint(startX + dirX * item.width, startY + dirY * item.width),\n viewport.convertToViewportPoint(startX - upX * item.height, startY - upY * item.height),\n viewport.convertToViewportPoint(\n startX + dirX * item.width - upX * item.height,\n startY + dirY * item.width - upY * item.height\n ),\n ]\n\n const xs = corners.map((p) => p[0] as number)\n const ys = corners.map((p) => p[1] as number)\n const x = Math.min(...xs)\n const y = Math.min(...ys)\n\n // Reading direction in viewport space, taken from the start and end points\n // rather than from the matrix, so the viewport's own flip is accounted for.\n const [startScreenX = 0, startScreenY = 0] = corners[0] ?? []\n const [endScreenX = 0, endScreenY = 0] = corners[1] ?? []\n const readMag = Math.hypot(endScreenX - startScreenX, endScreenY - startScreenY) || 1\n\n return {\n text: item.str,\n score: TEXT_LAYER_SCORE,\n x: Math.floor(x),\n y: Math.floor(y),\n width: Math.max(1, Math.ceil(Math.max(...xs) - x)),\n height: Math.max(1, Math.ceil(Math.max(...ys) - y)),\n angle: 0,\n readDirX: (endScreenX - startScreenX) / readMag,\n readDirY: (endScreenY - startScreenY) / readMag,\n fontName: item.fontName ?? '',\n }\n}\n\n/** Extract every text item on a page as a viewport-space box. */\nexport async function extractTextBoxes(\n page: { getTextContent(): Promise<{ items: unknown[] }> },\n viewport: ViewportLike\n): Promise<TextBox[]> {\n const content = await page.getTextContent()\n const boxes: TextBox[] = []\n for (const item of content.items) {\n if (!isTextItem(item)) continue\n const box = textItemToBox(item, viewport)\n if (box) boxes.push(box)\n }\n return boxes\n}\n","/**\n * Lazy pdf.js loader.\n *\n * pdfjs-dist is an optional peer dependency, so it is imported only when a PDF\n * stage actually runs. Every asset path comes from `configure()` -- unlike the\n * pdftools prototype, which hardcoded `/pdf.worker.min.mjs`, a path only its\n * own Next app could serve.\n */\n\nimport { getConfig } from '../core/config.js'\n\ntype PdfjsModule = typeof import('pdfjs-dist')\n\nlet modulePromise: Promise<PdfjsModule> | null = null\n\nexport async function loadPdfjs(): Promise<PdfjsModule> {\n if (modulePromise) return modulePromise\n\n modulePromise = (async () => {\n let pdfjs: PdfjsModule\n try {\n pdfjs = await import('pdfjs-dist')\n } catch (cause) {\n throw new Error('pdfjs-dist is required for PDF support. Install it: npm i pdfjs-dist', { cause })\n }\n\n const { workerSrc } = getConfig().pdf\n if (workerSrc) pdfjs.GlobalWorkerOptions.workerSrc = workerSrc\n return pdfjs\n })()\n\n return modulePromise\n}\n\n/** Reset the cached module. Tests only. */\nexport function resetPdfjs(): void {\n modulePromise = null\n}\n\n/**\n * Turn pdf.js's worker-setup failure into something actionable.\n *\n * When `workerSrc` is unset or 404s, pdf.js reports \"Setting up fake worker\n * failed\" with a bare module URL, which says nothing about what to do. The\n * worker is not bundled with this library on purpose -- it has to be served by\n * the consuming application -- so the fix is always the same two steps.\n */\nexport function describePdfError(error: unknown): Error {\n const message = error instanceof Error ? error.message : String(error)\n if (!/fake worker|worker/i.test(message)) {\n return error instanceof Error ? error : new Error(message)\n }\n\n const { workerSrc } = getConfig().pdf\n const cause = workerSrc\n ? `pdf.js could not load its worker from \"${workerSrc}\".`\n : 'pdf.js has no worker configured.'\n\n return new Error(\n `${cause}\\n` +\n 'Copy it out of the package and point the config at it:\\n' +\n ' cp node_modules/pdfjs-dist/build/pdf.worker.min.mjs public/\\n' +\n \" configure({ pdf: { workerSrc: '/pdf.worker.min.mjs' } })\\n\" +\n `Original error: ${message}`,\n { cause: error }\n )\n}\n\n/** Document-level options assembled from the global config. */\nexport function documentOptions(data: Uint8Array): Record<string, unknown> {\n const { cMapUrl, standardFontDataUrl, wasmUrl } = getConfig().pdf\n // pdf.js takes ownership of the buffer it is given and detaches it, so hand\n // over a copy: callers routinely reuse the row's `content` afterwards.\n const owned = new Uint8Array(data.byteLength)\n owned.set(data)\n\n const options: Record<string, unknown> = { data: owned }\n if (cMapUrl) {\n options.cMapUrl = cMapUrl\n options.cMapPacked = true\n }\n if (standardFontDataUrl) options.standardFontDataUrl = standardFontDataUrl\n if (wasmUrl) options.wasmUrl = wasmUrl\n return options\n}\n","/**\n * Port of `scaledp/pdf/PdfDataToImage.py`: a PDF into one `Image` row per page.\n *\n * Python renders with PyMuPDF at a DPI; pdf.js works in scale factors, so the\n * DPI converts through the PDF unit of 72 points per inch.\n */\n\nimport { ImageError } from '../core/errors.js'\nimport { createCanvas, encodeImage } from '../core/image.js'\nimport { BASE_STAGE_DEFAULTS, type BaseStageParams, resolveParams } from '../core/params.js'\nimport { type Row, Stage, type StageContext } from '../core/pipeline.js'\nimport { createImage, type ImageFormat, type ScaleDpImage } from '../schemas/image.js'\nimport { toBytes } from '../stages/data-to-image.js'\nimport { describePdfError, documentOptions, loadPdfjs } from './pdfjs.js'\n\n/** PDF user space is defined in points; 72 of them make an inch. */\nexport const POINTS_PER_INCH = 72\n\nexport interface PdfToImageParams extends BaseStageParams {\n /** Render DPI. 300 matches ScaleDP's default and suits OCR. */\n resolution: number\n /** Maximum pages to render; 0 renders all of them. */\n pageLimit: number\n imageType: ImageFormat\n}\n\nexport const PDF_TO_IMAGE_DEFAULTS: PdfToImageParams = Object.freeze({\n ...BASE_STAGE_DEFAULTS,\n inputCol: 'content',\n outputCol: 'image',\n resolution: 300,\n pageLimit: 0,\n imageType: 'png' as ImageFormat,\n})\n\nconst MIME: Record<ImageFormat, 'image/png' | 'image/webp' | 'image/jpeg'> = {\n png: 'image/png',\n webp: 'image/webp',\n jpeg: 'image/jpeg',\n}\n\nexport class PdfToImage extends Stage<PdfToImageParams> {\n readonly name = 'PdfToImage'\n\n constructor(options: Partial<PdfToImageParams> = {}) {\n super(\n resolveParams(PDF_TO_IMAGE_DEFAULTS, options, {\n resolution: (value) => {\n if (!Number.isFinite(value) || value <= 0) {\n throw new RangeError(`resolution must be positive, received ${value}`)\n }\n },\n pageLimit: (value) => {\n if (!Number.isInteger(value) || value < 0) {\n throw new RangeError(`pageLimit must be a non-negative integer, received ${value}`)\n }\n },\n })\n )\n }\n\n /** One input PDF becomes N rows, each carrying its page index. */\n protected override async expand(input: unknown, row: Row, ctx: StageContext): Promise<Row[]> {\n const { outputCol, pageCol, pathCol, resolution, pageLimit, imageType } = this.params\n const path = String(row[pathCol] ?? 'memory')\n\n const pdfjs = await loadPdfjs()\n const task = pdfjs.getDocument(documentOptions(toBytes(input)))\n\n try {\n // pdf.js defers worker setup, so a missing worker surfaces on first\n // page access rather than from task.promise. Wrap the whole\n // operation so the failure is described wherever it lands.\n const document = await task.promise\n const pageCount = pageLimit > 0 ? Math.min(pageLimit, document.numPages) : document.numPages\n const rows: Row[] = []\n\n for (let index = 0; index < pageCount; index++) {\n ctx.signal?.throwIfAborted()\n const image = await renderPage(document, index + 1, {\n resolution,\n imageType,\n path,\n })\n rows.push({ ...row, [pageCol]: index, [outputCol]: image })\n }\n return rows\n } catch (error) {\n throw describePdfError(error)\n } finally {\n // destroy() lives on the loading task, not the document proxy, and\n // is what releases the pdf.js worker's copy of the file.\n await task.destroy()\n }\n }\n\n protected async apply(): Promise<never> {\n throw new ImageError('unreachable: expand handles every row', this.name)\n }\n\n protected onError(message: string, row: Row): ScaleDpImage {\n return createImage({ path: String(row[this.params.pathCol] ?? 'memory'), exception: message })\n }\n}\n\n/** Rasterise a single 1-based page to encoded image bytes. */\nexport async function renderPage(\n document: Awaited<ReturnType<typeof import('pdfjs-dist').getDocument>['promise']>,\n pageNumber: number,\n opts: { resolution: number; imageType: ImageFormat; path: string }\n): Promise<ScaleDpImage> {\n const page = await document.getPage(pageNumber)\n try {\n const viewport = page.getViewport({ scale: opts.resolution / POINTS_PER_INCH })\n const canvas = createCanvas(viewport.width, viewport.height)\n\n // pdf.js >= 5 takes `canvas`, not `canvasContext`. Passing the context\n // still works but is the documented legacy path.\n await page.render({\n canvas: canvas as unknown as HTMLCanvasElement,\n viewport,\n }).promise\n\n return createImage({\n path: opts.path,\n resolution: opts.resolution,\n data: await encodeImage(canvas, MIME[opts.imageType]),\n imageType: opts.imageType,\n width: canvas.width,\n height: canvas.height,\n })\n } finally {\n page.cleanup()\n }\n}\n","/**\n * Split a pdf.js text run into word-level boxes.\n *\n * pdf.js emits runs at line granularity (\"Client: Raja Raman\"), but detection\n * boxes and NER offsets both want words. Each word is measured with Canvas 2D\n * `measureText` using a font reconstructed from the pdf.js font name, then the\n * measured widths are scaled so they sum to the run's actual width -- the\n * substitute font is never metrically identical to the embedded one, so the\n * measurements are only useful as *proportions*.\n *\n * Walking along `readDirX`/`readDirY` rather than assuming left-to-right is what\n * makes rotated, bottom-to-top and right-to-left runs come out correctly.\n */\n\nimport { context2d, createCanvas } from '../core/image.js'\nimport type { Box } from '../schemas/box.js'\nimport type { TextBox } from './extract-text.js'\n\n/** Widen each word slightly so glyph overhang is not clipped. */\nconst WORD_PADDING_RATIO = 0.1\n\nlet measureCtx: OffscreenCanvasRenderingContext2D | null = null\n\nfunction measurementContext(): OffscreenCanvasRenderingContext2D | null {\n if (measureCtx) return measureCtx\n try {\n measureCtx = context2d(createCanvas(1, 1))\n return measureCtx\n } catch {\n // No canvas (e.g. a non-browser test run): fall back to glyph heuristics.\n return null\n }\n}\n\n/**\n * Rebuild a CSS font string from a pdf.js font name.\n *\n * Names look like `AAAAAA+Helvetica-BoldOblique`: a six-letter subset prefix,\n * then the real family and style suffixes.\n */\nexport function cssFontFromPdfName(fontName: string, size: number): string {\n const name = fontName.replace(/^[A-Z]{6}\\+/, '')\n const lower = name.toLowerCase()\n const weight = /bold|black|heavy|semibold/.test(lower) ? 'bold' : 'normal'\n const style = /italic|oblique/.test(lower) ? 'italic' : 'normal'\n const family = /serif|times|georgia|garamond|roman/.test(lower)\n ? 'serif'\n : /mono|courier|consol/.test(lower)\n ? 'monospace'\n : 'sans-serif'\n return `${style} ${weight} ${Math.max(1, Math.round(size))}px ${family}`\n}\n\n/**\n * Relative advance width per character, used when no canvas is available.\n * Buckets rather than real metrics -- enough to keep proportions sane.\n */\nexport function relativeCharWidth(char: string): number {\n if (\"iljI|.,:;'`!\".includes(char)) return 0.6\n if ('ftr()[]{}-'.includes(char)) return 0.8\n if ('MWmw@%'.includes(char)) return 1.6\n if (char === ' ') return 0.6\n if (char >= 'A' && char <= 'Z') return 1.3\n return 1.0\n}\n\nfunction measureWord(word: string, font: string): number {\n const ctx = measurementContext()\n if (ctx) {\n ctx.font = font\n return ctx.measureText(word).width\n }\n let total = 0\n for (const char of word) total += relativeCharWidth(char)\n return total\n}\n\n/**\n * Split one run into word boxes.\n *\n * Returns the run itself when it holds a single word, so the common case costs\n * nothing.\n */\nexport function splitRunIntoWords(run: TextBox): Box[] {\n const trimmed = run.text.trim()\n if (trimmed.length === 0) return []\n\n const tokens = trimmed.split(/(\\s+)/).filter((t) => t.length > 0)\n const words = tokens.filter((t) => !/^\\s+$/.test(t))\n if (words.length <= 1) {\n return [{ ...run, text: trimmed }]\n }\n\n const font = cssFontFromPdfName(run.fontName, run.height)\n const measured = tokens.map((token) => measureWord(token, font))\n const totalMeasured = measured.reduce((sum, w) => sum + w, 0) || 1\n\n // The substitute font's absolute metrics are meaningless; only the ratios\n // matter, so normalise them onto the run's real extent.\n const runLength = Math.hypot(run.width * run.readDirX, run.height * run.readDirY) || run.width\n const scale = runLength / totalMeasured\n\n // Walking starts at whichever corner the reading direction comes *from*, so\n // a right-to-left or bottom-to-top run starts at the opposite edge.\n let cursorX = run.readDirX >= 0 ? run.x : run.x + run.width\n let cursorY = run.readDirY >= 0 ? run.y : run.y + run.height\n\n const out: Box[] = []\n for (const [i, token] of tokens.entries()) {\n const advance = (measured[i] as number) * scale\n if (!/^\\s+$/.test(token)) {\n const pad = run.height * WORD_PADDING_RATIO\n const spanX = Math.abs(run.readDirX) > Math.abs(run.readDirY) ? advance : run.width\n const spanY = Math.abs(run.readDirY) > Math.abs(run.readDirX) ? advance : run.height\n\n const left = run.readDirX >= 0 ? cursorX : cursorX - spanX\n const top = run.readDirY >= 0 ? cursorY : cursorY - spanY\n\n out.push({\n text: token,\n score: run.score,\n x: Math.floor(left - pad),\n y: Math.floor(top - pad),\n width: Math.max(1, Math.ceil(spanX + pad * 2)),\n height: Math.max(1, Math.ceil(spanY + pad * 2)),\n angle: run.angle,\n })\n }\n cursorX += run.readDirX * advance\n cursorY += run.readDirY * advance\n }\n return out\n}\n\n/** Split every run on a page into word boxes. */\nexport function splitRunsIntoWords(runs: readonly TextBox[]): Box[] {\n return runs.flatMap(splitRunIntoWords)\n}\n\n/** Reset the cached measurement canvas. Tests only. */\nexport function resetMeasurementContext(): void {\n measureCtx = null\n}\n","/**\n * Port of `scaledp/pdf/PdfDataToText.py`: a PDF's embedded text layer into one\n * `Document` row per page, with word-level boxes.\n *\n * Coordinates are emitted in the same pixel space `PdfToImage` renders at, so\n * boxes from this stage and boxes from OCR are directly comparable. Python\n * leaves PdfDataToText in PDF points and scales only in PdfDataToDocument; a\n * single consistent space is more useful and avoids a class of silent mismatch.\n *\n * The output feeds the `bypassCol` optimisation: a page that already has a text\n * layer does not need OCR.\n */\n\nimport { ImageError } from '../core/errors.js'\nimport { BASE_STAGE_DEFAULTS, type BaseStageParams, resolveParams } from '../core/params.js'\nimport { type Row, Stage, type StageContext } from '../core/pipeline.js'\nimport { createDocument, type Document } from '../schemas/document.js'\nimport { toBytes } from '../stages/data-to-image.js'\nimport { extractTextBoxes } from './extract-text.js'\nimport { POINTS_PER_INCH } from './pdf-to-image.js'\nimport { describePdfError, documentOptions, loadPdfjs } from './pdfjs.js'\nimport { splitRunsIntoWords } from './split-words.js'\n\nexport interface PdfToDocumentParams extends BaseStageParams {\n /** Pixel space the boxes are expressed in; match PdfToImage to align them. */\n resolution: number\n pageLimit: number\n /** Split pdf.js line runs into word boxes. Off yields run-level boxes. */\n splitWords: boolean\n}\n\nexport const PDF_TO_DOCUMENT_DEFAULTS: PdfToDocumentParams = Object.freeze({\n ...BASE_STAGE_DEFAULTS,\n inputCol: 'content',\n outputCol: 'document',\n keepInputData: true,\n resolution: 300,\n pageLimit: 0,\n splitWords: true,\n})\n\nexport class PdfToDocument extends Stage<PdfToDocumentParams> {\n readonly name = 'PdfToDocument'\n\n constructor(options: Partial<PdfToDocumentParams> = {}) {\n super(resolveParams(PDF_TO_DOCUMENT_DEFAULTS, options))\n }\n\n protected override async expand(input: unknown, row: Row, ctx: StageContext): Promise<Row[]> {\n const { outputCol, pageCol, pathCol, resolution, pageLimit, splitWords } = this.params\n const path = String(row[pathCol] ?? 'memory')\n\n const pdfjs = await loadPdfjs()\n const task = pdfjs.getDocument(documentOptions(toBytes(input)))\n\n try {\n // pdf.js defers worker setup, so a missing worker surfaces on first\n // page access rather than from task.promise.\n const pdf = await task.promise\n const pageCount = pageLimit > 0 ? Math.min(pageLimit, pdf.numPages) : pdf.numPages\n const rows: Row[] = []\n\n for (let index = 0; index < pageCount; index++) {\n ctx.signal?.throwIfAborted()\n const page = await pdf.getPage(index + 1)\n try {\n const viewport = page.getViewport({ scale: resolution / POINTS_PER_INCH })\n const runs = await extractTextBoxes(page, viewport)\n const bboxes = splitWords ? splitRunsIntoWords(runs) : runs\n\n rows.push({\n ...row,\n [pageCol]: index,\n [outputCol]: createDocument({\n path,\n type: 'pdf',\n text: runs.map((r) => r.text).join('\\n'),\n bboxes,\n }),\n })\n } finally {\n page.cleanup()\n }\n }\n return rows\n } catch (error) {\n throw describePdfError(error)\n } finally {\n await task.destroy()\n }\n }\n\n protected async apply(): Promise<never> {\n throw new ImageError('unreachable: expand handles every row', this.name)\n }\n\n protected onError(message: string, row: Row): Document {\n return createDocument({\n path: String(row[this.params.pathCol] ?? 'memory'),\n type: 'pdf',\n exception: message,\n })\n }\n}\n\n/** True when a page's text layer is substantive enough to skip OCR. */\nexport function hasUsableTextLayer(document: Document, minimumBoxes = 1): boolean {\n return document.exception === '' && document.bboxes.length >= minimumBoxes\n}\n"],"mappings":"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"}