extract-pdf 0.1.260 → 0.1.261
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.
- package/README.md +88 -1
- package/dist/docling-ocr.d.ts +60 -0
- package/dist/ocr-page-scan.d.ts +56 -0
- package/dist/pdf-to-html.cjs.js +1 -1
- package/dist/pdf-to-html.d.ts +42 -2
- package/dist/pdf-to-html.es.js +1 -1
- package/dist/utils/load-pdfjs.d.ts +19 -0
- package/package.json +18 -6
- package/server/model.js +96 -0
- package/server/routes.js +91 -0
- package/server/schemas.js +91 -0
- package/server/server.js +180 -0
- package/server/wrangler.jsonc +18 -0
- package/src/docling-ocr.ts +271 -0
- package/src/ocr-page-scan.ts +141 -0
- package/src/pdf-to-html.ts +121 -9
- package/src/utils/load-pdfjs.ts +32 -0
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
<a href="https://codespaces.new/vtempest/qwksearch-research-agent">
|
|
26
26
|
<img src="https://github.com/codespaces/badge.svg" width="150" height="20" />
|
|
27
27
|
</a>
|
|
28
|
+
<a href="https://codecov.io/gh/OpenSourceAGI/qwksearch-research-agent"><img src="https://codecov.io/gh/OpenSourceAGI/qwksearch-research-agent/graph/badge.svg?component=package-extract-pdf" alt="Coverage" /></a>
|
|
28
29
|
</p>
|
|
29
30
|
|
|
30
31
|
# extract-pdf
|
|
@@ -34,6 +35,10 @@
|
|
|
34
35
|
|
|
35
36
|
Instant no-backend-needed javascript to convert a PDF (URL or `ArrayBuffer`) into clean HTML with structural tagging — headings, lists, footnotes, code blocks, bold/italic, and Table of Contents entries. Works in Node.js, Cloudflare Workers, and browser environments via [pdfjs-serverless](https://github.com/johannschopplich/pdfjs-serverless).
|
|
36
37
|
|
|
38
|
+
**Slim by default:** the package bundles no PDF engine. PDF.js is loaded lazily at runtime from jsDelivr's ESM build of `pdfjs-serverless` (`https://cdn.jsdelivr.net/npm/pdfjs-serverless@1/+esm`, pinned to the major version) — a zero-dependency, single ~1.6 MB minified redistribution of Mozilla PDF.js built for serverless/edge runtimes. Node.js and Bun can't import remote URLs, so there the loader falls back to the locally-installed `pdfjs-serverless` optional dependency. Everything OCR-related (Granite Docling via `@huggingface/transformers`, `@napi-rs/canvas`) is likewise optional and imported only when actually used.
|
|
39
|
+
|
|
40
|
+
For OCR-grade fidelity on pages with infographics, charts, and tables, the package also ships the Granite Docling path (merged from the former `extract-pdf-docling` package): run all pages through the frontend JS parser, all pages through Docling OCR, a **hybrid** mode that regex-scans pages and OCRs only the ones that need it, or point at the URL of a remote docling-compatible processor (see `server/`).
|
|
41
|
+
|
|
37
42
|
## Install
|
|
38
43
|
|
|
39
44
|
```sh
|
|
@@ -60,11 +65,16 @@ const { html } = await convertPDFToHTML(buffer, { addPageNumbers: true });
|
|
|
60
65
|
| `addCitation` | `true` | Reads PDF metadata and first-page heading to populate `title`/`author` in the return value |
|
|
61
66
|
| `method` | `"ts-block-algorithm"` | Parsing engine — `"ts-block-algorithm"`, `"liteparse"`, or `"liteparse-wasm"` (see below) |
|
|
62
67
|
| `liteParseOptions`| `{}` | Passed through to LiteParse's constructor when `method` is `"liteparse"` or `"liteparse-wasm"` |
|
|
68
|
+
| `processor` | `"frontend"` | Where OCR happens — `"frontend"`, `"hybrid"`, `"docling"`, or a processor URL (see below) |
|
|
69
|
+
| `processorUrl` | — | Remote docling-compatible API used by `"hybrid"`/`"docling"` instead of the in-process model |
|
|
70
|
+
| `ocrScanOptions` | `{}` | Threshold tuning for the hybrid page scan (`scanPagesForOCR`) |
|
|
71
|
+
| `doclingOptions` | `{}` | `prompt`, `maxTokens`, `scale` for the Docling OCR model |
|
|
63
72
|
|
|
64
73
|
### Return value
|
|
65
74
|
|
|
66
75
|
```ts
|
|
67
|
-
{ html: string, title?: string, author?: string, format: "pdf"
|
|
76
|
+
{ html: string, title?: string, author?: string, format: "pdf",
|
|
77
|
+
processor: string, ocrScan: OcrScanResult }
|
|
68
78
|
```
|
|
69
79
|
|
|
70
80
|
## Parse methods
|
|
@@ -105,6 +115,76 @@ matching this package's "instant, no backend" philosophy. Use
|
|
|
105
115
|
`detectPdfNeedsOcr` (below) to decide when a document is worth re-parsing with
|
|
106
116
|
`liteParseOptions: { ocrEnabled: true }`.
|
|
107
117
|
|
|
118
|
+
## Processor modes: frontend, hybrid, Docling OCR, or a remote URL
|
|
119
|
+
|
|
120
|
+
Separately from `method`, the `processor` option decides whether pages go
|
|
121
|
+
through the [Granite Docling](https://huggingface.co/ibm-granite/granite-docling-258M)
|
|
122
|
+
OCR model — which preserves layout, tables, charts, code, and formulas at the
|
|
123
|
+
cost of model inference per page:
|
|
124
|
+
|
|
125
|
+
| `processor` | What happens |
|
|
126
|
+
| ----------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
127
|
+
| `"frontend"` (default) | All pages parsed by the pure-JS text-layer pipeline. No OCR, no model, instant. |
|
|
128
|
+
| `"hybrid"` | JS pipeline everywhere + a regex scan (`scanPagesForOCR`) flags pages with infographics/figures/tables or no usable text layer; only those pages are rasterized and OCR'd with Docling. |
|
|
129
|
+
| `"docling"` | Every page rasterized and OCR'd with Docling. |
|
|
130
|
+
| `"https://..."` (URL) | Like `"docling"`, but every page is POSTed to that docling-compatible processor API instead of running the model in-process. |
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// Hybrid: fast JS parse, OCR only the pages that need it
|
|
134
|
+
const { html, ocrScan } = await convertPDFToHTML(buffer, { processor: "hybrid" });
|
|
135
|
+
console.log(ocrScan.pagesNeedingOcr); // e.g. [3, 7] — pages with tables/figures
|
|
136
|
+
// each ocrScan.pages[i] lists reasons: "table-caption", "figure-caption",
|
|
137
|
+
// "table-rows", "numeric-grid", "no-text", "sparse-text", "garbled-text"
|
|
138
|
+
|
|
139
|
+
// All pages through a remote Docling processor (this package's server/):
|
|
140
|
+
const { html } = await convertPDFToHTML(buffer, {
|
|
141
|
+
processor: "http://localhost:3000",
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Hybrid with the OCR offloaded to a remote processor:
|
|
145
|
+
const { html } = await convertPDFToHTML(buffer, {
|
|
146
|
+
processor: "hybrid",
|
|
147
|
+
processorUrl: "http://localhost:3000",
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
OCR'd pages are emitted as `<section class="ocr-page" id="page-N">…</section>`
|
|
152
|
+
(tables become real `<table>` markup, captions `<figcaption>`, formulas
|
|
153
|
+
`<code class="formula">`, …); pages whose OCR fails keep their JS-parsed HTML.
|
|
154
|
+
|
|
155
|
+
Running Docling **in-process** requires the optional `@huggingface/transformers`
|
|
156
|
+
dependency (the ONNX model `onnx-community/granite-docling-258M-ONNX`, ~1 GB,
|
|
157
|
+
downloads on first use) and — in Node.js — `@napi-rs/canvas` for page
|
|
158
|
+
rasterization (browsers/Workers use `OffscreenCanvas`).
|
|
159
|
+
|
|
160
|
+
The regex scanner is also exported standalone, and so are the OCR helpers:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import { scanPagesForOCR, doctagsToHtml, ocrImageWithDocling } from "extract-pdf";
|
|
164
|
+
|
|
165
|
+
const scan = scanPagesForOCR(pageTexts); // string[] of per-page text
|
|
166
|
+
// { needsOcr, pagesNeedingOcr: number[], pages: [{ page, reasons, captions }] }
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Running the Docling processor server
|
|
170
|
+
|
|
171
|
+
The `server/` folder contains the Hono HTTP API (formerly the
|
|
172
|
+
`extract-pdf-docling` package) exposing the Granite Docling model:
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
bun run serve:docling # or dev:docling for --watch; port 3000
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- Swagger UI: `http://localhost:3000/docs`, spec at `/openapi.json`
|
|
179
|
+
- `POST /api/v1/convert` — `{ imageUrl, prompt?, maxTokens? }`
|
|
180
|
+
- `POST /api/v1/convert-base64` — `{ imageBase64, mimeType?, prompt?, maxTokens? }`
|
|
181
|
+
- `POST /api/v1/convert-stream` — SSE token stream
|
|
182
|
+
- `GET /health`
|
|
183
|
+
|
|
184
|
+
A `server/wrangler.jsonc` is included for deploying it as a Cloudflare Worker.
|
|
185
|
+
Any deployment of it (or any API with the same contract) can be passed as the
|
|
186
|
+
`processor`/`processorUrl` option above.
|
|
187
|
+
|
|
108
188
|
## Detecting whether a PDF needs OCR
|
|
109
189
|
|
|
110
190
|
Before committing to a full (and potentially slow) OCR parse, `detectPdfNeedsOcr`
|
|
@@ -163,6 +243,10 @@ src/
|
|
|
163
243
|
pdf-to-html.ts — main entry point (convertPDFToHTML)
|
|
164
244
|
liteparse-to-html.ts — "liteparse" method (native napi addon)
|
|
165
245
|
liteparse-wasm-to-html.ts — "liteparse-wasm" method (WASM, browser/edge)
|
|
246
|
+
ocr-page-scan.ts — regex scan flagging infographic/table pages for OCR
|
|
247
|
+
docling-ocr.ts — Granite Docling OCR (local ONNX or remote URL),
|
|
248
|
+
│ page rasterization, doctags → HTML
|
|
249
|
+
detect-needs-ocr.ts — LiteParse-based OCR routing (Node.js only)
|
|
166
250
|
models/ — data classes: Page, ParseResult, TextItem,
|
|
167
251
|
│ LineItem, LineItemBlock, Word, BlockType, …
|
|
168
252
|
transforms/
|
|
@@ -173,7 +257,10 @@ src/
|
|
|
173
257
|
│ to-text-blocks.ts
|
|
174
258
|
│ to-html.ts
|
|
175
259
|
utils/
|
|
260
|
+
load-pdfjs.ts — lazy CDN loader for pdfjs-serverless
|
|
176
261
|
string-functions.ts
|
|
177
262
|
page-item-functions.ts
|
|
178
263
|
page-number-functions.ts
|
|
264
|
+
server/ — Granite Docling HTTP processor (Hono):
|
|
265
|
+
server.js, routes.js, schemas.js, model.js, wrangler.jsonc
|
|
179
266
|
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview OCR of PDF pages with the Granite Docling vision model —
|
|
3
|
+
* either in-process (via the optional `@huggingface/transformers` dependency
|
|
4
|
+
* and the ONNX build of `ibm-granite/granite-docling-258M`) or by POSTing
|
|
5
|
+
* page images to a docling-compatible HTTP processor (the Hono service in
|
|
6
|
+
* this package's `server/` folder, or any other deployment of it).
|
|
7
|
+
*
|
|
8
|
+
* Everything heavy is imported lazily: requiring this module costs nothing
|
|
9
|
+
* until a page is actually OCR'd.
|
|
10
|
+
*/
|
|
11
|
+
/** Options accepted by the docling OCR helpers. */
|
|
12
|
+
export interface DoclingOcrOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Base URL of a remote docling-compatible processor
|
|
15
|
+
* (e.g. `"http://localhost:3000"`). When set, page images are POSTed to
|
|
16
|
+
* `{processorUrl}/api/v1/convert-base64` instead of running the model
|
|
17
|
+
* in-process.
|
|
18
|
+
*/
|
|
19
|
+
processorUrl?: string;
|
|
20
|
+
/** Instruction for the model. default="Convert this page to docling." */
|
|
21
|
+
prompt?: string;
|
|
22
|
+
/** Max tokens generated per page. default=4096 */
|
|
23
|
+
maxTokens?: number;
|
|
24
|
+
/** Rasterization scale for PDF pages (1 = 72 DPI). default=2 */
|
|
25
|
+
scale?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* OCRs one image with Granite Docling and returns the raw doctags output.
|
|
29
|
+
* Uses the remote processor when `processorUrl` is set, otherwise the local
|
|
30
|
+
* ONNX model.
|
|
31
|
+
*
|
|
32
|
+
* @param imageBase64 - Base64-encoded PNG of the page/image (no data: prefix)
|
|
33
|
+
* @category Extract
|
|
34
|
+
*/
|
|
35
|
+
export declare function ocrImageWithDocling(imageBase64: string, options?: DoclingOcrOptions): Promise<string>;
|
|
36
|
+
/**
|
|
37
|
+
* Rasterizes one PDF.js page object to a base64 PNG.
|
|
38
|
+
* @param page - A page from `pdfDocument.getPage(n)`
|
|
39
|
+
* @category Extract
|
|
40
|
+
*/
|
|
41
|
+
export declare function renderPdfPageToPngBase64(page: any, scale?: number): Promise<string>;
|
|
42
|
+
/**
|
|
43
|
+
* OCRs a set of pages from a loaded PDF.js document with Granite Docling.
|
|
44
|
+
*
|
|
45
|
+
* @param pdfDocument - Document from PDF.js `getDocument(...).promise`
|
|
46
|
+
* @param pageNumbers - 1-based page numbers to OCR
|
|
47
|
+
* @returns Map of page number → HTML (converted from the model's doctags);
|
|
48
|
+
* pages whose OCR failed are absent from the map.
|
|
49
|
+
* @category Extract
|
|
50
|
+
*/
|
|
51
|
+
export declare function ocrPdfPagesWithDocling(pdfDocument: any, pageNumbers: number[], options?: DoclingOcrOptions): Promise<Map<number, string>>;
|
|
52
|
+
/**
|
|
53
|
+
* Converts Granite Docling's doctags output to plain HTML: strips location
|
|
54
|
+
* tokens, maps doctags elements (section headers, text, lists, code,
|
|
55
|
+
* formulas, captions, OTSL tables) to their HTML equivalents, and drops page
|
|
56
|
+
* furniture (running headers/footers). Best-effort — unknown tags are
|
|
57
|
+
* removed, their text content kept.
|
|
58
|
+
* @category Extract
|
|
59
|
+
*/
|
|
60
|
+
export declare function doctagsToHtml(doctags: string): string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Dependency-free, regex-based scan of per-page PDF text that
|
|
3
|
+
* flags which pages contain infographics, figures, charts, or tables — the
|
|
4
|
+
* pages worth sending through a heavy OCR model (e.g. Granite Docling) instead
|
|
5
|
+
* of the fast text-layer pipeline. Complements `detectPdfNeedsOcr` (which uses
|
|
6
|
+
* LiteParse's native complexity analysis and is Node.js only): this scan runs
|
|
7
|
+
* anywhere on plain strings, so it also powers the `"hybrid"` processor mode
|
|
8
|
+
* of {@link convertPDFToHTML}.
|
|
9
|
+
*/
|
|
10
|
+
/** Per-page result of {@link scanPagesForOCR}. */
|
|
11
|
+
export interface PageOcrScan {
|
|
12
|
+
/** 1-based page number. */
|
|
13
|
+
page: number;
|
|
14
|
+
/** True when the page should be routed through OCR. */
|
|
15
|
+
needsOcr: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Why the page was flagged: `"no-text"`, `"sparse-text"`, `"garbled-text"`,
|
|
18
|
+
* `"figure-caption"`, `"table-caption"`, `"table-rows"`, `"numeric-grid"`.
|
|
19
|
+
* Empty when `needsOcr` is false.
|
|
20
|
+
*/
|
|
21
|
+
reasons: string[];
|
|
22
|
+
/** Caption strings matched on the page (e.g. `"Figure 3"`, `"Table 2"`). */
|
|
23
|
+
captions: string[];
|
|
24
|
+
}
|
|
25
|
+
/** Result of {@link scanPagesForOCR}. */
|
|
26
|
+
export interface OcrScanResult {
|
|
27
|
+
/** True when any page was flagged. */
|
|
28
|
+
needsOcr: boolean;
|
|
29
|
+
/** 1-based page numbers of every flagged page. */
|
|
30
|
+
pagesNeedingOcr: number[];
|
|
31
|
+
/** Per-page detail, one entry per page in input order. */
|
|
32
|
+
pages: PageOcrScan[];
|
|
33
|
+
}
|
|
34
|
+
/** Options for {@link scanPagesForOCR}. */
|
|
35
|
+
export interface ScanPagesForOCROptions {
|
|
36
|
+
/**
|
|
37
|
+
* Pages with fewer non-whitespace characters than this are flagged as
|
|
38
|
+
* `"sparse-text"` (likely a scanned image or a full-page graphic).
|
|
39
|
+
* default=200
|
|
40
|
+
*/
|
|
41
|
+
sparseTextThreshold?: number;
|
|
42
|
+
/** Minimum table-looking lines before flagging `"table-rows"`. default=3 */
|
|
43
|
+
minTableRows?: number;
|
|
44
|
+
/** Minimum numeric-run lines before flagging `"numeric-grid"`. default=2 */
|
|
45
|
+
minNumericRows?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Scans per-page extracted text with regex heuristics and reports which pages
|
|
49
|
+
* contain infographics/figures/tables (or have no usable text layer) and
|
|
50
|
+
* therefore need to be OCR'd for full fidelity.
|
|
51
|
+
*
|
|
52
|
+
* @param pageTexts - Extracted plain text of each page, in page order
|
|
53
|
+
* @param options - Threshold tuning, see {@link ScanPagesForOCROptions}
|
|
54
|
+
* @category Extract
|
|
55
|
+
*/
|
|
56
|
+
export declare function scanPagesForOCR(pageTexts: string[], options?: ScanPagesForOCROptions): OcrScanResult;
|
package/dist/pdf-to-html.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function t(t){for(;32===t.charCodeAt(0);)t=t.substring(1,t.length);return t}function e(t){for(;32===t.charCodeAt(t.length-1);)t=t.substring(0,t.length-1);return t}function s(t){return t>=48&&t<=57}function n(t){for(var e=0;e<t.length;e++)if(!s(t.charCodeAt(e)))return!1;return!0}function o(t,e){const s=e.charCodeAt(0);for(var n=0;n<t.length;n++)if(t.charCodeAt(n)!==s)return!1;return!0}function r(t){return function(t){const e=[];for(var s=0;s<t.length;s++)e.push(t.charCodeAt(s));return e}(t=t.toUpperCase()).filter(t=>32!==t&&9!==t&&46!==t)}function a(t){if(t.length>1)return!1;const e=t.charAt(0);return"-"===e||"•"===e||"–"===e}function i(t){return/^[\s]*[-•–][\s].*$/g.test(t)}function h(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function l(t,e){const s=new Set(t.toUpperCase().split(" ")),n=new Set(e.toUpperCase().split(" "));return new Set([...s].filter(t=>n.has(t))).size/Math.max(s.size,n.size)}var c=(t,e,s)=>Math.floor(t/e*s),m=(s,o,r)=>{for(const{str:a}of s){const s=e(t(a));n(s)&&(o[r]||(o[r]=[]),o[r].push(Number(s)))}return o},u=(t,e,s)=>{const n=c(1,6,s.length),o=c(5,6,s.length),r=m(s.slice(0,n),t,e);return m(s.slice(o),r,e)},p=t=>{let e=0;const s=Object.keys(t);if(0!==s.length&&1!==s.length)for(let n=0;n<s.length-1;n++){const o=t[s[n]],r=t[s[n+1]],a=e;for(let t=0;t<o.length&&e<2;t++)for(let a=0;a<r.length&&e<2;a++){const i=Number(s[n+1])-Number(s[n]);(o[t]+1===r[a]||i>1&&o[t]+i===r[a])&&e++}let i=n>0?Object.entries(t)[n-1]:Object.entries(t)[n];if(a===e)e=0,i=Object.entries(t)[n];else if(e>=2)return{pageIndex:Number(i[0]),pageNum:i[1][0]}}},d=(t,e)=>{const s={items:[...t.items]},n=c(1,6,s.items.length),o=c(5,6,s.items.length);return s.items=s.items.filter((t,r)=>{const a=r>0&&r<n,i=r>o&&r<s.items.length;return a||i?Number(t.str)!==Number(e):t}),s},f=class t{type;annotation;parsedElements;constructor(e){if(this.constructor===t)throw new TypeError("Can not construct abstract class.");this.type=e.type??null,this.annotation=e.annotation??null,this.parsedElements=e.parsedElements??null}},g=class extends f{x;y;width;height;text;font;lineFormat;unopenedFormat;unclosedFormat;constructor(t){super(t),this.x=t.x,this.y=t.y,this.width=t.width,this.height=t.height,this.text=t.text,this.font=t.font,this.lineFormat=t.lineFormat??null,this.unopenedFormat=t.unopenedFormat??null,this.unclosedFormat=t.unclosedFormat??null}},y=class{index;items;constructor(t){this.index=t.index,this.items=t.items||[]}};async function w(t,e={}){const s=e.timeout?1e3*e.timeout:1e4,n=new AbortController,o=setTimeout(()=>n.abort(),s);try{const s=await fetch(t,{signal:n.signal});if(clearTimeout(o),!s.ok)throw new Error(`HTTP ${s.status}`);return"arraybuffer"===e.responseType?await s.arrayBuffer():await s.text()}catch(r){throw clearTimeout(o),r}}var x=class t{name;itemType;constructor(e,s){if(this.constructor===t)throw new TypeError("Can not construct abstract class.");if(this.transform===t.prototype.transform)throw new TypeError("Please implement abstract method 'transform()'.");this.name=e,this.itemType=s}transform(t){throw new TypeError("Do not call abstract method foo from child.")}completeTransform(t){return t.messages=[],t}},E=class{category;color;constructor(t){this.category=t.category,this.color=t.color}},T=new E({category:"Added",color:"green"}),v=new E({category:"Removed",color:"red"}),b=new E({category:"Unchanged",color:"brown"}),L=new E({category:"Detected",color:"green"}),O=new E({category:"Modified",color:"green"}),I=class t extends x{constructor(e){if(super(e,g.name),this.constructor===t)throw new TypeError("Can not construct abstract class.")}completeTransform(t){return t.messages=[],t.pages.forEach(t=>{t.items=t.items.filter(t=>!t.annotation||t.annotation!==v),t.items.forEach(t=>t.annotation=null)}),t}},S=class{pages;globals;messages;constructor(t){this.pages=t.pages||[],this.globals=t.globals||{},this.messages=t.messages||[]}},F=class{string;type;format;constructor(t){this.string=t.string,this.type=t.type??null,this.format=t.format??null}},k=class extends f{x;y;width;height;words;constructor(t){super(t),this.x=t.x??0,this.y=t.y??0,this.width=t.width??0,this.height=t.height??0,this.words=t.words||[],t.text&&!t.words&&(this.words=t.text.split(" ").filter(t=>t.trim().length>0).map(t=>new F({string:t})))}text(){return this.wordStrings().join(" ")}wordStrings(){return this.words.map(t=>t.string)}},C=class t{results;stash;constructor(){if(this.constructor===t)throw new TypeError("Can not construct abstract class.");this.results=[],this.stash=[]}consumeAll(t){t.forEach(t=>this.consume(t))}consume(t){this.shouldStash(t)?(this.matchesStash(t)||this.flushStash(),this.pushOnStash(t)):(this.stash.length>0&&this.flushStash(),this.results.push(t))}pushOnStash(t){this.onPushOnStash(t),this.stash.push(t)}complete(){return this.stash.length>0&&this.flushStash(),this.results}matchesStash(t){if(0===this.stash.length)return!0;const e=this.stash[this.stash.length-1];return this.doMatchesStash(e,t)}flushStash(){this.stash.length>0&&(this.doFlushStash(this.stash,this.results),this.stash=[])}onPushOnStash(t){}shouldStash(t){throw new TypeError(" Do not call abstract method foo from child."+t)}doMatchesStash(t,e){throw new TypeError(" Do not call abstract method foo from child."+t+e)}doFlushStash(t,e){throw new TypeError(" Do not call abstract method foo from child."+t+e)}},N=class{footnoteLinks;footnotes;containLinks;formattedWords;constructor(t){this.footnoteLinks=t.footnoteLinks||[],this.footnotes=t.footnotes||[],this.containLinks=t.containLinks??!1,this.formattedWords=t.formattedWords??0}add(t){this.footnoteLinks=this.footnoteLinks.concat(t.footnoteLinks),this.footnotes=this.footnotes.concat(t.footnotes),this.containLinks=this.containLinks||t.containLinks,this.formattedWords+=t.formattedWords}};function M(t){t.sort((t,e)=>t.x-e.x)}var D={BOLD:{name:"BOLD",startSymbol:"<strong>",endSymbol:"</strong>"},OBLIQUE:{name:"OBLIQUE",startSymbol:"<em>",endSymbol:"</em>"},BOLD_OBLIQUE:{name:"BOLD_OBLIQUE",startSymbol:"<strong><em>",endSymbol:"</em></strong>"}},H={LINK:{name:"LINK",toText:t=>`<a href="${t}">${t}</a>`},FOOTNOTE_LINK:{name:"FOOTNOTE_LINK",attachWithoutWhitespace:!0,plainTextFormat:!0,toText:t=>`<sup><a href="#${t}">${t}</a></sup>`},FOOTNOTE:{name:"FOOTNOTE",toText:t=>`<p id="${t}">^${t}</p>`}},U=class{fontToFormats;constructor(t){this.fontToFormats=t}compact(t){M(t);const e=new B(this.fontToFormats);e.consumeAll(t.map(t=>new g({...t})));const s=e.complete();var n=0,o=0;return t.forEach(t=>{n=Math.max(n,t.height),o+=t.width}),new k({x:t[0].x,y:t[0].y,height:n,width:o,words:s,parsedElements:new N({footnoteLinks:e.footnoteLinks,footnotes:e.footnotes,containLinks:e.containLinks,formattedWords:e.formattedWords})})}},B=class extends C{fontToFormats;footnoteLinks;footnotes;formattedWords;containLinks;stashedNumber;firstY;currentItem;constructor(t){super(),this.fontToFormats=t,this.footnoteLinks=[],this.footnotes=[],this.formattedWords=0,this.containLinks=!1,this.stashedNumber=!1,this.currentItem=null}shouldStash(t){return this.firstY||(this.firstY=t.y),this.currentItem=t,!0}onPushOnStash(t){this.stashedNumber=n(t.text.trim())}doMatchesStash(t,e){if(this.fontToFormats.get(t.font)!==this.fontToFormats.get(e.font))return!1;const s=n(e.text.trim());return this.stashedNumber===s}doFlushStash(t,e){if(this.stashedNumber){const s=t.map(t=>t.text).join("").trim();t[0].y>this.firstY?(e.push(new F({string:`${s}`,type:H.FOOTNOTE_LINK})),this.footnoteLinks.push(parseInt(s))):this.currentItem&&this.currentItem.y<t[0].y?(e.push(new F({string:`${s}`,type:H.FOOTNOTE})),this.footnotes.push(s)):this.copyStashItemsAsText(t,e)}else this.copyStashItemsAsText(t,e)}copyStashItemsAsText(t,e){const s=this.fontToFormats.get(t[0].font);e.push(...this.itemsToWords(t,s))}itemsToWords(t,e){const s=(n=t,o="",r=null,n.forEach(t=>{var e=t.text;o.endsWith(" ")||e.startsWith(" ")||(r?t.x-r.x-r.width>5&&(o+=" "):a(t.text)&&(e+=" ")),o+=e,r=t}),o).split(" ");var n,o,r;const i=e?D[e]:null;return s.filter(t=>t.trim().length>0).map(t=>{var e=null;return t.startsWith("http:")?(this.containLinks=!0,e=H.LINK):t.startsWith("www.")&&(this.containLinks=!0,t=`http://${t}`,e=H.LINK),i&&this.formattedWords++,new F({string:t,type:e,format:i})})}};var P=class extends I{fontMap;constructor(t){super("Calculate Global Stats"),this.fontMap=t}transform(t){const e={},s={};var n,o=0;t.pages.forEach(t=>{t.items.forEach(t=>{t.height&&(e[t.height]=e[t.height]?e[t.height]+1:1,s[t.font]=s[t.font]?s[t.font]+1:1,t.height>o&&(o=t.height,n=t.font))})});const r=parseInt(A(e)),a=A(s),i={};t.pages.forEach(t=>{var e;t.items.forEach(t=>{if(t.height===r&&t.text.trim().length>0){if(e&&t.y!==e.y){const s=e.y-t.y;s>0&&(i[s]=i[s]?i[s]+1:1)}e=t}else e=null})});const h=parseInt(A(i)),l=[],c=new Map;this.fontMap?.forEach(function(t,e){l.push(e+" = "+t.name);const s=t.name.toLowerCase();var o=null;e===a?o=null:s.includes("bold")&&(s.includes("oblique")||s.includes("italic"))?o=D.BOLD_OBLIQUE:s.includes("bold")?o=D.BOLD:s.includes("oblique")||s.includes("italic")?o=D.OBLIQUE:s===n&&(o=D.BOLD),o&&c.set(e,o.name)}),l.sort();const m=t.pages.map(t=>({...t,items:t.items.map(t=>({...t}))}));return new S({...t,pages:m,globals:{mostUsedHeight:r,mostUsedFont:a,mostUsedDistance:h,maxHeight:o,maxHeightFont:n,fontToFormats:c},messages:["Items per height: "+JSON.stringify(e),"Items per font: "+JSON.stringify(s),"Items per distance: "+JSON.stringify(i),"Fonts:"+JSON.stringify(l)]})}};function A(t){var e=0,s="";return Object.keys(t).map(n=>{(!s||t[n]>e)&&(e=t[n],s=n)}),s}var $=class t extends x{constructor(e){if(super(e,k.name),this.constructor===t)throw new TypeError("Can not construct abstract class.")}completeTransform(t){return t.messages=[],t.pages.forEach(t=>{t.items=t.items.filter(t=>!t.annotation||t.annotation!==v),t.items.forEach(t=>t.annotation=null)}),t}},W=class{mostUsedDistance;constructor(t){this.mostUsedDistance=t.mostUsedDistance||12}group(t){const e=[];var s=[];return t.forEach(t=>{s.length>0&&Math.abs(s[0].y-t.y)>=this.mostUsedDistance/2&&(e.push(s),s=[]),s.push(t)}),e.push(s),e.forEach(t=>{M(t)}),e}},j={H1:{name:"H1",headline:!0,headlineLevel:1,toText:t=>"<h1>"+q(t.items,!0)+"</h1>"},H2:{name:"H2",headline:!0,headlineLevel:2,toText:t=>"<h2>"+q(t.items,!0)+"</h2>"},H3:{name:"H3",headline:!0,headlineLevel:3,toText:t=>"<h3>"+q(t.items,!0)+"</h3>"},H4:{name:"H4",headline:!0,headlineLevel:4,toText:t=>"<h4>"+q(t.items,!0)+"</h4>"},H5:{name:"H5",headline:!0,headlineLevel:5,toText:t=>"<h5>"+q(t.items,!0)+"</h5>"},H6:{name:"H6",headline:!0,headlineLevel:6,toText:t=>"<h6>"+q(t.items,!0)+"</h6>"},TOC:{name:"TOC",mergeToBlock:!0,toText:t=>q(t.items,!0)},FOOTNOTES:{name:"FOOTNOTES",mergeToBlock:!0,mergeFollowingNonTypedItems:!0,toText:t=>"<p>"+q(t.items,!1)+"</p>"},CODE:{name:"CODE",mergeToBlock:!0,toText:t=>"<code>"+q(t.items,!0)+"</code>"},LIST:{name:"LIST",mergeToBlock:!1,mergeFollowingNonTypedItemsWithSmallDistance:!0,toText:t=>"<ul>\n"+q(t.items,!1)+"</ul>"},PARAGRAPH:{name:"PARAGRAPH",toText:t=>"<p>"+q(t.items,!1)+"</p>"}};function q(t,e){var s,n="";const o=()=>{n+=s.endSymbol,s=null};return t.forEach((r,a)=>{var i;r&&(r.words.forEach((t,r)=>{const a=t.type,i=t.format;var h;!s||i&&i===s||o(),r>0&&(!a||!a.attachWithoutWhitespace)&&(1!==(h=t.string).length||"."!==h[0]&&"!"!==h[0]&&"?"!==h[0])&&(n+=" "),!i||s||e||(n+=(s=i).startSymbol),!a||e&&!a.plainTextFormat?n+=t.string:n+=a.toText(t.string)}),!s||a!==t.length-1&&(0===(i=t[a+1]).words.length?null:i.words[0].format)===s||o(),n+="\n")}),n}j.enumValueOf=function(t){return j[t]},j.isHeadline=function(t){return t&&2===t.name.length&&"H"===t.name[0]},j.blockToText=function(t){return t.type?t.type.toText(t):q(t.items,!1)},j.headlineByLevel=function(t){return 1===t?j.H1:2===t?j.H2:3===t?j.H3:4===t?j.H4:5===t?j.H5:(t>6&&console.warn("Unsupported headline level: "+t+" (supported are 1-6), defaulting to level 6"),j.H6)};var Y=class extends ${constructor(){super("Compact To Lines")}transform(t){const{mostUsedDistance:e,fontToFormats:s}=t.globals,n=[],o=[];var r=0,a=0;const i=new W({mostUsedDistance:e}),h=new U(s);return t.pages.forEach(t=>{if(t.items.length>0){const e=[];i.group(t.items).forEach(s=>{const i=h.compact(s);if(s.length>1&&(i.annotation=T,s.forEach(t=>{t.annotation=v,e.push(new k({...t}))})),0===i.words.length&&(i.annotation=v),e.push(i),i.parsedElements.formattedWords&&(a+=i.parsedElements.formattedWords),i.parsedElements.containLinks&&r++,i.parsedElements.footnoteLinks.length>0){const e=i.parsedElements.footnoteLinks.map(e=>({footnoteLink:e,page:t.index+1}));o.push.apply(o,e)}if(i.parsedElements.footnotes.length>0){i.type=j.FOOTNOTES;const e=i.parsedElements.footnotes.map(e=>({footnote:e,page:t.index+1}));n.push.apply(n,e)}}),t.items=e}}),new S({...t,messages:["Detected "+a+" formatted words","Found "+r+" links","Detected "+o.length+" footnotes links","Detected "+n.length+" footnotes"]})}};function R(t){var e=0;if(0===t.trim().length)return e;for(var n=0;n<t.length;n++){const o=t.charCodeAt(n);s(o)||32===o||160===o||(e=(e<<5)-e+o,e|=0)}return e}var V=class extends ${constructor(){super("Remove Repetitive Elements")}transform(t){const e=[],s={},n={};t.pages.forEach(t=>{const o=t.items.reduce((t,e)=>(e.y<t.minY?(t.minElements=[e],t.minY=e.y):e.y===t.minY&&t.minElements.push(e),e.y>t.maxY?(t.maxElements=[e],t.maxY=e.y):e.y===t.maxY&&t.maxElements.push(e),t),{minY:999,maxY:0,minElements:[],maxElements:[]}),r=R(o.minElements.reduce((t,e)=>t+e.text().toUpperCase(),"")),a=R(o.maxElements.reduce((t,e)=>t+e.text().toUpperCase(),""));e.push({minElements:o.minElements,maxElements:o.maxElements,minLineHash:r,maxLineHash:a}),s[r]=s[r]?s[r]+1:1,n[a]=n[a]?n[a]+1:1});var o=0,r=0;return t.pages.forEach((a,i)=>{s[e[i].minLineHash]>=Math.max(3,2*t.pages.length/3)&&(e[i].minElements.forEach(t=>{t.annotation=v}),r++),n[e[i].maxLineHash]>=Math.max(3,2*t.pages.length/3)&&(e[i].maxElements.forEach(t=>{t.annotation=v}),o++)}),new S({...t,messages:["Removed Header: "+o,"Removed Footers: "+r]})}},K=class extends ${constructor(){super("Vertical to Horizontal Text")}transform(t){var e=0;return t.pages.forEach(t=>{const s=new J;s.consumeAll(t.items),t.items=s.complete(),e+=s.foundVerticals}),new S({...t,messages:["Converted "+e+" verticals"]})}},J=class extends C{foundVerticals;constructor(){super(),this.foundVerticals=0}shouldStash(t){return 1===t.words.length&&1===t.words[0].string.length}doMatchesStash(t,e){return t.y-e.y>5&&t.words[0].type===e.words[0].type}doFlushStash(t,e){if(t.length>5){var s=[],n=999,o=0,r=0,a=0;t.forEach(t=>{t.annotation=v,e.push(t),s.push(t.words[0]),n=Math.min(n,t.x),o=Math.max(o,t.y),r+=t.width,a=Math.max(a,t.height)}),e.push(new k({...t[0],x:n,y:o,width:r,height:a,words:s,annotation:T})),this.foundVerticals++}else e.push(...t)}},Q=class{headlineCharCodes;stackedLineItems;stackedChars;constructor(t){this.headlineCharCodes=r(t.headline),this.stackedLineItems=[],this.stackedChars=0}consume(t){const e=r(t.text());if(this.matchAll(e)){if(this.stackedLineItems.push(t),this.stackedChars+=e.length,this.stackedChars===this.headlineCharCodes.length)return this.stackedLineItems}else this.stackedChars>0&&(this.stackedChars=0,this.stackedLineItems=[],this.consume(t));return null}matchAll(t){for(var e=0;e<t.length;e++){const s=this.headlineCharCodes[this.stackedChars+e];if(t[e]!==s)return!1}return!0}},_=class extends ${constructor(){super("Detect TOC")}transform(t){const e=[],r=Math.min(20,t.pages.length),a=new X;var i,h,c=[];t.pages.slice(0,r).forEach(t=>{var r=0;const l=new Set,m=[];var u,p;if(t.items.filter(t=>t.words.includes(t=>o(t.string,"."))).forEach(t=>{var e=t.words.filter(t=>!o(t.string,"."));const a=[];for(;e.length>0&&n(e[e.length-1].string);){const t=e.pop();a.unshift(t.string)}if(0===a.length&&e.length>0){const t=e[e.length-1];for(;s(t.string.charCodeAt(t.string.length-1));)a.unshift(t.string.charAt(t.string.length-1)),t.string=t.string.substring(0,t.string.length-1)}var i=a.length>0;i?(i=!0,u&&(e.push(...u),u=null),m.push(new Z({pageNumber:parseInt(a.join("")),lineItem:new k({...t,words:e})})),r++):h?(u&&l.add(p),u=e,p=t):h=t}),100*r/t.items.length>75){e.push(t.index+1),i=t,a.levelPageItems(m),c.push(...m);const s=[];t.items.forEach(t=>{l.has(t)||(t.annotation=v),s.push(t),t===h&&s.push(new k({...t,type:j.H2,annotation:T}))}),t.items=s}else h=null});var m=c.length;const u=[],p=[],d={};if(e.length>0){c.forEach(t=>{i.items.push(new k({words:[new F({string:" ".repeat(3*t.level)+"-"})].concat(t.lineItem.words),type:j.TOC,annotation:T}))});const e=function(t,e){for(var s of e){const e=z(t,s.lineItem.text());if(e)return e.index-s.pageNumber}return null}(t.pages.filter(t=>t.index>i.index),c);c.forEach(s=>{var n,o=t.pages[s.pageNumber+e];o&&((n=G(o,s.lineItem.text()))||(o=t.pages[s.pageNumber+e+1])&&(n=G(o,s.lineItem.text()))),n?function(t,e,s,n){s.headlineItems.forEach(t=>t.annotation=v);const o=j.headlineByLevel(e.level+2),r=s.headlineItems.reduce((t,e)=>Math.max(t,e.height),0);t.items.splice(s.lineIndex+1,0,new k({...s.headlineItems[0],words:e.lineItem.words,height:r,type:o,annotation:T}));var a=n[o.name];a?(a.min=Math.min(a.min,r),a.max=Math.max(a.max,r)):(a={min:r,max:r},n[o.name]=a)}(o,s,n,d):u.push(s)});var f=i.index+2,g=[];const s=e=>{g.length>0&&(g.forEach(s=>{const n=j.headlineByLevel(s.level+2),o=d[n.name];if(o){const[r,a]=function(t,e,s,n,o){const r=e.lineItem.text().toUpperCase();for(var a=n;a<=o;a++){const e=t[a-1];if(e){const t=e.items.findIndex(t=>!t.type&&!t.annotation&&t.height>=s.min&&t.height<=s.max&&l(r,t.text())>=.5);if(t>-1)return[a-1,t]}}return[-1,-1]}(t.pages,s,o,f,e);if(a>-1){const e=t.pages[r];e.items[a].annotation=v,e.items.splice(a+1,0,new k({...s.lineItem,type:n,annotation:T})),p.push(s)}}}),g=[])};u.length>0&&(c.forEach(t=>{u.includes(t)?g.push(t):(s(t.pageNumber),f=t.pageNumber)}),g.length>0&&s(t.pages.length))}const y=[];return y.push("Detected "+e.length+" table of content pages"),e.length>0&&(y.push("TOC headline heights: "+JSON.stringify(d)),y.push("Found TOC headlines: "+(m-u.length+p.length)+"/"+m)),u.length>0&&(y.push("Found TOC headlines (by size): "+p.map(t=>t.lineItem.text())),y.push("Missing TOC headlines: "+u.filter(t=>!p.includes(t)).map(t=>t.lineItem.text()+"=>"+t.pageNumber))),new S({...t,globals:{...t.globals,tocPages:e,headlineTypeToHeightRange:d},messages:y})}};function z(t,e){for(var s of t)if(G(s,e))return s;return null}function G(t,e){const s=new Q({headline:e});var n=0;for(var o of t.items){const t=s.consume(o);if(t)return{lineIndex:n,headlineItems:t};n++}return null}var X=class{levelByMethod;uniqueFonts;constructor(){this.levelByMethod=null,this.uniqueFonts=[]}levelPageItems(t){if(!this.levelByMethod)if(this.calculateUniqueX(t).length>1)this.levelByMethod=this.levelByXDiff;else{const e=this.calculateUniqueFonts(t);e.length>1?(this.uniqueFonts=e,this.levelByMethod=this.levelByFont):this.levelByMethod=this.levelToZero}this.levelByMethod(t)}levelByXDiff(t){const e=this.calculateUniqueX(t);t.forEach(t=>{t.level=e.indexOf(t.lineItem.x)})}levelByFont(t){t.forEach(t=>{t.level=this.uniqueFonts.indexOf(t.lineItem.font)})}levelToZero(t){t.forEach(t=>{t.level=0})}calculateUniqueX(t){var e=t.reduce(function(t,e){return t.indexOf(e.lineItem.x)<0&&t.push(e.lineItem.x),t},[]);return e.sort((t,e)=>t-e),e}calculateUniqueFonts(t){return t.reduce(function(t,e){return t.indexOf(e.lineItem.font)<0&&t.push(e.lineItem.font),t},[])}},Z=class{lineItem;pageNumber;level;constructor(t){this.lineItem=t.lineItem,this.pageNumber=t.pageNumber,this.level=0}},tt=class extends ${constructor(){super("Detect List Items")}transform(t){var e=0,s=0;return t.pages.forEach(t=>{const n=[];t.items.forEach(t=>{if(n.push(t),!t.type){var o=t.text();if(a(t.words[0].string))if(e++,"-"===t.words[0].string)t.annotation=L,t.type=j.LIST;else{t.annotation=v;const e=t.words.map(t=>new F({...t}));e[0].string="-",n.push(new k({...t,words:e,annotation:T,type:j.LIST}))}else/^[\s]*[\d]*[.][\s].*$/g.test(o)&&(s++,t.annotation=L,t.type=j.LIST)}}),t.items=n}),new S({...t,messages:["Detected "+e+" plain list items.","Detected "+s+" numbered list items."]})}},et=class extends ${constructor(){super("Detect Headers")}transform(t){const{tocPages:e,headlineTypeToHeightRange:s,mostUsedHeight:n,mostUsedDistance:o,mostUsedFont:r,maxHeight:a}=t.globals,h=e.length>0;var l=0;const c=function(t,e){const s=new Set;return t.forEach(t=>{t.items.forEach(n=>{n.type||n.height!==e||s.add(t)})}),s}(t.pages,a),m=n+(a-n)/4;if(c.forEach(t=>{t.items.forEach(t=>{const e=t.height;!t.type&&e>m&&(t.type=e===a?j.H1:j.H2,t.annotation=L,l++)})}),h)Object.keys(s).forEach(e=>{var o=s[e];o.max>n&&t.pages.forEach(t=>{t.items.forEach(t=>{t.type||t.height!==o.max||(t.annotation=L,t.type=j.enumValueOf(e),l++)})})});else{const e=[];t.pages.forEach(t=>{t.items.forEach(t=>{!t.type&&t.height>n&&!i(t.text())&&(e.includes(t.height)||e.push(t.height))})}),e.sort((t,e)=>e-t),e.forEach((e,s)=>{if(s+2<=6){const n=j.headlineByLevel(2+s);t.pages.forEach(t=>{t.items.forEach(t=>{t.type||t.height!==e||i(t.text())||(l++,t.annotation=L,t.type=n)})})}})}var u=1;if(t.pages.forEach(t=>{t.items.forEach(t=>{t.type&&t.type.headline&&(u=Math.max(u,t.type.headlineLevel))})}),u<6){const e=j.headlineByLevel(u+1);t.pages.forEach(t=>{var s;t.items.forEach(t=>{!t.type&&t.height===n&&t.font!==r&&(!s||s.y<t.y||s.type&&s.type.headline||s.y-t.y>2*o)&&t.text()===t.text().toUpperCase()&&(l++,t.annotation=L,t.type=e),s=t})})}return new S({...t,messages:["Detected "+l+" headlines."]})}};var st=class extends f{items;constructor(t){super(t),this.items=[],t.items&&t.items.forEach(t=>this.addItem(t))}addItem(t){if(this.type&&t.type&&this.type!==t.type)throw new Error(`Adding item of type ${t.type.name} to block of type ${this.type.name}`);this.type||(this.type=t.type),t.parsedElements&&(this.parsedElements?this.parsedElements.add(t.parsedElements):this.parsedElements=t.parsedElements);const e=new k({...t});e.type=null,this.items.push(e)}},nt=class t extends x{constructor(e){if(super(e,st.name),this.constructor===t)throw new TypeError("Can not construct abstract class.")}completeTransform(t){return t.messages=[],t.pages.forEach(t=>{t.items=t.items.filter(t=>!t.annotation||t.annotation!==v),t.items.forEach(t=>t.annotation=null)}),t}},ot=class extends nt{constructor(){super("Gather Blocks")}transform(t){const{mostUsedDistance:e}=t.globals;var s=0,n=0;return t.pages.map(t=>{n+=t.items.length;const o=[];var r=new st({});const a=()=>{r.items.length>1&&(r.annotation=L),o.push(r),r=new st({}),s++};var i=function(t){var e=999;return t.forEach(t=>{e=Math.min(e,t.x)}),999===e?null:e}(t.items);t.items.forEach(t=>{r.items.length>0&&function(t,e,s,n){if(t.type&&t.type.mergeFollowingNonTypedItems&&!e.type)return!1;const o=t.items[t.items.length-1],r=function(t,e,s,n){const o=t.y-e.y;if(o<0-n/2)return!0;var r=n+1;t.x>s&&e.x>s&&(r=n+n/2);return o>r}(o,e,s,n);if(t.type&&t.type.mergeFollowingNonTypedItemsWithSmallDistance&&!e.type&&!r)return!1;if(e.type!==t.type)return!0;return e.type?!e.type.mergeToBlock:r}(r,t,i,e)&&a(),r.addItem(t)}),r.items.length>0&&a(),t.items=o}),new S({...t,messages:["Gathered "+s+" blocks out of "+n+" line items"]})}};var rt=class extends nt{constructor(){super("$1")}transform(t){const e=t.globals.mostUsedHeight??0;var s=0;return t.pages.forEach(t=>{var n=function(t){var e=999;return t.forEach(t=>{t.items.forEach(t=>{e=Math.min(e,t.x)})}),999===e?null:e}(t.items);t.items.forEach(t=>{!t.type&&function(t,e,s){if(0===e.length)return!1;if(1===e.length)return e[0].x>t&&e[0].height<=s+1;for(var n of e)if(n.x===t)return!1;return!0}(n,t.items,e)&&(t.annotation=L,t.type=j.CODE,s++)})}),new S({...t,messages:["Detected "+s+" code/quote items."]})}};var at,it=class extends nt{constructor(){super("Level Lists")}transform(t){var e=0,s=0;return t.pages.forEach(t=>{t.items.filter(t=>t.type===j.LIST).forEach(t=>{var n,o=0;const r={};var a=!1;t.items.forEach(t=>{n?t.x>n?(o++,r[t.x]=o):t.x<n&&(o=r[t.x]):r[t.x]=0,o>0&&(t.words=[new F({string:" ".repeat(3*o)})].concat(t.words),a=!0),n=t.x}),e++,a?(s++,t.annotation=O):t.annotation=b})}),new S({...t,messages:["Modified "+s+" / "+e+" list blocks."]})}},ht=class extends x{constructor(){super("To Text Blocks","TextBlock")}transform(t){return t.pages.forEach(t=>{const e=[];t.items.forEach(t=>{const s=t.type?t.type.name:"Unknown";e.push({category:s,text:j.blockToText(t)})}),t.items=e}),new S({...t})}},lt=class extends x{constructor(){super("To HTML","String")}transform(t){return t.pages.forEach(t=>{var e="";t.items.forEach(t=>{let s;s="TOC"===t.category?t.text:t.text.replace(/(\r\n|\n|\r)/gm,"\n"),"LIST"!==t.category&&(s=s.split("- ").join("")),"CODE"===t.category&&(s=s.split("<code>").join("").split("</code>").join("")),e+=`<p>${s}</p>\n\n`}),t.items=[e]}),new S({...t})}};async function ct(t,e={}){const{addPageNumbers:s=!1,addCitation:n=!0,liteParseOptions:o={}}=e,r="string"==typeof t?await w(t,{responseType:"arraybuffer",timeout:10}):t;let a;try{({LiteParse:a}=await import("@llamaindex/liteparse"))}catch(p){return{error:`method: 'liteparse' requires the optional @llamaindex/liteparse dependency (Node.js only): ${p.message}`}}const i=new a({quiet:!0,ocrEnabled:!1,ocrFailureFatal:!1,...o});let l;try{l=await i.parse(new Uint8Array(r))}catch(p){return{error:p.message}}const c=l.pages.reduce((t,e)=>{const n=e.text.split(/\n{2,}/).filter(t=>t.trim().length>0).map(t=>h(t).replace(/\n/g,"<br/>")).join("</p><p>");return t+`<p id="page-${e.pageNum}">${s?` [${e.pageNum}] `:""}${n}</p>`},"");let m,u;return n&&(u=l.creator||l.producer,m=l.pages[0]?.text.split("\n").find(t=>t.trim().length>0)),{author:u,title:m,html:c,format:"pdf"}}async function mt(t,e={}){const{addPageNumbers:s=!1,addCitation:n=!0,liteParseOptions:o={}}=e,r="string"==typeof t?await w(t,{responseType:"arraybuffer",timeout:10}):t;let a;try{a=await import("@llamaindex/liteparse-wasm"),await async function(t){at||(at=t.default()),await at}(a)}catch(p){return{error:`method: 'liteparse-wasm' requires the optional @llamaindex/liteparse-wasm dependency: ${p.message}`}}const i=new a.LiteParse({quiet:!0,ocrEnabled:!1,ocrFailureFatal:!1,...o});let l;try{l=await i.parse(new Uint8Array(r))}catch(p){return{error:p.message}}const c=l.pages.reduce((t,e)=>{const n=e.text.split(/\n{2,}/).filter(t=>t.trim().length>0).map(t=>h(t).replace(/\n/g,"<br/>")).join("</p><p>");return t+`<p id="page-${e.pageNum}">${s?` [${e.pageNum}] `:""}${n}</p>`},"");let m,u;return n&&(u=l.creator||l.producer,m=l.pages[0]?.text.split("\n").find(t=>t.trim().length>0)),{author:u,title:m,html:c,format:"pdf"}}exports.convertPDFToHTML=async function(t,e={}){if("liteparse"===e.method){const{method:s,...n}=e;return ct(t,n)}if("liteparse-wasm"===e.method){const{method:s,...n}=e;return mt(t,n)}var{addPageNumbers:s=!1,addCitation:n=!0}=e,o="string"==typeof t?await w(t,{responseType:"arraybuffer",timeout:10}):t;let r;try{let{resolvePDFJS:t}=await import("https://cdn.jsdelivr.net/npm/pdfjs-serverless@1.1.0/+esm");const{getDocument:e}=await t();r=await e({data:new Uint8Array(o),useSystemFonts:!0,verbosity:0}).promise}catch(T){return{error:T.message}}const a=[...Array(r.numPages).keys()].map(t=>new y({index:t}));let i,h={};for(let d=1;d<=r.numPages;d++){const t=await r.getPage(d),e=await t.getTextContent();if(!(Object.keys(h).length<10)){i=p(h);break}h=u(h,t.pageNumber-1,e.items)}let l=i?i.pageNum:0;for(let u=1;u<=r.numPages;u++){const t=await r.getPage(u);await t.getOperatorList();const e=t.getViewport({scale:1});let s=await t.getTextContent();i&&t.pageIndex>=i.pageIndex&&(s=d(s,l),l++);const n=s.items.map(t=>{const s=[1,0,0,1,0,0];for(let r=0;r<6;r++)s[r]+=t.transform[r]*e.transform[r%2?3:0],r%2&&(s[r+1]+=t.transform[r]*e.transform[1]);const n=Math.sqrt(s[2]*s[2]+s[3]*s[3]),o=t.height/n;return new g({x:Math.round(t.transform[4]),y:Math.round(t.transform[5]),width:Math.round(t.width),height:Math.round(o<=1?t.height:o),text:t.str,font:t.fontName})});a[t.pageNumber-1].items=n}var c=new S({pages:a});let m;[new P,new Y,new V,new K,new _,new et,new tt,new ot,new rt,new it,new ht,new lt].forEach(t=>{m&&(c=m.completeTransform(c)),c=t.transform(c),m=t});var f=c.pages.reduce((t,e,n)=>t+`<p id="page-${n+1}">${s?` [${n+1}] `:""}${e.items.join('</p><p id="page-'+n+'">')}</p>`,"");if(n){var{Author:x,Title:E}=(await r.getMetadata()).info;E=f.slice(0,400).match(/<h[0-9]>(.*?)<\/h[0-9]>/)?.[1]||E}return{author:x,title:E,html:f,format:"pdf"}},exports.convertPDFToHTMLWithLiteParse=ct,exports.convertPDFToHTMLWithLiteParseWasm=mt,exports.detectPdfNeedsOcr=async function(t,e={}){const s="string"==typeof t?await w(t,{responseType:"arraybuffer",timeout:10}):t,{LiteParse:n}=await import("@llamaindex/liteparse"),o=await new n({quiet:!0,...e.liteParseOptions}).isComplex(new Uint8Array(s));return{needsOcr:o.some(t=>t.needsOcr),pages:o,reasons:[...new Set(o.flatMap(t=>t.reasons))]}};
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function t(t){for(;32===t.charCodeAt(0);)t=t.substring(1,t.length);return t}function e(t){for(;32===t.charCodeAt(t.length-1);)t=t.substring(0,t.length-1);return t}function s(t){return t>=48&&t<=57}function n(t){for(var e=0;e<t.length;e++)if(!s(t.charCodeAt(e)))return!1;return!0}function r(t,e){const s=e.charCodeAt(0);for(var n=0;n<t.length;n++)if(t.charCodeAt(n)!==s)return!1;return!0}function o(t){return function(t){const e=[];for(var s=0;s<t.length;s++)e.push(t.charCodeAt(s));return e}(t=t.toUpperCase()).filter(t=>32!==t&&9!==t&&46!==t)}function a(t){if(t.length>1)return!1;const e=t.charAt(0);return"-"===e||"•"===e||"–"===e}function i(t){return/^[\s]*[-•–][\s].*$/g.test(t)}function c(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function l(t,e){const s=new Set(t.toUpperCase().split(" ")),n=new Set(e.toUpperCase().split(" "));return new Set([...s].filter(t=>n.has(t))).size/Math.max(s.size,n.size)}var h=(t,e,s)=>Math.floor(t/e*s),m=(s,r,o)=>{for(const{str:a}of s){const s=e(t(a));n(s)&&(r[o]||(r[o]=[]),r[o].push(Number(s)))}return r},p=(t,e,s)=>{const n=h(1,6,s.length),r=h(5,6,s.length),o=m(s.slice(0,n),t,e);return m(s.slice(r),o,e)},u=t=>{let e=0;const s=Object.keys(t);if(0!==s.length&&1!==s.length)for(let n=0;n<s.length-1;n++){const r=t[s[n]],o=t[s[n+1]],a=e;for(let t=0;t<r.length&&e<2;t++)for(let a=0;a<o.length&&e<2;a++){const i=Number(s[n+1])-Number(s[n]);(r[t]+1===o[a]||i>1&&r[t]+i===o[a])&&e++}let i=n>0?Object.entries(t)[n-1]:Object.entries(t)[n];if(a===e)e=0,i=Object.entries(t)[n];else if(e>=2)return{pageIndex:Number(i[0]),pageNum:i[1][0]}}},d=(t,e)=>{const s={items:[...t.items]},n=h(1,6,s.items.length),r=h(5,6,s.items.length);return s.items=s.items.filter((t,o)=>{const a=o>0&&o<n,i=o>r&&o<s.items.length;return a||i?Number(t.str)!==Number(e):t}),s},f=class t{type;annotation;parsedElements;constructor(e){if(this.constructor===t)throw new TypeError("Can not construct abstract class.");this.type=e.type??null,this.annotation=e.annotation??null,this.parsedElements=e.parsedElements??null}},g=class extends f{x;y;width;height;text;font;lineFormat;unopenedFormat;unclosedFormat;constructor(t){super(t),this.x=t.x,this.y=t.y,this.width=t.width,this.height=t.height,this.text=t.text,this.font=t.font,this.lineFormat=t.lineFormat??null,this.unopenedFormat=t.unopenedFormat??null,this.unclosedFormat=t.unclosedFormat??null}},y=class{index;items;constructor(t){this.index=t.index,this.items=t.items||[]}};async function w(t,e={}){const s=e.timeout?1e3*e.timeout:1e4,n=new AbortController,r=setTimeout(()=>n.abort(),s);try{const s=await fetch(t,{signal:n.signal});if(clearTimeout(r),!s.ok)throw new Error(`HTTP ${s.status}`);return"arraybuffer"===e.responseType?await s.arrayBuffer():await s.text()}catch(o){throw clearTimeout(r),o}}var x=class t{name;itemType;constructor(e,s){if(this.constructor===t)throw new TypeError("Can not construct abstract class.");if(this.transform===t.prototype.transform)throw new TypeError("Please implement abstract method 'transform()'.");this.name=e,this.itemType=s}transform(t){throw new TypeError("Do not call abstract method foo from child.")}completeTransform(t){return t.messages=[],t}},v=class{category;color;constructor(t){this.category=t.category,this.color=t.color}},T=new v({category:"Added",color:"green"}),E=new v({category:"Removed",color:"red"}),b=new v({category:"Unchanged",color:"brown"}),O=new v({category:"Detected",color:"green"}),L=new v({category:"Modified",color:"green"}),S=class t extends x{constructor(e){if(super(e,g.name),this.constructor===t)throw new TypeError("Can not construct abstract class.")}completeTransform(t){return t.messages=[],t.pages.forEach(t=>{t.items=t.items.filter(t=>!t.annotation||t.annotation!==E),t.items.forEach(t=>t.annotation=null)}),t}},I=class{pages;globals;messages;constructor(t){this.pages=t.pages||[],this.globals=t.globals||{},this.messages=t.messages||[]}},C=class{string;type;format;constructor(t){this.string=t.string,this.type=t.type??null,this.format=t.format??null}},k=class extends f{x;y;width;height;words;constructor(t){super(t),this.x=t.x??0,this.y=t.y??0,this.width=t.width??0,this.height=t.height??0,this.words=t.words||[],t.text&&!t.words&&(this.words=t.text.split(" ").filter(t=>t.trim().length>0).map(t=>new C({string:t})))}text(){return this.wordStrings().join(" ")}wordStrings(){return this.words.map(t=>t.string)}},F=class t{results;stash;constructor(){if(this.constructor===t)throw new TypeError("Can not construct abstract class.");this.results=[],this.stash=[]}consumeAll(t){t.forEach(t=>this.consume(t))}consume(t){this.shouldStash(t)?(this.matchesStash(t)||this.flushStash(),this.pushOnStash(t)):(this.stash.length>0&&this.flushStash(),this.results.push(t))}pushOnStash(t){this.onPushOnStash(t),this.stash.push(t)}complete(){return this.stash.length>0&&this.flushStash(),this.results}matchesStash(t){if(0===this.stash.length)return!0;const e=this.stash[this.stash.length-1];return this.doMatchesStash(e,t)}flushStash(){this.stash.length>0&&(this.doFlushStash(this.stash,this.results),this.stash=[])}onPushOnStash(t){}shouldStash(t){throw new TypeError(" Do not call abstract method foo from child."+t)}doMatchesStash(t,e){throw new TypeError(" Do not call abstract method foo from child."+t+e)}doFlushStash(t,e){throw new TypeError(" Do not call abstract method foo from child."+t+e)}},N=class{footnoteLinks;footnotes;containLinks;formattedWords;constructor(t){this.footnoteLinks=t.footnoteLinks||[],this.footnotes=t.footnotes||[],this.containLinks=t.containLinks??!1,this.formattedWords=t.formattedWords??0}add(t){this.footnoteLinks=this.footnoteLinks.concat(t.footnoteLinks),this.footnotes=this.footnotes.concat(t.footnotes),this.containLinks=this.containLinks||t.containLinks,this.formattedWords+=t.formattedWords}};function _(t){t.sort((t,e)=>t.x-e.x)}var D={BOLD:{name:"BOLD",startSymbol:"<strong>",endSymbol:"</strong>"},OBLIQUE:{name:"OBLIQUE",startSymbol:"<em>",endSymbol:"</em>"},BOLD_OBLIQUE:{name:"BOLD_OBLIQUE",startSymbol:"<strong><em>",endSymbol:"</em></strong>"}},M={LINK:{name:"LINK",toText:t=>`<a href="${t}">${t}</a>`},FOOTNOTE_LINK:{name:"FOOTNOTE_LINK",attachWithoutWhitespace:!0,plainTextFormat:!0,toText:t=>`<sup><a href="#${t}">${t}</a></sup>`},FOOTNOTE:{name:"FOOTNOTE",toText:t=>`<p id="${t}">^${t}</p>`}},$=class{fontToFormats;constructor(t){this.fontToFormats=t}compact(t){_(t);const e=new B(this.fontToFormats);e.consumeAll(t.map(t=>new g({...t})));const s=e.complete();var n=0,r=0;return t.forEach(t=>{n=Math.max(n,t.height),r+=t.width}),new k({x:t[0].x,y:t[0].y,height:n,width:r,words:s,parsedElements:new N({footnoteLinks:e.footnoteLinks,footnotes:e.footnotes,containLinks:e.containLinks,formattedWords:e.formattedWords})})}},B=class extends F{fontToFormats;footnoteLinks;footnotes;formattedWords;containLinks;stashedNumber;firstY;currentItem;constructor(t){super(),this.fontToFormats=t,this.footnoteLinks=[],this.footnotes=[],this.formattedWords=0,this.containLinks=!1,this.stashedNumber=!1,this.currentItem=null}shouldStash(t){return this.firstY||(this.firstY=t.y),this.currentItem=t,!0}onPushOnStash(t){this.stashedNumber=n(t.text.trim())}doMatchesStash(t,e){if(this.fontToFormats.get(t.font)!==this.fontToFormats.get(e.font))return!1;const s=n(e.text.trim());return this.stashedNumber===s}doFlushStash(t,e){if(this.stashedNumber){const s=t.map(t=>t.text).join("").trim();t[0].y>this.firstY?(e.push(new C({string:`${s}`,type:M.FOOTNOTE_LINK})),this.footnoteLinks.push(parseInt(s))):this.currentItem&&this.currentItem.y<t[0].y?(e.push(new C({string:`${s}`,type:M.FOOTNOTE})),this.footnotes.push(s)):this.copyStashItemsAsText(t,e)}else this.copyStashItemsAsText(t,e)}copyStashItemsAsText(t,e){const s=this.fontToFormats.get(t[0].font);e.push(...this.itemsToWords(t,s))}itemsToWords(t,e){const s=(n=t,r="",o=null,n.forEach(t=>{var e=t.text;r.endsWith(" ")||e.startsWith(" ")||(o?t.x-o.x-o.width>5&&(r+=" "):a(t.text)&&(e+=" ")),r+=e,o=t}),r).split(" ");var n,r,o;const i=e?D[e]:null;return s.filter(t=>t.trim().length>0).map(t=>{var e=null;return t.startsWith("http:")?(this.containLinks=!0,e=M.LINK):t.startsWith("www.")&&(this.containLinks=!0,t=`http://${t}`,e=M.LINK),i&&this.formattedWords++,new C({string:t,type:e,format:i})})}};var P=class extends S{fontMap;constructor(t){super("Calculate Global Stats"),this.fontMap=t}transform(t){const e={},s={};var n,r=0;t.pages.forEach(t=>{t.items.forEach(t=>{t.height&&(e[t.height]=e[t.height]?e[t.height]+1:1,s[t.font]=s[t.font]?s[t.font]+1:1,t.height>r&&(r=t.height,n=t.font))})});const o=parseInt(H(e)),a=H(s),i={};t.pages.forEach(t=>{var e;t.items.forEach(t=>{if(t.height===o&&t.text.trim().length>0){if(e&&t.y!==e.y){const s=e.y-t.y;s>0&&(i[s]=i[s]?i[s]+1:1)}e=t}else e=null})});const c=parseInt(H(i)),l=[],h=new Map;this.fontMap?.forEach(function(t,e){l.push(e+" = "+t.name);const s=t.name.toLowerCase();var r=null;e===a?r=null:s.includes("bold")&&(s.includes("oblique")||s.includes("italic"))?r=D.BOLD_OBLIQUE:s.includes("bold")?r=D.BOLD:s.includes("oblique")||s.includes("italic")?r=D.OBLIQUE:s===n&&(r=D.BOLD),r&&h.set(e,r.name)}),l.sort();const m=t.pages.map(t=>({...t,items:t.items.map(t=>({...t}))}));return new I({...t,pages:m,globals:{mostUsedHeight:o,mostUsedFont:a,mostUsedDistance:c,maxHeight:r,maxHeightFont:n,fontToFormats:h},messages:["Items per height: "+JSON.stringify(e),"Items per font: "+JSON.stringify(s),"Items per distance: "+JSON.stringify(i),"Fonts:"+JSON.stringify(l)]})}};function H(t){var e=0,s="";return Object.keys(t).map(n=>{(!s||t[n]>e)&&(e=t[n],s=n)}),s}var U=class t extends x{constructor(e){if(super(e,k.name),this.constructor===t)throw new TypeError("Can not construct abstract class.")}completeTransform(t){return t.messages=[],t.pages.forEach(t=>{t.items=t.items.filter(t=>!t.annotation||t.annotation!==E),t.items.forEach(t=>t.annotation=null)}),t}},A=class{mostUsedDistance;constructor(t){this.mostUsedDistance=t.mostUsedDistance||12}group(t){const e=[];var s=[];return t.forEach(t=>{s.length>0&&Math.abs(s[0].y-t.y)>=this.mostUsedDistance/2&&(e.push(s),s=[]),s.push(t)}),e.push(s),e.forEach(t=>{_(t)}),e}},j={H1:{name:"H1",headline:!0,headlineLevel:1,toText:t=>"<h1>"+W(t.items,!0)+"</h1>"},H2:{name:"H2",headline:!0,headlineLevel:2,toText:t=>"<h2>"+W(t.items,!0)+"</h2>"},H3:{name:"H3",headline:!0,headlineLevel:3,toText:t=>"<h3>"+W(t.items,!0)+"</h3>"},H4:{name:"H4",headline:!0,headlineLevel:4,toText:t=>"<h4>"+W(t.items,!0)+"</h4>"},H5:{name:"H5",headline:!0,headlineLevel:5,toText:t=>"<h5>"+W(t.items,!0)+"</h5>"},H6:{name:"H6",headline:!0,headlineLevel:6,toText:t=>"<h6>"+W(t.items,!0)+"</h6>"},TOC:{name:"TOC",mergeToBlock:!0,toText:t=>W(t.items,!0)},FOOTNOTES:{name:"FOOTNOTES",mergeToBlock:!0,mergeFollowingNonTypedItems:!0,toText:t=>"<p>"+W(t.items,!1)+"</p>"},CODE:{name:"CODE",mergeToBlock:!0,toText:t=>"<code>"+W(t.items,!0)+"</code>"},LIST:{name:"LIST",mergeToBlock:!1,mergeFollowingNonTypedItemsWithSmallDistance:!0,toText:t=>"<ul>\n"+W(t.items,!1)+"</ul>"},PARAGRAPH:{name:"PARAGRAPH",toText:t=>"<p>"+W(t.items,!1)+"</p>"}};function W(t,e){var s,n="";const r=()=>{n+=s.endSymbol,s=null};return t.forEach((o,a)=>{var i;o&&(o.words.forEach((t,o)=>{const a=t.type,i=t.format;var c;!s||i&&i===s||r(),o>0&&(!a||!a.attachWithoutWhitespace)&&(1!==(c=t.string).length||"."!==c[0]&&"!"!==c[0]&&"?"!==c[0])&&(n+=" "),!i||s||e||(n+=(s=i).startSymbol),!a||e&&!a.plainTextFormat?n+=t.string:n+=a.toText(t.string)}),!s||a!==t.length-1&&(0===(i=t[a+1]).words.length?null:i.words[0].format)===s||r(),n+="\n")}),n}j.enumValueOf=function(t){return j[t]},j.isHeadline=function(t){return t&&2===t.name.length&&"H"===t.name[0]},j.blockToText=function(t){return t.type?t.type.toText(t):W(t.items,!1)},j.headlineByLevel=function(t){return 1===t?j.H1:2===t?j.H2:3===t?j.H3:4===t?j.H4:5===t?j.H5:(t>6&&console.warn("Unsupported headline level: "+t+" (supported are 1-6), defaulting to level 6"),j.H6)};var q=class extends U{constructor(){super("Compact To Lines")}transform(t){const{mostUsedDistance:e,fontToFormats:s}=t.globals,n=[],r=[];var o=0,a=0;const i=new A({mostUsedDistance:e}),c=new $(s);return t.pages.forEach(t=>{if(t.items.length>0){const e=[];i.group(t.items).forEach(s=>{const i=c.compact(s);if(s.length>1&&(i.annotation=T,s.forEach(t=>{t.annotation=E,e.push(new k({...t}))})),0===i.words.length&&(i.annotation=E),e.push(i),i.parsedElements.formattedWords&&(a+=i.parsedElements.formattedWords),i.parsedElements.containLinks&&o++,i.parsedElements.footnoteLinks.length>0){const e=i.parsedElements.footnoteLinks.map(e=>({footnoteLink:e,page:t.index+1}));r.push.apply(r,e)}if(i.parsedElements.footnotes.length>0){i.type=j.FOOTNOTES;const e=i.parsedElements.footnotes.map(e=>({footnote:e,page:t.index+1}));n.push.apply(n,e)}}),t.items=e}}),new I({...t,messages:["Detected "+a+" formatted words","Found "+o+" links","Detected "+r.length+" footnotes links","Detected "+n.length+" footnotes"]})}};function R(t){var e=0;if(0===t.trim().length)return e;for(var n=0;n<t.length;n++){const r=t.charCodeAt(n);s(r)||32===r||160===r||(e=(e<<5)-e+r,e|=0)}return e}var Y=class extends U{constructor(){super("Remove Repetitive Elements")}transform(t){const e=[],s={},n={};t.pages.forEach(t=>{const r=t.items.reduce((t,e)=>(e.y<t.minY?(t.minElements=[e],t.minY=e.y):e.y===t.minY&&t.minElements.push(e),e.y>t.maxY?(t.maxElements=[e],t.maxY=e.y):e.y===t.maxY&&t.maxElements.push(e),t),{minY:999,maxY:0,minElements:[],maxElements:[]}),o=R(r.minElements.reduce((t,e)=>t+e.text().toUpperCase(),"")),a=R(r.maxElements.reduce((t,e)=>t+e.text().toUpperCase(),""));e.push({minElements:r.minElements,maxElements:r.maxElements,minLineHash:o,maxLineHash:a}),s[o]=s[o]?s[o]+1:1,n[a]=n[a]?n[a]+1:1});var r=0,o=0;return t.pages.forEach((a,i)=>{s[e[i].minLineHash]>=Math.max(3,2*t.pages.length/3)&&(e[i].minElements.forEach(t=>{t.annotation=E}),o++),n[e[i].maxLineHash]>=Math.max(3,2*t.pages.length/3)&&(e[i].maxElements.forEach(t=>{t.annotation=E}),r++)}),new I({...t,messages:["Removed Header: "+r,"Removed Footers: "+o]})}},V=class extends U{constructor(){super("Vertical to Horizontal Text")}transform(t){var e=0;return t.pages.forEach(t=>{const s=new z;s.consumeAll(t.items),t.items=s.complete(),e+=s.foundVerticals}),new I({...t,messages:["Converted "+e+" verticals"]})}},z=class extends F{foundVerticals;constructor(){super(),this.foundVerticals=0}shouldStash(t){return 1===t.words.length&&1===t.words[0].string.length}doMatchesStash(t,e){return t.y-e.y>5&&t.words[0].type===e.words[0].type}doFlushStash(t,e){if(t.length>5){var s=[],n=999,r=0,o=0,a=0;t.forEach(t=>{t.annotation=E,e.push(t),s.push(t.words[0]),n=Math.min(n,t.x),r=Math.max(r,t.y),o+=t.width,a=Math.max(a,t.height)}),e.push(new k({...t[0],x:n,y:r,width:o,height:a,words:s,annotation:T})),this.foundVerticals++}else e.push(...t)}},J=class{headlineCharCodes;stackedLineItems;stackedChars;constructor(t){this.headlineCharCodes=o(t.headline),this.stackedLineItems=[],this.stackedChars=0}consume(t){const e=o(t.text());if(this.matchAll(e)){if(this.stackedLineItems.push(t),this.stackedChars+=e.length,this.stackedChars===this.headlineCharCodes.length)return this.stackedLineItems}else this.stackedChars>0&&(this.stackedChars=0,this.stackedLineItems=[],this.consume(t));return null}matchAll(t){for(var e=0;e<t.length;e++){const s=this.headlineCharCodes[this.stackedChars+e];if(t[e]!==s)return!1}return!0}},K=class extends U{constructor(){super("Detect TOC")}transform(t){const e=[],o=Math.min(20,t.pages.length),a=new G;var i,c,h=[];t.pages.slice(0,o).forEach(t=>{var o=0;const l=new Set,m=[];var p,u;if(t.items.filter(t=>t.words.includes(t=>r(t.string,"."))).forEach(t=>{var e=t.words.filter(t=>!r(t.string,"."));const a=[];for(;e.length>0&&n(e[e.length-1].string);){const t=e.pop();a.unshift(t.string)}if(0===a.length&&e.length>0){const t=e[e.length-1];for(;s(t.string.charCodeAt(t.string.length-1));)a.unshift(t.string.charAt(t.string.length-1)),t.string=t.string.substring(0,t.string.length-1)}var i=a.length>0;i?(i=!0,p&&(e.push(...p),p=null),m.push(new Z({pageNumber:parseInt(a.join("")),lineItem:new k({...t,words:e})})),o++):c?(p&&l.add(u),p=e,u=t):c=t}),100*o/t.items.length>75){e.push(t.index+1),i=t,a.levelPageItems(m),h.push(...m);const s=[];t.items.forEach(t=>{l.has(t)||(t.annotation=E),s.push(t),t===c&&s.push(new k({...t,type:j.H2,annotation:T}))}),t.items=s}else c=null});var m=h.length;const p=[],u=[],d={};if(e.length>0){h.forEach(t=>{i.items.push(new k({words:[new C({string:" ".repeat(3*t.level)+"-"})].concat(t.lineItem.words),type:j.TOC,annotation:T}))});const e=function(t,e){for(var s of e){const e=Q(t,s.lineItem.text());if(e)return e.index-s.pageNumber}return null}(t.pages.filter(t=>t.index>i.index),h);h.forEach(s=>{var n,r=t.pages[s.pageNumber+e];r&&((n=X(r,s.lineItem.text()))||(r=t.pages[s.pageNumber+e+1])&&(n=X(r,s.lineItem.text()))),n?function(t,e,s,n){s.headlineItems.forEach(t=>t.annotation=E);const r=j.headlineByLevel(e.level+2),o=s.headlineItems.reduce((t,e)=>Math.max(t,e.height),0);t.items.splice(s.lineIndex+1,0,new k({...s.headlineItems[0],words:e.lineItem.words,height:o,type:r,annotation:T}));var a=n[r.name];a?(a.min=Math.min(a.min,o),a.max=Math.max(a.max,o)):(a={min:o,max:o},n[r.name]=a)}(r,s,n,d):p.push(s)});var f=i.index+2,g=[];const s=e=>{g.length>0&&(g.forEach(s=>{const n=j.headlineByLevel(s.level+2),r=d[n.name];if(r){const[o,a]=function(t,e,s,n,r){const o=e.lineItem.text().toUpperCase();for(var a=n;a<=r;a++){const e=t[a-1];if(e){const t=e.items.findIndex(t=>!t.type&&!t.annotation&&t.height>=s.min&&t.height<=s.max&&l(o,t.text())>=.5);if(t>-1)return[a-1,t]}}return[-1,-1]}(t.pages,s,r,f,e);if(a>-1){const e=t.pages[o];e.items[a].annotation=E,e.items.splice(a+1,0,new k({...s.lineItem,type:n,annotation:T})),u.push(s)}}}),g=[])};p.length>0&&(h.forEach(t=>{p.includes(t)?g.push(t):(s(t.pageNumber),f=t.pageNumber)}),g.length>0&&s(t.pages.length))}const y=[];return y.push("Detected "+e.length+" table of content pages"),e.length>0&&(y.push("TOC headline heights: "+JSON.stringify(d)),y.push("Found TOC headlines: "+(m-p.length+u.length)+"/"+m)),p.length>0&&(y.push("Found TOC headlines (by size): "+u.map(t=>t.lineItem.text())),y.push("Missing TOC headlines: "+p.filter(t=>!u.includes(t)).map(t=>t.lineItem.text()+"=>"+t.pageNumber))),new I({...t,globals:{...t.globals,tocPages:e,headlineTypeToHeightRange:d},messages:y})}};function Q(t,e){for(var s of t)if(X(s,e))return s;return null}function X(t,e){const s=new J({headline:e});var n=0;for(var r of t.items){const t=s.consume(r);if(t)return{lineIndex:n,headlineItems:t};n++}return null}var G=class{levelByMethod;uniqueFonts;constructor(){this.levelByMethod=null,this.uniqueFonts=[]}levelPageItems(t){if(!this.levelByMethod)if(this.calculateUniqueX(t).length>1)this.levelByMethod=this.levelByXDiff;else{const e=this.calculateUniqueFonts(t);e.length>1?(this.uniqueFonts=e,this.levelByMethod=this.levelByFont):this.levelByMethod=this.levelToZero}this.levelByMethod(t)}levelByXDiff(t){const e=this.calculateUniqueX(t);t.forEach(t=>{t.level=e.indexOf(t.lineItem.x)})}levelByFont(t){t.forEach(t=>{t.level=this.uniqueFonts.indexOf(t.lineItem.font)})}levelToZero(t){t.forEach(t=>{t.level=0})}calculateUniqueX(t){var e=t.reduce(function(t,e){return t.indexOf(e.lineItem.x)<0&&t.push(e.lineItem.x),t},[]);return e.sort((t,e)=>t-e),e}calculateUniqueFonts(t){return t.reduce(function(t,e){return t.indexOf(e.lineItem.font)<0&&t.push(e.lineItem.font),t},[])}},Z=class{lineItem;pageNumber;level;constructor(t){this.lineItem=t.lineItem,this.pageNumber=t.pageNumber,this.level=0}},tt=class extends U{constructor(){super("Detect List Items")}transform(t){var e=0,s=0;return t.pages.forEach(t=>{const n=[];t.items.forEach(t=>{if(n.push(t),!t.type){var r=t.text();if(a(t.words[0].string))if(e++,"-"===t.words[0].string)t.annotation=O,t.type=j.LIST;else{t.annotation=E;const e=t.words.map(t=>new C({...t}));e[0].string="-",n.push(new k({...t,words:e,annotation:T,type:j.LIST}))}else/^[\s]*[\d]*[.][\s].*$/g.test(r)&&(s++,t.annotation=O,t.type=j.LIST)}}),t.items=n}),new I({...t,messages:["Detected "+e+" plain list items.","Detected "+s+" numbered list items."]})}},et=class extends U{constructor(){super("Detect Headers")}transform(t){const{tocPages:e,headlineTypeToHeightRange:s,mostUsedHeight:n,mostUsedDistance:r,mostUsedFont:o,maxHeight:a}=t.globals,c=e.length>0;var l=0;const h=function(t,e){const s=new Set;return t.forEach(t=>{t.items.forEach(n=>{n.type||n.height!==e||s.add(t)})}),s}(t.pages,a),m=n+(a-n)/4;if(h.forEach(t=>{t.items.forEach(t=>{const e=t.height;!t.type&&e>m&&(t.type=e===a?j.H1:j.H2,t.annotation=O,l++)})}),c)Object.keys(s).forEach(e=>{var r=s[e];r.max>n&&t.pages.forEach(t=>{t.items.forEach(t=>{t.type||t.height!==r.max||(t.annotation=O,t.type=j.enumValueOf(e),l++)})})});else{const e=[];t.pages.forEach(t=>{t.items.forEach(t=>{!t.type&&t.height>n&&!i(t.text())&&(e.includes(t.height)||e.push(t.height))})}),e.sort((t,e)=>e-t),e.forEach((e,s)=>{if(s+2<=6){const n=j.headlineByLevel(2+s);t.pages.forEach(t=>{t.items.forEach(t=>{t.type||t.height!==e||i(t.text())||(l++,t.annotation=O,t.type=n)})})}})}var p=1;if(t.pages.forEach(t=>{t.items.forEach(t=>{t.type&&t.type.headline&&(p=Math.max(p,t.type.headlineLevel))})}),p<6){const e=j.headlineByLevel(p+1);t.pages.forEach(t=>{var s;t.items.forEach(t=>{!t.type&&t.height===n&&t.font!==o&&(!s||s.y<t.y||s.type&&s.type.headline||s.y-t.y>2*r)&&t.text()===t.text().toUpperCase()&&(l++,t.annotation=O,t.type=e),s=t})})}return new I({...t,messages:["Detected "+l+" headlines."]})}};var st=class extends f{items;constructor(t){super(t),this.items=[],t.items&&t.items.forEach(t=>this.addItem(t))}addItem(t){if(this.type&&t.type&&this.type!==t.type)throw new Error(`Adding item of type ${t.type.name} to block of type ${this.type.name}`);this.type||(this.type=t.type),t.parsedElements&&(this.parsedElements?this.parsedElements.add(t.parsedElements):this.parsedElements=t.parsedElements);const e=new k({...t});e.type=null,this.items.push(e)}},nt=class t extends x{constructor(e){if(super(e,st.name),this.constructor===t)throw new TypeError("Can not construct abstract class.")}completeTransform(t){return t.messages=[],t.pages.forEach(t=>{t.items=t.items.filter(t=>!t.annotation||t.annotation!==E),t.items.forEach(t=>t.annotation=null)}),t}},rt=class extends nt{constructor(){super("Gather Blocks")}transform(t){const{mostUsedDistance:e}=t.globals;var s=0,n=0;return t.pages.map(t=>{n+=t.items.length;const r=[];var o=new st({});const a=()=>{o.items.length>1&&(o.annotation=O),r.push(o),o=new st({}),s++};var i=function(t){var e=999;return t.forEach(t=>{e=Math.min(e,t.x)}),999===e?null:e}(t.items);t.items.forEach(t=>{o.items.length>0&&function(t,e,s,n){if(t.type&&t.type.mergeFollowingNonTypedItems&&!e.type)return!1;const r=t.items[t.items.length-1],o=function(t,e,s,n){const r=t.y-e.y;if(r<0-n/2)return!0;var o=n+1;t.x>s&&e.x>s&&(o=n+n/2);return r>o}(r,e,s,n);if(t.type&&t.type.mergeFollowingNonTypedItemsWithSmallDistance&&!e.type&&!o)return!1;if(e.type!==t.type)return!0;return e.type?!e.type.mergeToBlock:o}(o,t,i,e)&&a(),o.addItem(t)}),o.items.length>0&&a(),t.items=r}),new I({...t,messages:["Gathered "+s+" blocks out of "+n+" line items"]})}};var ot=class extends nt{constructor(){super("$1")}transform(t){const e=t.globals.mostUsedHeight??0;var s=0;return t.pages.forEach(t=>{var n=function(t){var e=999;return t.forEach(t=>{t.items.forEach(t=>{e=Math.min(e,t.x)})}),999===e?null:e}(t.items);t.items.forEach(t=>{!t.type&&function(t,e,s){if(0===e.length)return!1;if(1===e.length)return e[0].x>t&&e[0].height<=s+1;for(var n of e)if(n.x===t)return!1;return!0}(n,t.items,e)&&(t.annotation=O,t.type=j.CODE,s++)})}),new I({...t,messages:["Detected "+s+" code/quote items."]})}};var at,it=class extends nt{constructor(){super("Level Lists")}transform(t){var e=0,s=0;return t.pages.forEach(t=>{t.items.filter(t=>t.type===j.LIST).forEach(t=>{var n,r=0;const o={};var a=!1;t.items.forEach(t=>{n?t.x>n?(r++,o[t.x]=r):t.x<n&&(r=o[t.x]):o[t.x]=0,r>0&&(t.words=[new C({string:" ".repeat(3*r)})].concat(t.words),a=!0),n=t.x}),e++,a?(s++,t.annotation=L):t.annotation=b})}),new I({...t,messages:["Modified "+s+" / "+e+" list blocks."]})}},ct=class extends x{constructor(){super("To Text Blocks","TextBlock")}transform(t){return t.pages.forEach(t=>{const e=[];t.items.forEach(t=>{const s=t.type?t.type.name:"Unknown";e.push({category:s,text:j.blockToText(t)})}),t.items=e}),new I({...t})}},lt=class extends x{constructor(){super("To HTML","String")}transform(t){return t.pages.forEach(t=>{var e="";t.items.forEach(t=>{let s;s="TOC"===t.category?t.text:t.text.replace(/(\r\n|\n|\r)/gm,"\n"),"LIST"!==t.category&&(s=s.split("- ").join("")),"CODE"===t.category&&(s=s.split("<code>").join("").split("</code>").join("")),e+=`<p>${s}</p>\n\n`}),t.items=[e]}),new I({...t})}};async function ht(t,e={}){const{addPageNumbers:s=!1,addCitation:n=!0,liteParseOptions:r={}}=e,o="string"==typeof t?await w(t,{responseType:"arraybuffer",timeout:10}):t;let a;try{({LiteParse:a}=await import("@llamaindex/liteparse"))}catch(u){return{error:`method: 'liteparse' requires the optional @llamaindex/liteparse dependency (Node.js only): ${u.message}`}}const i=new a({quiet:!0,ocrEnabled:!1,ocrFailureFatal:!1,...r});let l;try{l=await i.parse(new Uint8Array(o))}catch(u){return{error:u.message}}const h=l.pages.reduce((t,e)=>{const n=e.text.split(/\n{2,}/).filter(t=>t.trim().length>0).map(t=>c(t).replace(/\n/g,"<br/>")).join("</p><p>");return t+`<p id="page-${e.pageNum}">${s?` [${e.pageNum}] `:""}${n}</p>`},"");let m,p;return n&&(p=l.creator||l.producer,m=l.pages[0]?.text.split("\n").find(t=>t.trim().length>0)),{author:p,title:m,html:h,format:"pdf"}}async function mt(t,e={}){const{addPageNumbers:s=!1,addCitation:n=!0,liteParseOptions:r={}}=e,o="string"==typeof t?await w(t,{responseType:"arraybuffer",timeout:10}):t;let a;try{a=await import("@llamaindex/liteparse-wasm"),await async function(t){at||(at=t.default()),await at}(a)}catch(u){return{error:`method: 'liteparse-wasm' requires the optional @llamaindex/liteparse-wasm dependency: ${u.message}`}}const i=new a.LiteParse({quiet:!0,ocrEnabled:!1,ocrFailureFatal:!1,...r});let l;try{l=await i.parse(new Uint8Array(o))}catch(u){return{error:u.message}}const h=l.pages.reduce((t,e)=>{const n=e.text.split(/\n{2,}/).filter(t=>t.trim().length>0).map(t=>c(t).replace(/\n/g,"<br/>")).join("</p><p>");return t+`<p id="page-${e.pageNum}">${s?` [${e.pageNum}] `:""}${n}</p>`},"");let m,p;return n&&(p=l.creator||l.producer,m=l.pages[0]?.text.split("\n").find(t=>t.trim().length>0)),{author:p,title:m,html:h,format:"pdf"}}var pt=null;async function ut(){return pt||(pt=import("https://cdn.jsdelivr.net/npm/pdfjs-serverless@1/+esm").catch(()=>import("pdfjs-serverless")).then(({resolvePDFJS:t})=>t())),pt}var dt=/\b(?:fig(?:ure)?s?|table|chart|graph|diagram|infographic|exhibit|illustration|plate|scheme)\s*\.?\s*(?:\d+(?:\.\d+)*|[ivxlcdm]+)\b/gi,ft=/^\s*\S[^\t\n]{0,60}?(?:\t+| {2,})\S[^\t\n]{0,60}?(?:\t+| {2,})\S/,gt=/(?:[-+]?[\d][\d.,]*\s*[%$€£]?\s+){3,}[-+]?[\d]/,yt=/[\uFFFD\u0000-\u0008\u000B\u000C\u000E-\u001F]/g;function wt(t,e={}){const{sparseTextThreshold:s=200,minTableRows:n=3,minNumericRows:r=2}=e,o=t.map((t,e)=>{const o=[],a=(t||"").replace(/\s+/g," ").trim();0===a.length?o.push("no-text"):a.length<s&&o.push("sparse-text");const i=(t||"").match(yt)?.length||0;a.length>0&&i/a.length>.05&&o.push("garbled-text");const c=[...(t||"").matchAll(dt)].map(t=>t[0].replace(/\s+/g," ").trim()),l=[...(t||"").matchAll(new RegExp(`^\\s*${dt.source}`,"gim"))].map(t=>t[0].trim());l.some(t=>/^table/i.test(t))&&o.push("table-caption"),l.some(t=>!/^table/i.test(t))&&o.push("figure-caption");const h=(t||"").split(/\r?\n/),m=h.filter(t=>ft.test(t)).length,p=h.filter(t=>gt.test(t)).length;return m>=n&&o.push("table-rows"),p>=r&&o.push("numeric-grid"),{page:e+1,needsOcr:o.length>0,reasons:o,captions:c}}),a=o.filter(t=>t.needsOcr).map(t=>t.page);return{needsOcr:a.length>0,pagesNeedingOcr:a,pages:o}}var xt="onnx-community/granite-docling-258M-ONNX",vt="Convert this page to docling.",Tt=null;async function Et(t,e={}){const{processorUrl:s,prompt:n=vt,maxTokens:r=4096}=e;if(s){const e=await fetch(`${s.replace(/\/$/,"")}/api/v1/convert-base64`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({imageBase64:t,mimeType:"image/png",prompt:n,maxTokens:r})}),o=await e.json();if(!e.ok||!o?.success)throw new Error(o?.error||`Processor error ${e.status}`);return o.result}const{model:o,processor:a,lib:i}=await async function(){return Tt||(Tt=(async()=>{const t=await import("@huggingface/transformers"),e=await t.AutoProcessor.from_pretrained(xt);return{model:await t.AutoModelForVision2Seq.from_pretrained(xt,{dtype:"fp32"}),processor:e,lib:t}})()),Tt}(),c=await i.load_image(`data:image/png;base64,${t}`),l=[{role:"user",content:[{type:"image"},{type:"text",text:n}]}],h=await a(a.apply_chat_template(l,{add_generation_prompt:!0}),[c],{do_image_splitting:!0}),m=await o.generate({...h,max_new_tokens:r}),[p]=a.batch_decode(m.slice(null,[h.input_ids.dims.at(-1),null]),{skip_special_tokens:!0});return p}async function bt(t,e=2){const s=t.getViewport({scale:e}),n=await async function(t,e){if("undefined"!=typeof document){const s=document.createElement("canvas");return s.width=t,s.height=e,s}try{return(await import("@napi-rs/canvas")).createCanvas(t,e)}catch{}if("undefined"!=typeof OffscreenCanvas)return new OffscreenCanvas(t,e);throw new Error("No canvas available to rasterize PDF pages — install @napi-rs/canvas in Node.js, or run in a browser/Worker with OffscreenCanvas")}(Math.ceil(s.width),Math.ceil(s.height)),r=n.getContext("2d");return await t.render({canvasContext:r,viewport:s}).promise,async function(t){if("function"==typeof t.toBuffer)return t.toBuffer("image/png").toString("base64");if("function"==typeof t.convertToBlob){const e=await t.convertToBlob({type:"image/png"}),s=new Uint8Array(await e.arrayBuffer());let n="";for(const t of s)n+=String.fromCharCode(t);return btoa(n)}return t.toDataURL("image/png").split(",")[1]}(n)}async function Ot(t,e,s={}){const n=new Map;for(const o of e)try{const e=await Et(await bt(await t.getPage(o),s.scale),s);n.set(o,Lt(e))}catch(r){console.error(`Docling OCR failed for page ${o}:`,r)}return n}function Lt(t){let e=(t||"").replace(/<\/?doctag>/g,"").replace(/<loc_\d+>/g,"").replace(/<page_break>/g,"").replace(/<end_of_utterance>/g,"").replace(/<page_(?:header|footer)>[\s\S]*?<\/page_(?:header|footer)>/g,"");e=e.replace(/<otsl>([\s\S]*?)<\/otsl>/g,(t,e)=>function(t){return`<table>${t.split("<nl>").map(t=>t.trim()).filter(Boolean).map(t=>{const e=[...t.matchAll(/<(fcel|ched|rhed|ecel|lcel|ucel|xcel)>([^<]*)/g)];return e.length?`<tr>${e.map(([,t,e])=>{const s="ched"===t||"rhed"===t?"th":"td";return`<${s}>${e.trim()}</${s}>`}).join("")}</tr>`:""}).filter(Boolean).join("")}</table>`}(e));for(const[s,n,r]of[[/<title>([\s\S]*?)<\/title>/g,"<h1>","</h1>"],[/<section_header_level_1>([\s\S]*?)<\/section_header_level_1>/g,"<h2>","</h2>"],[/<section_header_level_2>([\s\S]*?)<\/section_header_level_2>/g,"<h3>","</h3>"],[/<section_header_level_3>([\s\S]*?)<\/section_header_level_3>/g,"<h4>","</h4>"],[/<section_header_level_[4-9]>([\s\S]*?)<\/section_header_level_[4-9]>/g,"<h5>","</h5>"],[/<(?:text|paragraph)>([\s\S]*?)<\/(?:text|paragraph)>/g,"<p>","</p>"],[/<caption>([\s\S]*?)<\/caption>/g,"<figcaption>","</figcaption>"],[/<(?:picture|chart)>([\s\S]*?)<\/(?:picture|chart)>/g,"<figure>","</figure>"],[/<code>([\s\S]*?)<\/code>/g,"<pre><code>","</code></pre>"],[/<formula>([\s\S]*?)<\/formula>/g,'<code class="formula">',"</code>"],[/<footnote>([\s\S]*?)<\/footnote>/g,'<p class="footnote">',"</p>"],[/<list_item>([\s\S]*?)<\/list_item>/g,"<li>","</li>"],[/<unordered_list>([\s\S]*?)<\/unordered_list>/g,"<ul>","</ul>"],[/<ordered_list>([\s\S]*?)<\/ordered_list>/g,"<ol>","</ol>"]])e=e.replace(s,(t,e)=>`${n}${e.trim()}${r}`);return e=e.replace(/<\/?(?:[a-z][a-z0-9]*_[a-z0-9_]+|otsl|smiles)>/g,""),e.trim()}exports.convertPDFToHTML=async function(t,e={}){if("liteparse"===e.method){const{method:s,...n}=e;return ht(t,n)}if("liteparse-wasm"===e.method){const{method:s,...n}=e;return mt(t,n)}var{addPageNumbers:s=!1,addCitation:n=!0}=e;const r=e.processor??"frontend",o=/^https?:\/\//i.test(r),a=o?"docling":r,i={...e.doclingOptions,processorUrl:o?r:e.processorUrl};var c="string"==typeof t?await w(t,{responseType:"arraybuffer",timeout:10}):t;let l;try{const{getDocument:t}=await ut();l=await t({data:new Uint8Array(c),useSystemFonts:!0,verbosity:0}).promise}catch(k){return{error:k.message}}const h=[...Array(l.numPages).keys()].map(t=>new y({index:t}));let m,f={};for(let d=1;d<=l.numPages;d++){const t=await l.getPage(d),e=await t.getTextContent();if(!(Object.keys(f).length<10)){m=u(f);break}f=p(f,t.pageNumber-1,e.items)}let x=m?m.pageNum:0;for(let p=1;p<=l.numPages;p++){const t=await l.getPage(p);await t.getOperatorList();const e=t.getViewport({scale:1});let s=await t.getTextContent();m&&t.pageIndex>=m.pageIndex&&(s=d(s,x),x++);const n=s.items.map(t=>{const s=[1,0,0,1,0,0];for(let o=0;o<6;o++)s[o]+=t.transform[o]*e.transform[o%2?3:0],o%2&&(s[o+1]+=t.transform[o]*e.transform[1]);const n=Math.sqrt(s[2]*s[2]+s[3]*s[3]),r=t.height/n;return new g({x:Math.round(t.transform[4]),y:Math.round(t.transform[5]),width:Math.round(t.width),height:Math.round(r<=1?t.height:r),text:t.str,font:t.fontName})});h[t.pageNumber-1].items=n}const v=h.map(t=>Object.values(t.items.reduce((t,e)=>((t[e.y]=t[e.y]||[]).push(e.text),t),{})).map(t=>t.join(" ")).join("\n"));var T=new I({pages:h});let E;[new P,new q,new Y,new V,new K,new et,new tt,new rt,new ot,new it,new ct,new lt].forEach(t=>{E&&(T=E.completeTransform(T)),T=t.transform(T),E=t});var b=T.pages.map((t,e)=>`<p id="page-${e+1}">${s?` [${e+1}] `:""}${t.items.join('</p><p id="page-'+e+'">')}</p>`);const O=wt(v,e.ocrScanOptions);if("docling"===a||"hybrid"===a){const t="docling"===a?b.map((t,e)=>e+1):O.pagesNeedingOcr,e=await Ot(l,t,i);for(const[n,r]of e)b[n-1]=`<section class="ocr-page" id="page-${n}">${s?` [${n}] `:""}${r}</section>`}var L=b.join("");if(n){var{Author:S,Title:C}=(await l.getMetadata()).info;C=L.slice(0,400).match(/<h[0-9]>(.*?)<\/h[0-9]>/)?.[1]||C}return{author:S,title:C,html:L,format:"pdf",processor:a,ocrScan:O}},exports.convertPDFToHTMLWithLiteParse=ht,exports.convertPDFToHTMLWithLiteParseWasm=mt,exports.detectPdfNeedsOcr=async function(t,e={}){const s="string"==typeof t?await w(t,{responseType:"arraybuffer",timeout:10}):t,{LiteParse:n}=await import("@llamaindex/liteparse"),r=await new n({quiet:!0,...e.liteParseOptions}).isComplex(new Uint8Array(s));return{needsOcr:r.some(t=>t.needsOcr),pages:r,reasons:[...new Set(r.flatMap(t=>t.reasons))]}},exports.doctagsToHtml=Lt,exports.loadPdfJs=ut,exports.ocrImageWithDocling=Et,exports.ocrPdfPagesWithDocling=Ot,exports.renderPdfPageToPngBase64=bt,exports.scanPagesForOCR=wt;
|
package/dist/pdf-to-html.d.ts
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { convertPDFToHTMLWithLiteParse, LiteParseHTMLOptions } from './liteparse-to-html';
|
|
2
2
|
import { convertPDFToHTMLWithLiteParseWasm } from './liteparse-wasm-to-html';
|
|
3
|
+
import { OcrScanResult, ScanPagesForOCROptions } from './ocr-page-scan';
|
|
4
|
+
import { DoclingOcrOptions } from './docling-ocr';
|
|
5
|
+
/**
|
|
6
|
+
* Where the OCR-capable Docling processing happens for
|
|
7
|
+
* {@link convertPDFToHTML}.
|
|
8
|
+
* - `"frontend"` (default) — all pages parsed by the pure-JS text-layer
|
|
9
|
+
* pipeline; no OCR, no model, works everywhere.
|
|
10
|
+
* - `"hybrid"` — every page goes through the JS pipeline, then a regex scan
|
|
11
|
+
* ({@link scanPagesForOCR}) flags pages containing infographics/figures/
|
|
12
|
+
* tables (or with no usable text layer) and only those pages are re-done
|
|
13
|
+
* with the Granite Docling OCR model.
|
|
14
|
+
* - `"docling"` — every page is rasterized and OCR'd with Granite Docling
|
|
15
|
+
* (in-process via the optional `@huggingface/transformers` dependency, or
|
|
16
|
+
* remotely when `processorUrl` is set).
|
|
17
|
+
* - any `http(s)://` URL — like `"docling"`, but all pages are sent to that
|
|
18
|
+
* docling-compatible processor API (this package's `server/`, or another
|
|
19
|
+
* deployment).
|
|
20
|
+
*/
|
|
21
|
+
export type ProcessorMode = "frontend" | "hybrid" | "docling" | (string & {});
|
|
3
22
|
/**
|
|
4
23
|
* Which parsing engine {@link convertPDFToHTML} runs.
|
|
5
24
|
* - `"ts-block-algorithm"` (default) — the pure-TS pipeline in this file: groups
|
|
@@ -30,6 +49,16 @@ export type ParseMethod = "ts-block-algorithm" | "liteparse" | "liteparse-wasm";
|
|
|
30
49
|
* @param {boolean} options.removePageHeaders default=true - Removes repeated headers found on each page
|
|
31
50
|
* @param {ParseMethod} options.method default="ts-block-algorithm" - Parsing engine to use;
|
|
32
51
|
* `"liteparse"` delegates to LiteParse (Node.js only, see {@link ParseMethod})
|
|
52
|
+
* @param {ProcessorMode} options.processor default="frontend" - Where OCR happens:
|
|
53
|
+
* `"frontend"` (all JS, no OCR), `"hybrid"` (regex-scan pages for
|
|
54
|
+
* infographics/tables and OCR only those), `"docling"` (OCR every page), or
|
|
55
|
+
* the URL of a docling-compatible processor API (see {@link ProcessorMode})
|
|
56
|
+
* @param {string} options.processorUrl - Remote docling-compatible API base URL
|
|
57
|
+
* used by `"hybrid"`/`"docling"` instead of the in-process model
|
|
58
|
+
* @param {Object} options.ocrScanOptions - Threshold tuning for the hybrid
|
|
59
|
+
* page scan, see {@link ScanPagesForOCROptions}
|
|
60
|
+
* @param {Object} options.doclingOptions - Prompt/maxTokens/scale for the OCR
|
|
61
|
+
* model, see {@link DoclingOcrOptions}
|
|
33
62
|
* @returns {string|Object} HTML formatted text
|
|
34
63
|
* @category Extract
|
|
35
64
|
* @author [vtempest (2025)](https://github.com/vtempest),
|
|
@@ -40,6 +69,10 @@ export declare function convertPDFToHTML(pdfURLOrBuffer: any, options?: {
|
|
|
40
69
|
addPageNumbers?: boolean;
|
|
41
70
|
addCitation?: boolean;
|
|
42
71
|
method?: ParseMethod;
|
|
72
|
+
processor?: ProcessorMode;
|
|
73
|
+
processorUrl?: string;
|
|
74
|
+
ocrScanOptions?: ScanPagesForOCROptions;
|
|
75
|
+
doclingOptions?: Omit<DoclingOcrOptions, "processorUrl">;
|
|
43
76
|
} & Pick<LiteParseHTMLOptions, "liteParseOptions">): Promise<{
|
|
44
77
|
error: any;
|
|
45
78
|
author?: undefined;
|
|
@@ -53,10 +86,12 @@ export declare function convertPDFToHTML(pdfURLOrBuffer: any, options?: {
|
|
|
53
86
|
format: string;
|
|
54
87
|
error?: undefined;
|
|
55
88
|
} | {
|
|
56
|
-
author
|
|
57
|
-
title
|
|
89
|
+
author?: string;
|
|
90
|
+
title?: string;
|
|
58
91
|
html: string;
|
|
59
92
|
format: string;
|
|
93
|
+
processor: string;
|
|
94
|
+
ocrScan: OcrScanResult;
|
|
60
95
|
}>;
|
|
61
96
|
export { convertPDFToHTMLWithLiteParse };
|
|
62
97
|
export type { LiteParseHTMLOptions } from './liteparse-to-html';
|
|
@@ -64,3 +99,8 @@ export { convertPDFToHTMLWithLiteParseWasm };
|
|
|
64
99
|
export type { LiteParseWasmHTMLOptions } from './liteparse-wasm-to-html';
|
|
65
100
|
export { detectPdfNeedsOcr } from './detect-needs-ocr';
|
|
66
101
|
export type { DetectPdfNeedsOcrOptions, PdfOcrAssessment, } from './detect-needs-ocr';
|
|
102
|
+
export { scanPagesForOCR } from './ocr-page-scan';
|
|
103
|
+
export type { OcrScanResult, PageOcrScan, ScanPagesForOCROptions, } from './ocr-page-scan';
|
|
104
|
+
export { doctagsToHtml, ocrImageWithDocling, ocrPdfPagesWithDocling, renderPdfPageToPngBase64, } from './docling-ocr';
|
|
105
|
+
export type { DoclingOcrOptions } from './docling-ocr';
|
|
106
|
+
export { loadPdfJs } from './utils/load-pdfjs';
|