extract-pdf 0.1.114 → 0.1.115

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 CHANGED
@@ -54,10 +54,12 @@ const { html } = await convertPDFToHTML(buffer, { addPageNumbers: true });
54
54
 
55
55
  ### Options
56
56
 
57
- | Option | Default | Description |
58
- | ---------------- | ------- | ------------------------------------------------------------------------------------------ |
59
- | `addPageNumbers` | `false` | Inserts `[n]` markers at each page boundary |
60
- | `addCitation` | `true` | Reads PDF metadata and first-page heading to populate `title`/`author` in the return value |
57
+ | Option | Default | Description |
58
+ | ----------------- | ---------------------- | ------------------------------------------------------------------------------------------ |
59
+ | `addPageNumbers` | `false` | Inserts `[n]` markers at each page boundary |
60
+ | `addCitation` | `true` | Reads PDF metadata and first-page heading to populate `title`/`author` in the return value |
61
+ | `method` | `"ts-block-algorithm"` | Parsing engine — `"ts-block-algorithm"` or `"liteparse"` (see below) |
62
+ | `liteParseOptions`| `{}` | Passed through to LiteParse's constructor when `method: "liteparse"` |
61
63
 
62
64
  ### Return value
63
65
 
@@ -65,6 +67,63 @@ const { html } = await convertPDFToHTML(buffer, { addPageNumbers: true });
65
67
  { html: string, title?: string, author?: string, format: "pdf" }
66
68
  ```
67
69
 
70
+ ## Parse methods
71
+
72
+ `convertPDFToHTML` supports two interchangeable parsing engines via `options.method`:
73
+
74
+ | Method | Engine | Environments | OCR |
75
+ | ------------------------------------ | -------------------------------------------------------------------- | ----------------------------------- | --- |
76
+ | `"ts-block-algorithm"` (default) | The pure-TS pipeline documented below (this package) | Node.js, Cloudflare Workers, browser | No |
77
+ | `"liteparse"` | [LiteParse](https://github.com/run-llama/liteparse) (native, `@llamaindex/liteparse`) | Node.js only | Optional |
78
+
79
+ ```ts
80
+ import { convertPDFToHTML } from "extract-pdf";
81
+
82
+ const { html } = await convertPDFToHTML(buffer, { method: "liteparse" });
83
+ ```
84
+
85
+ LiteParse ships a native (napi) addon, so `method: "liteparse"` only runs in
86
+ Node.js — it is not bundled into browser or Cloudflare Workers builds. Install
87
+ it explicitly (`bun add @llamaindex/liteparse`) since it's an optional
88
+ dependency; if it isn't installed, `convertPDFToHTML` returns `{ error }`
89
+ instead of throwing.
90
+
91
+ By default the LiteParse path runs with OCR disabled (`ocrEnabled: false`) —
92
+ matching this package's "instant, no backend" philosophy. Use
93
+ `detectPdfNeedsOcr` (below) to decide when a document is worth re-parsing with
94
+ `liteParseOptions: { ocrEnabled: true }`.
95
+
96
+ ## Detecting whether a PDF needs OCR
97
+
98
+ Before committing to a full (and potentially slow) OCR parse, `detectPdfNeedsOcr`
99
+ runs a cheap, text-layer-only pass and reports whether each page needs OCR or
100
+ other heavy parsing — useful for routing documents to different pipelines
101
+ (fast path vs. OCR vs. screenshots vs. a heavier parser like LlamaParse or
102
+ Docling):
103
+
104
+ ```ts
105
+ import { detectPdfNeedsOcr, convertPDFToHTML } from "extract-pdf";
106
+
107
+ const assessment = await detectPdfNeedsOcr(buffer);
108
+ // { needsOcr: boolean, pages: PageComplexityStats[], reasons: string[] }
109
+
110
+ if (!assessment.needsOcr) {
111
+ const { html } = await convertPDFToHTML(buffer, { method: "liteparse" });
112
+ } else {
113
+ console.log("Needs OCR:", assessment.reasons); // e.g. ["scanned", "sparse-text"]
114
+ // Route to an OCR-enabled pipeline, e.g.:
115
+ const { html } = await convertPDFToHTML(buffer, {
116
+ method: "liteparse",
117
+ liteParseOptions: { ocrEnabled: true },
118
+ });
119
+ }
120
+ ```
121
+
122
+ `reasons` collects every distinct signal found across pages: `"scanned"`,
123
+ `"no-text"`, `"sparse-text"`, `"embedded-images"`, `"garbled"`, or
124
+ `"vector-text"`. Like `method: "liteparse"`, this is Node.js only and requires
125
+ `@llamaindex/liteparse`.
126
+
68
127
  ## Pipeline
69
128
 
70
129
  The conversion runs a sequential chain of transformations on a `ParseResult` (pages → items):
@@ -0,0 +1,23 @@
1
+ import { LiteParseConfig, PageComplexityStats } from '@llamaindex/liteparse';
2
+ export interface DetectPdfNeedsOcrOptions {
3
+ liteParseOptions?: Partial<LiteParseConfig>;
4
+ }
5
+ export interface PdfOcrAssessment {
6
+ /** True when any page needs OCR or other advanced parsing. */
7
+ needsOcr: boolean;
8
+ /** Per-page complexity signals, one entry per page. */
9
+ pages: PageComplexityStats[];
10
+ /**
11
+ * Every distinct reason found across all pages, e.g. `"scanned"`,
12
+ * `"no-text"`, `"sparse-text"`, `"embedded-images"`, `"garbled"`,
13
+ * `"vector-text"`. Empty when `needsOcr` is false.
14
+ */
15
+ reasons: string[];
16
+ }
17
+ /**
18
+ * Determines whether a PDF (URL or ArrayBuffer) needs OCR to extract usable
19
+ * text, without running a full parse.
20
+ * @param pdfURLOrBuffer - URL to a PDF file or buffer from fs.readFile
21
+ * @category Extract
22
+ */
23
+ export declare function detectPdfNeedsOcr(pdfURLOrBuffer: any, options?: DetectPdfNeedsOcrOptions): Promise<PdfOcrAssessment>;
@@ -0,0 +1,31 @@
1
+ import { LiteParseConfig } from '@llamaindex/liteparse';
2
+ export interface LiteParseHTMLOptions {
3
+ addPageNumbers?: boolean;
4
+ addCitation?: boolean;
5
+ liteParseOptions?: Partial<LiteParseConfig>;
6
+ }
7
+ /**
8
+ * Converts a PDF (URL or ArrayBuffer) into HTML using LiteParse's spatial text
9
+ * extraction, mirroring the return shape of `convertPDFToHTML`.
10
+ * @param pdfURLOrBuffer - URL to a PDF file or buffer from fs.readFile
11
+ * @param options.addPageNumbers default=false - Adds `[n]` markers at each page boundary
12
+ * @param options.addCitation default=true - Populates `title`/`author` from PDF metadata
13
+ * @param options.liteParseOptions - Passed through to the `LiteParse` constructor;
14
+ * defaults to `{ ocrEnabled: false, ocrFailureFatal: false }` (use detectPdfNeedsOcr
15
+ * to decide when a document is worth re-parsing with `ocrEnabled: true`)
16
+ * @returns `{ html, title, author, format: "pdf" }`, or `{ error }` on failure
17
+ * @category Extract
18
+ */
19
+ export declare function convertPDFToHTMLWithLiteParse(pdfURLOrBuffer: any, options?: LiteParseHTMLOptions): Promise<{
20
+ error: any;
21
+ author?: undefined;
22
+ title?: undefined;
23
+ html?: undefined;
24
+ format?: undefined;
25
+ } | {
26
+ author: string;
27
+ title: string;
28
+ html: any;
29
+ format: string;
30
+ error?: undefined;
31
+ }>;
@@ -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,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 l=(t,e,s)=>Math.floor(t/e*s),c=(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},m=(t,e,s)=>{const n=l(1,6,s.length),o=l(5,6,s.length),r=c(s.slice(0,n),t,e);return c(s.slice(o),r,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 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]}}},f=(t,e)=>{const s={items:[...t.items]},n=l(1,6,s.items.length),o=l(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},p=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}},d=class extends p{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}},g=class{index;items;constructor(t){this.index=t.index,this.items=t.items||[]}},y=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}},w=class{category;color;constructor(t){this.category=t.category,this.color=t.color}},x=new w({category:"Added",color:"green"}),E=new w({category:"Removed",color:"red"}),T=new w({category:"Unchanged",color:"brown"}),v=new w({category:"Detected",color:"green"}),b=new w({category:"Modified",color:"green"}),L=class t extends y{constructor(e){if(super(e,d.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||[]}},O=class{string;type;format;constructor(t){this.string=t.string,this.type=t.type??null,this.format=t.format??null}},S=class extends p{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 O({string:t})))}text(){return this.wordStrings().join(" ")}wordStrings(){return this.words.map(t=>t.string)}},k=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)}},C=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 F(t){t.sort((t,e)=>t.x-e.x)}var N={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>"}},D={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>`}},H=class{fontToFormats;constructor(t){this.fontToFormats=t}compact(t){F(t);const e=new M(this.fontToFormats);e.consumeAll(t.map(t=>new d({...t})));const s=e.complete();var n=0,o=0;return t.forEach(t=>{n=Math.max(n,t.height),o+=t.width}),new S({x:t[0].x,y:t[0].y,height:n,width:o,words:s,parsedElements:new C({footnoteLinks:e.footnoteLinks,footnotes:e.footnotes,containLinks:e.containLinks,formattedWords:e.formattedWords})})}},M=class extends k{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 O({string:`${s}`,type:D.FOOTNOTE_LINK})),this.footnoteLinks.push(parseInt(s))):this.currentItem&&this.currentItem.y<t[0].y?(e.push(new O({string:`${s}`,type:D.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?N[e]:null;return s.filter(t=>t.trim().length>0).map(t=>{var e=null;return t.startsWith("http:")?(this.containLinks=!0,e=D.LINK):t.startsWith("www.")&&(this.containLinks=!0,t=`http://${t}`,e=D.LINK),i&&this.formattedWords++,new O({string:t,type:e,format:i})})}};var B=class extends L{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(U(e)),a=U(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(U(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=N.BOLD_OBLIQUE:s.includes("bold")?o=N.BOLD:s.includes("oblique")||s.includes("italic")?o=N.OBLIQUE:s===n&&(o=N.BOLD),o&&c.set(e,o.name)}),l.sort();const m=t.pages.map(t=>({...t,items:t.items.map(t=>({...t}))}));return new I({...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 U(t){var e=0,s="";return Object.keys(t).map(n=>{(!s||t[n]>e)&&(e=t[n],s=n)}),s}var A=class t extends y{constructor(e){if(super(e,S.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}},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=>{F(t)}),e}},P={H1:{name:"H1",headline:!0,headlineLevel:1,toText:t=>"<h1>"+$(t.items,!0)+"</h1>"},H2:{name:"H2",headline:!0,headlineLevel:2,toText:t=>"<h2>"+$(t.items,!0)+"</h2>"},H3:{name:"H3",headline:!0,headlineLevel:3,toText:t=>"<h3>"+$(t.items,!0)+"</h3>"},H4:{name:"H4",headline:!0,headlineLevel:4,toText:t=>"<h4>"+$(t.items,!0)+"</h4>"},H5:{name:"H5",headline:!0,headlineLevel:5,toText:t=>"<h5>"+$(t.items,!0)+"</h5>"},H6:{name:"H6",headline:!0,headlineLevel:6,toText:t=>"<h6>"+$(t.items,!0)+"</h6>"},TOC:{name:"TOC",mergeToBlock:!0,toText:t=>$(t.items,!0)},FOOTNOTES:{name:"FOOTNOTES",mergeToBlock:!0,mergeFollowingNonTypedItems:!0,toText:t=>"<p>"+$(t.items,!1)+"</p>"},CODE:{name:"CODE",mergeToBlock:!0,toText:t=>"<code>"+$(t.items,!0)+"</code>"},LIST:{name:"LIST",mergeToBlock:!1,mergeFollowingNonTypedItemsWithSmallDistance:!0,toText:t=>"<ul>\n"+$(t.items,!1)+"</ul>"},PARAGRAPH:{name:"PARAGRAPH",toText:t=>"<p>"+$(t.items,!1)+"</p>"}};function $(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}P.enumValueOf=function(t){return P[t]},P.isHeadline=function(t){return t&&2===t.name.length&&"H"===t.name[0]},P.blockToText=function(t){return t.type?t.type.toText(t):$(t.items,!1)},P.headlineByLevel=function(t){return 1===t?P.H1:2===t?P.H2:3===t?P.H3:4===t?P.H4:5===t?P.H5:(t>6&&console.warn("Unsupported headline level: "+t+" (supported are 1-6), defaulting to level 6"),P.H6)};var j=class extends A{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 H(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=x,s.forEach(t=>{t.annotation=E,e.push(new S({...t}))})),0===i.words.length&&(i.annotation=E),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=P.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 "+r+" links","Detected "+o.length+" footnotes links","Detected "+n.length+" footnotes"]})}};function q(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 Y=class extends A{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=q(o.minElements.reduce((t,e)=>t+e.text().toUpperCase(),"")),a=q(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=E}),r++),n[e[i].maxLineHash]>=Math.max(3,2*t.pages.length/3)&&(e[i].maxElements.forEach(t=>{t.annotation=E}),o++)}),new I({...t,messages:["Removed Header: "+o,"Removed Footers: "+r]})}},R=class extends A{constructor(){super("Vertical to Horizontal Text")}transform(t){var e=0;return t.pages.forEach(t=>{const s=new V;s.consumeAll(t.items),t.items=s.complete(),e+=s.foundVerticals}),new I({...t,messages:["Converted "+e+" verticals"]})}},V=class extends k{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=E,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 S({...t[0],x:n,y:o,width:r,height:a,words:s,annotation:x})),this.foundVerticals++}else e.push(...t)}},K=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}},J=class extends A{constructor(){super("Detect TOC")}transform(t){const e=[],r=Math.min(20,t.pages.length),a=new z;var i,l,c=[];t.pages.slice(0,r).forEach(t=>{var r=0;const h=new Set,m=[];var u,f;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 G({pageNumber:parseInt(a.join("")),lineItem:new S({...t,words:e})})),r++):l?(u&&h.add(f),u=e,f=t):l=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=>{h.has(t)||(t.annotation=E),s.push(t),t===l&&s.push(new S({...t,type:P.H2,annotation:x}))}),t.items=s}else l=null});var m=c.length;const u=[],f=[],p={};if(e.length>0){c.forEach(t=>{i.items.push(new S({words:[new O({string:" ".repeat(3*t.level)+"-"})].concat(t.lineItem.words),type:P.TOC,annotation:x}))});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),c);c.forEach(s=>{var n,o=t.pages[s.pageNumber+e];o&&((n=_(o,s.lineItem.text()))||(o=t.pages[s.pageNumber+e+1])&&(n=_(o,s.lineItem.text()))),n?function(t,e,s,n){s.headlineItems.forEach(t=>t.annotation=E);const o=P.headlineByLevel(e.level+2),r=s.headlineItems.reduce((t,e)=>Math.max(t,e.height),0);t.items.splice(s.lineIndex+1,0,new S({...s.headlineItems[0],words:e.lineItem.words,height:r,type:o,annotation:x}));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,p):u.push(s)});var d=i.index+2,g=[];const s=e=>{g.length>0&&(g.forEach(s=>{const n=P.headlineByLevel(s.level+2),o=p[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&&h(r,t.text())>=.5);if(t>-1)return[a-1,t]}}return[-1,-1]}(t.pages,s,o,d,e);if(a>-1){const e=t.pages[r];e.items[a].annotation=E,e.items.splice(a+1,0,new S({...s.lineItem,type:n,annotation:x})),f.push(s)}}}),g=[])};u.length>0&&(c.forEach(t=>{u.includes(t)?g.push(t):(s(t.pageNumber),d=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(p)),y.push("Found TOC headlines: "+(m-u.length+f.length)+"/"+m)),u.length>0&&(y.push("Found TOC headlines (by size): "+f.map(t=>t.lineItem.text())),y.push("Missing TOC headlines: "+u.filter(t=>!f.includes(t)).map(t=>t.lineItem.text()+"=>"+t.pageNumber))),new I({...t,globals:{...t.globals,tocPages:e,headlineTypeToHeightRange:p},messages:y})}};function Q(t,e){for(var s of t)if(_(s,e))return s;return null}function _(t,e){const s=new K({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 z=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},[])}},G=class{lineItem;pageNumber;level;constructor(t){this.lineItem=t.lineItem,this.pageNumber=t.pageNumber,this.level=0}},X=class extends A{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=v,t.type=P.LIST;else{t.annotation=E;const e=t.words.map(t=>new O({...t}));e[0].string="-",n.push(new S({...t,words:e,annotation:x,type:P.LIST}))}else/^[\s]*[\d]*[.][\s].*$/g.test(o)&&(s++,t.annotation=v,t.type=P.LIST)}}),t.items=n}),new I({...t,messages:["Detected "+e+" plain list items.","Detected "+s+" numbered list items."]})}},Z=class extends A{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?P.H1:P.H2,t.annotation=v,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=v,t.type=P.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=P.headlineByLevel(2+s);t.pages.forEach(t=>{t.items.forEach(t=>{t.type||t.height!==e||i(t.text())||(l++,t.annotation=v,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=P.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=v,t.type=e),s=t})})}return new I({...t,messages:["Detected "+l+" headlines."]})}};var tt=class extends p{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 S({...t});e.type=null,this.items.push(e)}},et=class t extends y{constructor(e){if(super(e,tt.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}},st=class extends et{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 tt({});const a=()=>{r.items.length>1&&(r.annotation=v),o.push(r),r=new tt({}),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 I({...t,messages:["Gathered "+s+" blocks out of "+n+" line items"]})}};var nt=class extends et{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=v,t.type=P.CODE,s++)})}),new I({...t,messages:["Detected "+s+" code/quote items."]})}};var ot=class extends et{constructor(){super("Level Lists")}transform(t){var e=0,s=0;return t.pages.forEach(t=>{t.items.filter(t=>t.type===P.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 O({string:" ".repeat(3*o)})].concat(t.words),a=!0),n=t.x}),e++,a?(s++,t.annotation=b):t.annotation=T})}),new I({...t,messages:["Modified "+s+" / "+e+" list blocks."]})}},rt=class extends y{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:P.blockToText(t)})}),t.items=e}),new I({...t})}},at=class extends y{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})}};exports.convertPDFToHTML=async function(t,e={}){var{addPageNumbers:s=!1,addCitation:n=!0}=e,o="string"==typeof t?await async function(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}}(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(E){return{error:E.message}}const a=[...Array(r.numPages).keys()].map(t=>new g({index:t}));let i,h={};for(let f=1;f<=r.numPages;f++){const t=await r.getPage(f),e=await t.getTextContent();if(!(Object.keys(h).length<10)){i=u(h);break}h=m(h,t.pageNumber-1,e.items)}let l=i?i.pageNum:0;for(let m=1;m<=r.numPages;m++){const t=await r.getPage(m);await t.getOperatorList();const e=t.getViewport({scale:1});let s=await t.getTextContent();i&&t.pageIndex>=i.pageIndex&&(s=f(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 d({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 I({pages:a});let p;[new B,new j,new Y,new R,new J,new Z,new X,new st,new nt,new ot,new rt,new at].forEach(t=>{p&&(c=p.completeTransform(c)),c=t.transform(c),p=t});var y=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:w,Title:x}=(await r.getMetadata()).info;x=y.slice(0,400).match(/<h[0-9]>(.*?)<\/h[0-9]>/)?.[1]||x}return{author:w,title:x,html:y,format:"pdf"}};
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,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 l=(t,e,s)=>Math.floor(t/e*s),c=(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},m=(t,e,s)=>{const n=l(1,6,s.length),o=l(5,6,s.length),r=c(s.slice(0,n),t,e);return c(s.slice(o),r,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 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]}}},p=(t,e)=>{const s={items:[...t.items]},n=l(1,6,s.items.length),o=l(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}},d=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}},g=class{index;items;constructor(t){this.index=t.index,this.items=t.items||[]}};async function y(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 w=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}},x=class{category;color;constructor(t){this.category=t.category,this.color=t.color}},E=new x({category:"Added",color:"green"}),T=new x({category:"Removed",color:"red"}),v=new x({category:"Unchanged",color:"brown"}),b=new x({category:"Detected",color:"green"}),L=new x({category:"Modified",color:"green"}),O=class t extends w{constructor(e){if(super(e,d.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!==T),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||[]}},S=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 S({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)}},C=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 N(t){t.sort((t,e)=>t.x-e.x)}var M={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>"}},D={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>`}},H=class{fontToFormats;constructor(t){this.fontToFormats=t}compact(t){N(t);const e=new B(this.fontToFormats);e.consumeAll(t.map(t=>new d({...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 C({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 S({string:`${s}`,type:D.FOOTNOTE_LINK})),this.footnoteLinks.push(parseInt(s))):this.currentItem&&this.currentItem.y<t[0].y?(e.push(new S({string:`${s}`,type:D.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?M[e]:null;return s.filter(t=>t.trim().length>0).map(t=>{var e=null;return t.startsWith("http:")?(this.containLinks=!0,e=D.LINK):t.startsWith("www.")&&(this.containLinks=!0,t=`http://${t}`,e=D.LINK),i&&this.formattedWords++,new S({string:t,type:e,format:i})})}};var U=class extends O{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=M.BOLD_OBLIQUE:s.includes("bold")?o=M.BOLD:s.includes("oblique")||s.includes("italic")?o=M.OBLIQUE:s===n&&(o=M.BOLD),o&&c.set(e,o.name)}),l.sort();const m=t.pages.map(t=>({...t,items:t.items.map(t=>({...t}))}));return new I({...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 P=class t extends w{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!==T),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=>{N(t)}),e}},$={H1:{name:"H1",headline:!0,headlineLevel:1,toText:t=>"<h1>"+j(t.items,!0)+"</h1>"},H2:{name:"H2",headline:!0,headlineLevel:2,toText:t=>"<h2>"+j(t.items,!0)+"</h2>"},H3:{name:"H3",headline:!0,headlineLevel:3,toText:t=>"<h3>"+j(t.items,!0)+"</h3>"},H4:{name:"H4",headline:!0,headlineLevel:4,toText:t=>"<h4>"+j(t.items,!0)+"</h4>"},H5:{name:"H5",headline:!0,headlineLevel:5,toText:t=>"<h5>"+j(t.items,!0)+"</h5>"},H6:{name:"H6",headline:!0,headlineLevel:6,toText:t=>"<h6>"+j(t.items,!0)+"</h6>"},TOC:{name:"TOC",mergeToBlock:!0,toText:t=>j(t.items,!0)},FOOTNOTES:{name:"FOOTNOTES",mergeToBlock:!0,mergeFollowingNonTypedItems:!0,toText:t=>"<p>"+j(t.items,!1)+"</p>"},CODE:{name:"CODE",mergeToBlock:!0,toText:t=>"<code>"+j(t.items,!0)+"</code>"},LIST:{name:"LIST",mergeToBlock:!1,mergeFollowingNonTypedItemsWithSmallDistance:!0,toText:t=>"<ul>\n"+j(t.items,!1)+"</ul>"},PARAGRAPH:{name:"PARAGRAPH",toText:t=>"<p>"+j(t.items,!1)+"</p>"}};function j(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}$.enumValueOf=function(t){return $[t]},$.isHeadline=function(t){return t&&2===t.name.length&&"H"===t.name[0]},$.blockToText=function(t){return t.type?t.type.toText(t):j(t.items,!1)},$.headlineByLevel=function(t){return 1===t?$.H1:2===t?$.H2:3===t?$.H3:4===t?$.H4:5===t?$.H5:(t>6&&console.warn("Unsupported headline level: "+t+" (supported are 1-6), defaulting to level 6"),$.H6)};var q=class extends P{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 H(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=E,s.forEach(t=>{t.annotation=T,e.push(new k({...t}))})),0===i.words.length&&(i.annotation=T),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=$.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 "+r+" links","Detected "+o.length+" footnotes links","Detected "+n.length+" footnotes"]})}};function Y(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 R=class extends P{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=Y(o.minElements.reduce((t,e)=>t+e.text().toUpperCase(),"")),a=Y(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=T}),r++),n[e[i].maxLineHash]>=Math.max(3,2*t.pages.length/3)&&(e[i].maxElements.forEach(t=>{t.annotation=T}),o++)}),new I({...t,messages:["Removed Header: "+o,"Removed Footers: "+r]})}},V=class extends P{constructor(){super("Vertical to Horizontal Text")}transform(t){var e=0;return t.pages.forEach(t=>{const s=new K;s.consumeAll(t.items),t.items=s.complete(),e+=s.foundVerticals}),new I({...t,messages:["Converted "+e+" verticals"]})}},K=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,o=0,r=0,a=0;t.forEach(t=>{t.annotation=T,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:E})),this.foundVerticals++}else e.push(...t)}},J=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}},Q=class extends P{constructor(){super("Detect TOC")}transform(t){const e=[],r=Math.min(20,t.pages.length),a=new G;var i,l,c=[];t.pages.slice(0,r).forEach(t=>{var r=0;const h=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 X({pageNumber:parseInt(a.join("")),lineItem:new k({...t,words:e})})),r++):l?(u&&h.add(p),u=e,p=t):l=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=>{h.has(t)||(t.annotation=T),s.push(t),t===l&&s.push(new k({...t,type:$.H2,annotation:E}))}),t.items=s}else l=null});var m=c.length;const u=[],p=[],f={};if(e.length>0){c.forEach(t=>{i.items.push(new k({words:[new S({string:" ".repeat(3*t.level)+"-"})].concat(t.lineItem.words),type:$.TOC,annotation:E}))});const e=function(t,e){for(var s of e){const e=_(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=z(o,s.lineItem.text()))||(o=t.pages[s.pageNumber+e+1])&&(n=z(o,s.lineItem.text()))),n?function(t,e,s,n){s.headlineItems.forEach(t=>t.annotation=T);const o=$.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:E}));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,f):u.push(s)});var d=i.index+2,g=[];const s=e=>{g.length>0&&(g.forEach(s=>{const n=$.headlineByLevel(s.level+2),o=f[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&&h(r,t.text())>=.5);if(t>-1)return[a-1,t]}}return[-1,-1]}(t.pages,s,o,d,e);if(a>-1){const e=t.pages[r];e.items[a].annotation=T,e.items.splice(a+1,0,new k({...s.lineItem,type:n,annotation:E})),p.push(s)}}}),g=[])};u.length>0&&(c.forEach(t=>{u.includes(t)?g.push(t):(s(t.pageNumber),d=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(f)),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 I({...t,globals:{...t.globals,tocPages:e,headlineTypeToHeightRange:f},messages:y})}};function _(t,e){for(var s of t)if(z(s,e))return s;return null}function z(t,e){const s=new J({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 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},[])}},X=class{lineItem;pageNumber;level;constructor(t){this.lineItem=t.lineItem,this.pageNumber=t.pageNumber,this.level=0}},Z=class extends P{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=b,t.type=$.LIST;else{t.annotation=T;const e=t.words.map(t=>new S({...t}));e[0].string="-",n.push(new k({...t,words:e,annotation:E,type:$.LIST}))}else/^[\s]*[\d]*[.][\s].*$/g.test(o)&&(s++,t.annotation=b,t.type=$.LIST)}}),t.items=n}),new I({...t,messages:["Detected "+e+" plain list items.","Detected "+s+" numbered list items."]})}},tt=class extends P{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?$.H1:$.H2,t.annotation=b,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=b,t.type=$.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=$.headlineByLevel(2+s);t.pages.forEach(t=>{t.items.forEach(t=>{t.type||t.height!==e||i(t.text())||(l++,t.annotation=b,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=$.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=b,t.type=e),s=t})})}return new I({...t,messages:["Detected "+l+" headlines."]})}};var et=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)}},st=class t extends w{constructor(e){if(super(e,et.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!==T),t.items.forEach(t=>t.annotation=null)}),t}},nt=class extends st{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 et({});const a=()=>{r.items.length>1&&(r.annotation=b),o.push(r),r=new et({}),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 I({...t,messages:["Gathered "+s+" blocks out of "+n+" line items"]})}};var ot=class extends st{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=b,t.type=$.CODE,s++)})}),new I({...t,messages:["Detected "+s+" code/quote items."]})}};var rt=class extends st{constructor(){super("Level Lists")}transform(t){var e=0,s=0;return t.pages.forEach(t=>{t.items.filter(t=>t.type===$.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 S({string:" ".repeat(3*o)})].concat(t.words),a=!0),n=t.x}),e++,a?(s++,t.annotation=L):t.annotation=v})}),new I({...t,messages:["Modified "+s+" / "+e+" list blocks."]})}},at=class extends w{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:$.blockToText(t)})}),t.items=e}),new I({...t})}},it=class extends w{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:o={}}=e,r="string"==typeof t?await y(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,...o});let h;try{h=await i.parse(new Uint8Array(r))}catch(u){return{error:u.message}}const l=h.pages.reduce((t,e)=>{const n=e.text.split(/\n{2,}/).filter(t=>t.trim().length>0).map(t=>{return(e=t,e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")).replace(/\n/g,"<br/>");var e}).join("</p><p>");return t+`<p id="page-${e.pageNum}">${s?` [${e.pageNum}] `:""}${n}</p>`},"");let c,m;return n&&(m=h.creator||h.producer,c=h.pages[0]?.text.split("\n").find(t=>t.trim().length>0)),{author:m,title:c,html:l,format:"pdf"}}exports.convertPDFToHTML=async function(t,e={}){if("liteparse"===e.method){const{method:s,...n}=e;return ht(t,n)}var{addPageNumbers:s=!1,addCitation:n=!0}=e,o="string"==typeof t?await y(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 g({index:t}));let i,h={};for(let p=1;p<=r.numPages;p++){const t=await r.getPage(p),e=await t.getTextContent();if(!(Object.keys(h).length<10)){i=u(h);break}h=m(h,t.pageNumber-1,e.items)}let l=i?i.pageNum:0;for(let m=1;m<=r.numPages;m++){const t=await r.getPage(m);await t.getOperatorList();const e=t.getViewport({scale:1});let s=await t.getTextContent();i&&t.pageIndex>=i.pageIndex&&(s=p(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 d({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 I({pages:a});let f;[new U,new q,new R,new V,new Q,new tt,new Z,new nt,new ot,new rt,new at,new it].forEach(t=>{f&&(c=f.completeTransform(c)),c=t.transform(c),f=t});var w=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=w.slice(0,400).match(/<h[0-9]>(.*?)<\/h[0-9]>/)?.[1]||E}return{author:x,title:E,html:w,format:"pdf"}},exports.convertPDFToHTMLWithLiteParse=ht,exports.detectPdfNeedsOcr=async function(t,e={}){const s="string"==typeof t?await y(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,3 +1,14 @@
1
+ import { convertPDFToHTMLWithLiteParse, LiteParseHTMLOptions } from './liteparse-to-html';
2
+ /**
3
+ * Which parsing engine {@link convertPDFToHTML} runs.
4
+ * - `"ts-block-algorithm"` (default) — the pure-TS pipeline in this file: groups
5
+ * pdfjs text spans into lines/blocks and renders headings/lists/code blocks.
6
+ * Works in Node.js, Cloudflare Workers, and browsers.
7
+ * - `"liteparse"` — delegates to [LiteParse](https://github.com/run-llama/liteparse),
8
+ * a native/OCR-capable parser. Node.js only (ships a napi addon), and requires
9
+ * the optional `@llamaindex/liteparse` dependency to be installed.
10
+ */
11
+ export type ParseMethod = "ts-block-algorithm" | "liteparse";
1
12
  /**
2
13
  * Extracts formatted text from PDF with parsing of linebreaks ,
3
14
  * page headers, footnotes, and section headings. Supports fonts, links, bold,
@@ -12,6 +23,8 @@
12
23
  * @param {Object} [options]
13
24
  * @param {boolean} options.addPageNumbers default=false - Adds # to end of each page
14
25
  * @param {boolean} options.removePageHeaders default=true - Removes repeated headers found on each page
26
+ * @param {ParseMethod} options.method default="ts-block-algorithm" - Parsing engine to use;
27
+ * `"liteparse"` delegates to LiteParse (Node.js only, see {@link ParseMethod})
15
28
  * @returns {string|Object} HTML formatted text
16
29
  * @category Extract
17
30
  * @author [vtempest (2025)](https://github.com/vtempest),
@@ -21,16 +34,26 @@
21
34
  export declare function convertPDFToHTML(pdfURLOrBuffer: any, options?: {
22
35
  addPageNumbers?: boolean;
23
36
  addCitation?: boolean;
24
- }): Promise<{
37
+ method?: ParseMethod;
38
+ } & Pick<LiteParseHTMLOptions, "liteParseOptions">): Promise<{
25
39
  error: any;
26
40
  author?: undefined;
27
41
  title?: undefined;
28
42
  html?: undefined;
29
43
  format?: undefined;
44
+ } | {
45
+ author: string;
46
+ title: string;
47
+ html: any;
48
+ format: string;
49
+ error?: undefined;
30
50
  } | {
31
51
  author: any;
32
52
  title: any;
33
53
  html: string;
34
54
  format: string;
35
- error?: undefined;
36
55
  }>;
56
+ export { convertPDFToHTMLWithLiteParse };
57
+ export type { LiteParseHTMLOptions } from './liteparse-to-html';
58
+ export { detectPdfNeedsOcr } from './detect-needs-ocr';
59
+ export type { DetectPdfNeedsOcrOptions, PdfOcrAssessment, } from './detect-needs-ocr';
@@ -1 +1 @@
1
- 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,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 l=(t,e,s)=>Math.floor(t/e*s),c=(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},m=(t,e,s)=>{const n=l(1,6,s.length),o=l(5,6,s.length),r=c(s.slice(0,n),t,e);return c(s.slice(o),r,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 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]}}},f=(t,e)=>{const s={items:[...t.items]},n=l(1,6,s.items.length),o=l(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},p=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}},d=class extends p{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}},g=class{index;items;constructor(t){this.index=t.index,this.items=t.items||[]}},y=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}},w=class{category;color;constructor(t){this.category=t.category,this.color=t.color}},x=new w({category:"Added",color:"green"}),E=new w({category:"Removed",color:"red"}),T=new w({category:"Unchanged",color:"brown"}),v=new w({category:"Detected",color:"green"}),b=new w({category:"Modified",color:"green"}),L=class t extends y{constructor(e){if(super(e,d.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||[]}},O=class{string;type;format;constructor(t){this.string=t.string,this.type=t.type??null,this.format=t.format??null}},k=class extends p{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 O({string:t})))}text(){return this.wordStrings().join(" ")}wordStrings(){return this.words.map(t=>t.string)}},S=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)}},C=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 F(t){t.sort((t,e)=>t.x-e.x)}var N={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>"}},D={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>`}},H=class{fontToFormats;constructor(t){this.fontToFormats=t}compact(t){F(t);const e=new M(this.fontToFormats);e.consumeAll(t.map(t=>new d({...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 C({footnoteLinks:e.footnoteLinks,footnotes:e.footnotes,containLinks:e.containLinks,formattedWords:e.formattedWords})})}},M=class extends S{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 O({string:`${s}`,type:D.FOOTNOTE_LINK})),this.footnoteLinks.push(parseInt(s))):this.currentItem&&this.currentItem.y<t[0].y?(e.push(new O({string:`${s}`,type:D.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?N[e]:null;return s.filter(t=>t.trim().length>0).map(t=>{var e=null;return t.startsWith("http:")?(this.containLinks=!0,e=D.LINK):t.startsWith("www.")&&(this.containLinks=!0,t=`http://${t}`,e=D.LINK),i&&this.formattedWords++,new O({string:t,type:e,format:i})})}};var B=class extends L{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(U(e)),a=U(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(U(i)),l=[],c=/* @__PURE__ */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=N.BOLD_OBLIQUE:s.includes("bold")?o=N.BOLD:s.includes("oblique")||s.includes("italic")?o=N.OBLIQUE:s===n&&(o=N.BOLD),o&&c.set(e,o.name)}),l.sort();const m=t.pages.map(t=>({...t,items:t.items.map(t=>({...t}))}));return new I({...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 U(t){var e=0,s="";return Object.keys(t).map(n=>{(!s||t[n]>e)&&(e=t[n],s=n)}),s}var A=class t extends y{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}},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=>{F(t)}),e}},P={H1:{name:"H1",headline:!0,headlineLevel:1,toText:t=>"<h1>"+$(t.items,!0)+"</h1>"},H2:{name:"H2",headline:!0,headlineLevel:2,toText:t=>"<h2>"+$(t.items,!0)+"</h2>"},H3:{name:"H3",headline:!0,headlineLevel:3,toText:t=>"<h3>"+$(t.items,!0)+"</h3>"},H4:{name:"H4",headline:!0,headlineLevel:4,toText:t=>"<h4>"+$(t.items,!0)+"</h4>"},H5:{name:"H5",headline:!0,headlineLevel:5,toText:t=>"<h5>"+$(t.items,!0)+"</h5>"},H6:{name:"H6",headline:!0,headlineLevel:6,toText:t=>"<h6>"+$(t.items,!0)+"</h6>"},TOC:{name:"TOC",mergeToBlock:!0,toText:t=>$(t.items,!0)},FOOTNOTES:{name:"FOOTNOTES",mergeToBlock:!0,mergeFollowingNonTypedItems:!0,toText:t=>"<p>"+$(t.items,!1)+"</p>"},CODE:{name:"CODE",mergeToBlock:!0,toText:t=>"<code>"+$(t.items,!0)+"</code>"},LIST:{name:"LIST",mergeToBlock:!1,mergeFollowingNonTypedItemsWithSmallDistance:!0,toText:t=>"<ul>\n"+$(t.items,!1)+"</ul>"},PARAGRAPH:{name:"PARAGRAPH",toText:t=>"<p>"+$(t.items,!1)+"</p>"}};function $(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}P.enumValueOf=function(t){return P[t]},P.isHeadline=function(t){return t&&2===t.name.length&&"H"===t.name[0]},P.blockToText=function(t){return t.type?t.type.toText(t):$(t.items,!1)},P.headlineByLevel=function(t){return 1===t?P.H1:2===t?P.H2:3===t?P.H3:4===t?P.H4:5===t?P.H5:(t>6&&console.warn("Unsupported headline level: "+t+" (supported are 1-6), defaulting to level 6"),P.H6)};var j=class extends A{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 H(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=x,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&&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=P.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 "+r+" links","Detected "+o.length+" footnotes links","Detected "+n.length+" footnotes"]})}};function q(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 Y=class extends A{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=q(o.minElements.reduce((t,e)=>t+e.text().toUpperCase(),"")),a=q(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=E}),r++),n[e[i].maxLineHash]>=Math.max(3,2*t.pages.length/3)&&(e[i].maxElements.forEach(t=>{t.annotation=E}),o++)}),new I({...t,messages:["Removed Header: "+o,"Removed Footers: "+r]})}},R=class extends A{constructor(){super("Vertical to Horizontal Text")}transform(t){var e=0;return t.pages.forEach(t=>{const s=new V;s.consumeAll(t.items),t.items=s.complete(),e+=s.foundVerticals}),new I({...t,messages:["Converted "+e+" verticals"]})}},V=class extends S{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=E,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:x})),this.foundVerticals++}else e.push(...t)}},K=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}},J=class extends A{constructor(){super("Detect TOC")}transform(t){const e=[],r=Math.min(20,t.pages.length),a=new z;var i,l,c=[];t.pages.slice(0,r).forEach(t=>{var r=0;const h=/* @__PURE__ */new Set,m=[];var u,f;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 G({pageNumber:parseInt(a.join("")),lineItem:new k({...t,words:e})})),r++):l?(u&&h.add(f),u=e,f=t):l=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=>{h.has(t)||(t.annotation=E),s.push(t),t===l&&s.push(new k({...t,type:P.H2,annotation:x}))}),t.items=s}else l=null});var m=c.length;const u=[],f=[],p={};if(e.length>0){c.forEach(t=>{i.items.push(new k({words:[new O({string:" ".repeat(3*t.level)+"-"})].concat(t.lineItem.words),type:P.TOC,annotation:x}))});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),c);c.forEach(s=>{var n,o=t.pages[s.pageNumber+e];o&&((n=_(o,s.lineItem.text()))||(o=t.pages[s.pageNumber+e+1])&&(n=_(o,s.lineItem.text()))),n?function(t,e,s,n){s.headlineItems.forEach(t=>t.annotation=E);const o=P.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:x}));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,p):u.push(s)});var d=i.index+2,g=[];const s=e=>{g.length>0&&(g.forEach(s=>{const n=P.headlineByLevel(s.level+2),o=p[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&&h(r,t.text())>=.5);if(t>-1)return[a-1,t]}}return[-1,-1]}(t.pages,s,o,d,e);if(a>-1){const e=t.pages[r];e.items[a].annotation=E,e.items.splice(a+1,0,new k({...s.lineItem,type:n,annotation:x})),f.push(s)}}}),g=[])};u.length>0&&(c.forEach(t=>{u.includes(t)?g.push(t):(s(t.pageNumber),d=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(p)),y.push("Found TOC headlines: "+(m-u.length+f.length)+"/"+m)),u.length>0&&(y.push("Found TOC headlines (by size): "+f.map(t=>t.lineItem.text())),y.push("Missing TOC headlines: "+u.filter(t=>!f.includes(t)).map(t=>t.lineItem.text()+"=>"+t.pageNumber))),new I({...t,globals:{...t.globals,tocPages:e,headlineTypeToHeightRange:p},messages:y})}};function Q(t,e){for(var s of t)if(_(s,e))return s;return null}function _(t,e){const s=new K({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 z=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},[])}},G=class{lineItem;pageNumber;level;constructor(t){this.lineItem=t.lineItem,this.pageNumber=t.pageNumber,this.level=0}},X=class extends A{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=v,t.type=P.LIST;else{t.annotation=E;const e=t.words.map(t=>new O({...t}));e[0].string="-",n.push(new k({...t,words:e,annotation:x,type:P.LIST}))}else/^[\s]*[\d]*[.][\s].*$/g.test(o)&&(s++,t.annotation=v,t.type=P.LIST)}}),t.items=n}),new I({...t,messages:["Detected "+e+" plain list items.","Detected "+s+" numbered list items."]})}},Z=class extends A{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=/* @__PURE__ */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?P.H1:P.H2,t.annotation=v,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=v,t.type=P.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=P.headlineByLevel(2+s);t.pages.forEach(t=>{t.items.forEach(t=>{t.type||t.height!==e||i(t.text())||(l++,t.annotation=v,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=P.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=v,t.type=e),s=t})})}return new I({...t,messages:["Detected "+l+" headlines."]})}};var tt=class extends p{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)}},et=class t extends y{constructor(e){if(super(e,tt.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}},st=class extends et{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 tt({});const a=()=>{r.items.length>1&&(r.annotation=v),o.push(r),r=new tt({}),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 I({...t,messages:["Gathered "+s+" blocks out of "+n+" line items"]})}};var nt=class extends et{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=v,t.type=P.CODE,s++)})}),new I({...t,messages:["Detected "+s+" code/quote items."]})}};var ot=class extends et{constructor(){super("Level Lists")}transform(t){var e=0,s=0;return t.pages.forEach(t=>{t.items.filter(t=>t.type===P.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 O({string:" ".repeat(3*o)})].concat(t.words),a=!0),n=t.x}),e++,a?(s++,t.annotation=b):t.annotation=T})}),new I({...t,messages:["Modified "+s+" / "+e+" list blocks."]})}},rt=class extends y{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:P.blockToText(t)})}),t.items=e}),new I({...t})}},at=class extends y{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 it(t,e={}){var{addPageNumbers:s=!1,addCitation:n=!0}=e,o="string"==typeof t?await async function(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}}(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(E){return{error:E.message}}const a=[...Array(r.numPages).keys()].map(t=>new g({index:t}));let i,h={};for(let f=1;f<=r.numPages;f++){const t=await r.getPage(f),e=await t.getTextContent();if(!(Object.keys(h).length<10)){i=u(h);break}h=m(h,t.pageNumber-1,e.items)}let l=i?i.pageNum:0;for(let m=1;m<=r.numPages;m++){const t=await r.getPage(m);await t.getOperatorList();const e=t.getViewport({scale:1});let s=await t.getTextContent();i&&t.pageIndex>=i.pageIndex&&(s=f(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 d({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 I({pages:a});let p;[new B,new j,new Y,new R,new J,new Z,new X,new st,new nt,new ot,new rt,new at].forEach(t=>{p&&(c=p.completeTransform(c)),c=t.transform(c),p=t});var y=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:w,Title:x}=(await r.getMetadata()).info;x=y.slice(0,400).match(/<h[0-9]>(.*?)<\/h[0-9]>/)?.[1]||x}return{author:w,title:x,html:y,format:"pdf"}}export{it as convertPDFToHTML};
1
+ 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,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 l=(t,e,s)=>Math.floor(t/e*s),c=(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},m=(t,e,s)=>{const n=l(1,6,s.length),o=l(5,6,s.length),r=c(s.slice(0,n),t,e);return c(s.slice(o),r,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 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]}}},p=(t,e)=>{const s={items:[...t.items]},n=l(1,6,s.items.length),o=l(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}},d=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}},g=class{index;items;constructor(t){this.index=t.index,this.items=t.items||[]}};async function y(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 w=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}},x=class{category;color;constructor(t){this.category=t.category,this.color=t.color}},E=new x({category:"Added",color:"green"}),T=new x({category:"Removed",color:"red"}),v=new x({category:"Unchanged",color:"brown"}),b=new x({category:"Detected",color:"green"}),L=new x({category:"Modified",color:"green"}),O=class t extends w{constructor(e){if(super(e,d.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!==T),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||[]}},S=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 S({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)}},F=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 N(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>`}},M=class{fontToFormats;constructor(t){this.fontToFormats=t}compact(t){N(t);const e=new B(this.fontToFormats);e.consumeAll(t.map(t=>new d({...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 F({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 S({string:`${s}`,type:H.FOOTNOTE_LINK})),this.footnoteLinks.push(parseInt(s))):this.currentItem&&this.currentItem.y<t[0].y?(e.push(new S({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 S({string:t,type:e,format:i})})}};var U=class extends O{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=/* @__PURE__ */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 I({...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 P=class t extends w{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!==T),t.items.forEach(t=>t.annotation=null)}),t}},$=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=>{N(t)}),e}},W={H1:{name:"H1",headline:!0,headlineLevel:1,toText:t=>"<h1>"+j(t.items,!0)+"</h1>"},H2:{name:"H2",headline:!0,headlineLevel:2,toText:t=>"<h2>"+j(t.items,!0)+"</h2>"},H3:{name:"H3",headline:!0,headlineLevel:3,toText:t=>"<h3>"+j(t.items,!0)+"</h3>"},H4:{name:"H4",headline:!0,headlineLevel:4,toText:t=>"<h4>"+j(t.items,!0)+"</h4>"},H5:{name:"H5",headline:!0,headlineLevel:5,toText:t=>"<h5>"+j(t.items,!0)+"</h5>"},H6:{name:"H6",headline:!0,headlineLevel:6,toText:t=>"<h6>"+j(t.items,!0)+"</h6>"},TOC:{name:"TOC",mergeToBlock:!0,toText:t=>j(t.items,!0)},FOOTNOTES:{name:"FOOTNOTES",mergeToBlock:!0,mergeFollowingNonTypedItems:!0,toText:t=>"<p>"+j(t.items,!1)+"</p>"},CODE:{name:"CODE",mergeToBlock:!0,toText:t=>"<code>"+j(t.items,!0)+"</code>"},LIST:{name:"LIST",mergeToBlock:!1,mergeFollowingNonTypedItemsWithSmallDistance:!0,toText:t=>"<ul>\n"+j(t.items,!1)+"</ul>"},PARAGRAPH:{name:"PARAGRAPH",toText:t=>"<p>"+j(t.items,!1)+"</p>"}};function j(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}W.enumValueOf=function(t){return W[t]},W.isHeadline=function(t){return t&&2===t.name.length&&"H"===t.name[0]},W.blockToText=function(t){return t.type?t.type.toText(t):j(t.items,!1)},W.headlineByLevel=function(t){return 1===t?W.H1:2===t?W.H2:3===t?W.H3:4===t?W.H4:5===t?W.H5:(t>6&&console.warn("Unsupported headline level: "+t+" (supported are 1-6), defaulting to level 6"),W.H6)};var q=class extends P{constructor(){super("Compact To Lines")}transform(t){const{mostUsedDistance:e,fontToFormats:s}=t.globals,n=[],o=[];var r=0,a=0;const i=new $({mostUsedDistance:e}),h=new M(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=E,s.forEach(t=>{t.annotation=T,e.push(new k({...t}))})),0===i.words.length&&(i.annotation=T),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=W.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 "+r+" links","Detected "+o.length+" footnotes links","Detected "+n.length+" footnotes"]})}};function Y(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 R=class extends P{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=Y(o.minElements.reduce((t,e)=>t+e.text().toUpperCase(),"")),a=Y(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=T}),r++),n[e[i].maxLineHash]>=Math.max(3,2*t.pages.length/3)&&(e[i].maxElements.forEach(t=>{t.annotation=T}),o++)}),new I({...t,messages:["Removed Header: "+o,"Removed Footers: "+r]})}},V=class extends P{constructor(){super("Vertical to Horizontal Text")}transform(t){var e=0;return t.pages.forEach(t=>{const s=new K;s.consumeAll(t.items),t.items=s.complete(),e+=s.foundVerticals}),new I({...t,messages:["Converted "+e+" verticals"]})}},K=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=T,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:E})),this.foundVerticals++}else e.push(...t)}},J=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}},Q=class extends P{constructor(){super("Detect TOC")}transform(t){const e=[],r=Math.min(20,t.pages.length),a=new G;var i,l,c=[];t.pages.slice(0,r).forEach(t=>{var r=0;const h=/* @__PURE__ */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 X({pageNumber:parseInt(a.join("")),lineItem:new k({...t,words:e})})),r++):l?(u&&h.add(p),u=e,p=t):l=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=>{h.has(t)||(t.annotation=T),s.push(t),t===l&&s.push(new k({...t,type:W.H2,annotation:E}))}),t.items=s}else l=null});var m=c.length;const u=[],p=[],f={};if(e.length>0){c.forEach(t=>{i.items.push(new k({words:[new S({string:" ".repeat(3*t.level)+"-"})].concat(t.lineItem.words),type:W.TOC,annotation:E}))});const e=function(t,e){for(var s of e){const e=_(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=z(o,s.lineItem.text()))||(o=t.pages[s.pageNumber+e+1])&&(n=z(o,s.lineItem.text()))),n?function(t,e,s,n){s.headlineItems.forEach(t=>t.annotation=T);const o=W.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:E}));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,f):u.push(s)});var d=i.index+2,g=[];const s=e=>{g.length>0&&(g.forEach(s=>{const n=W.headlineByLevel(s.level+2),o=f[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&&h(r,t.text())>=.5);if(t>-1)return[a-1,t]}}return[-1,-1]}(t.pages,s,o,d,e);if(a>-1){const e=t.pages[r];e.items[a].annotation=T,e.items.splice(a+1,0,new k({...s.lineItem,type:n,annotation:E})),p.push(s)}}}),g=[])};u.length>0&&(c.forEach(t=>{u.includes(t)?g.push(t):(s(t.pageNumber),d=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(f)),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 I({...t,globals:{...t.globals,tocPages:e,headlineTypeToHeightRange:f},messages:y})}};function _(t,e){for(var s of t)if(z(s,e))return s;return null}function z(t,e){const s=new J({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 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},[])}},X=class{lineItem;pageNumber;level;constructor(t){this.lineItem=t.lineItem,this.pageNumber=t.pageNumber,this.level=0}},Z=class extends P{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=b,t.type=W.LIST;else{t.annotation=T;const e=t.words.map(t=>new S({...t}));e[0].string="-",n.push(new k({...t,words:e,annotation:E,type:W.LIST}))}else/^[\s]*[\d]*[.][\s].*$/g.test(o)&&(s++,t.annotation=b,t.type=W.LIST)}}),t.items=n}),new I({...t,messages:["Detected "+e+" plain list items.","Detected "+s+" numbered list items."]})}},tt=class extends P{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=/* @__PURE__ */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?W.H1:W.H2,t.annotation=b,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=b,t.type=W.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=W.headlineByLevel(2+s);t.pages.forEach(t=>{t.items.forEach(t=>{t.type||t.height!==e||i(t.text())||(l++,t.annotation=b,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=W.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=b,t.type=e),s=t})})}return new I({...t,messages:["Detected "+l+" headlines."]})}};var et=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)}},st=class t extends w{constructor(e){if(super(e,et.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!==T),t.items.forEach(t=>t.annotation=null)}),t}},nt=class extends st{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 et({});const a=()=>{r.items.length>1&&(r.annotation=b),o.push(r),r=new et({}),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 I({...t,messages:["Gathered "+s+" blocks out of "+n+" line items"]})}};var ot=class extends st{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=b,t.type=W.CODE,s++)})}),new I({...t,messages:["Detected "+s+" code/quote items."]})}};var rt=class extends st{constructor(){super("Level Lists")}transform(t){var e=0,s=0;return t.pages.forEach(t=>{t.items.filter(t=>t.type===W.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 S({string:" ".repeat(3*o)})].concat(t.words),a=!0),n=t.x}),e++,a?(s++,t.annotation=L):t.annotation=v})}),new I({...t,messages:["Modified "+s+" / "+e+" list blocks."]})}},at=class extends w{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:W.blockToText(t)})}),t.items=e}),new I({...t})}},it=class extends w{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:o={}}=e,r="string"==typeof t?await y(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,...o});let h;try{h=await i.parse(new Uint8Array(r))}catch(u){return{error:u.message}}const l=h.pages.reduce((t,e)=>{const n=e.text.split(/\n{2,}/).filter(t=>t.trim().length>0).map(t=>{return(e=t,e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")).replace(/\n/g,"<br/>");var e}).join("</p><p>");return t+`<p id="page-${e.pageNum}">${s?` [${e.pageNum}] `:""}${n}</p>`},"");let c,m;return n&&(m=h.creator||h.producer,c=h.pages[0]?.text.split("\n").find(t=>t.trim().length>0)),{author:m,title:c,html:l,format:"pdf"}}async function lt(t,e={}){const s="string"==typeof t?await y(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))]}}async function ct(t,e={}){if("liteparse"===e.method){const{method:s,...n}=e;return ht(t,n)}var{addPageNumbers:s=!1,addCitation:n=!0}=e,o="string"==typeof t?await y(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 g({index:t}));let i,h={};for(let p=1;p<=r.numPages;p++){const t=await r.getPage(p),e=await t.getTextContent();if(!(Object.keys(h).length<10)){i=u(h);break}h=m(h,t.pageNumber-1,e.items)}let l=i?i.pageNum:0;for(let m=1;m<=r.numPages;m++){const t=await r.getPage(m);await t.getOperatorList();const e=t.getViewport({scale:1});let s=await t.getTextContent();i&&t.pageIndex>=i.pageIndex&&(s=p(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 d({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 I({pages:a});let f;[new U,new q,new R,new V,new Q,new tt,new Z,new nt,new ot,new rt,new at,new it].forEach(t=>{f&&(c=f.completeTransform(c)),c=t.transform(c),f=t});var w=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=w.slice(0,400).match(/<h[0-9]>(.*?)<\/h[0-9]>/)?.[1]||E}return{author:x,title:E,html:w,format:"pdf"}}export{ct as convertPDFToHTML,ht as convertPDFToHTMLWithLiteParse,lt as detectPdfNeedsOcr};
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Minimal fetch wrapper for grabbing binary/text content, shared by every
3
+ * parse method (ts-block-algorithm, liteparse) and the OCR-need detector.
4
+ */
5
+ export declare function grab(url: string, options?: {
6
+ responseType?: string;
7
+ timeout?: number;
8
+ }): Promise<string | ArrayBuffer>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "extract-pdf",
3
- "version": "0.1.114",
3
+ "version": "0.1.115",
4
4
  "description": "Convert a PDF (URL or ArrayBuffer) into clean HTML with structural tagging — headings, lists, footnotes, code blocks, bold/italic. Works in Node.js, Cloudflare Workers, and browser environments.",
5
5
  "author": "vtempest <grokthiscontact@gmail.com>",
6
6
  "license": "rights.institute/PROSPER",
@@ -26,8 +26,9 @@
26
26
  "test:coverage": "bun test --coverage --coverage-reporter=lcov --coverage-reporter=text",
27
27
  "ship": "npm run build && npx standard-version --release-as patch; rm -f CHANGELOG.md; npm publish"
28
28
  },
29
- "dependencies": {
30
- "grab-url": "^1.6.22"
29
+ "dependencies": {},
30
+ "optionalDependencies": {
31
+ "@llamaindex/liteparse": "^2.10.0"
31
32
  },
32
33
  "devDependencies": {
33
34
  "terser": "^5.46.0",
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Cheap, text-layer-only pass that determines whether a PDF needs OCR (or
3
+ * other heavy parsing) before committing to a full parse — useful for routing
4
+ * documents to different pipelines. Backed by LiteParse's `isComplex`, so this
5
+ * is Node.js only (native napi addon) and requires the optional
6
+ * `@llamaindex/liteparse` dependency.
7
+ */
8
+ import { grab } from "./utils/grab";
9
+ import type { LiteParseConfig, PageComplexityStats } from "@llamaindex/liteparse";
10
+
11
+ export interface DetectPdfNeedsOcrOptions {
12
+ liteParseOptions?: Partial<LiteParseConfig>;
13
+ }
14
+
15
+ export interface PdfOcrAssessment {
16
+ /** True when any page needs OCR or other advanced parsing. */
17
+ needsOcr: boolean;
18
+ /** Per-page complexity signals, one entry per page. */
19
+ pages: PageComplexityStats[];
20
+ /**
21
+ * Every distinct reason found across all pages, e.g. `"scanned"`,
22
+ * `"no-text"`, `"sparse-text"`, `"embedded-images"`, `"garbled"`,
23
+ * `"vector-text"`. Empty when `needsOcr` is false.
24
+ */
25
+ reasons: string[];
26
+ }
27
+
28
+ /**
29
+ * Determines whether a PDF (URL or ArrayBuffer) needs OCR to extract usable
30
+ * text, without running a full parse.
31
+ * @param pdfURLOrBuffer - URL to a PDF file or buffer from fs.readFile
32
+ * @category Extract
33
+ */
34
+ export async function detectPdfNeedsOcr(
35
+ pdfURLOrBuffer: any,
36
+ options: DetectPdfNeedsOcrOptions = {},
37
+ ): Promise<PdfOcrAssessment> {
38
+ const buffer =
39
+ typeof pdfURLOrBuffer === "string"
40
+ ? await grab(pdfURLOrBuffer, { responseType: "arraybuffer", timeout: 10 })
41
+ : pdfURLOrBuffer;
42
+
43
+ const { LiteParse } = await import("@llamaindex/liteparse");
44
+ const parser = new LiteParse({ quiet: true, ...options.liteParseOptions });
45
+ const pages = await parser.isComplex(new Uint8Array(buffer));
46
+
47
+ return {
48
+ needsOcr: pages.some((page) => page.needsOcr),
49
+ pages,
50
+ reasons: [...new Set(pages.flatMap((page) => page.reasons))],
51
+ };
52
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Alternate `ParseMethod` for convertPDFToHTML that delegates to
3
+ * [LiteParse](https://github.com/run-llama/liteparse) instead of the built-in
4
+ * ts-block-algorithm pipeline. LiteParse ships a native (napi) addon, so this
5
+ * path is Node.js only — it will not run in Cloudflare Workers or browsers —
6
+ * and requires the optional `@llamaindex/liteparse` dependency to be installed.
7
+ */
8
+ import { grab } from "./utils/grab";
9
+ import type { LiteParseConfig } from "@llamaindex/liteparse";
10
+
11
+ export interface LiteParseHTMLOptions {
12
+ addPageNumbers?: boolean;
13
+ addCitation?: boolean;
14
+ liteParseOptions?: Partial<LiteParseConfig>;
15
+ }
16
+
17
+ function escapeHtml(text: string): string {
18
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
19
+ }
20
+
21
+ /**
22
+ * Converts a PDF (URL or ArrayBuffer) into HTML using LiteParse's spatial text
23
+ * extraction, mirroring the return shape of `convertPDFToHTML`.
24
+ * @param pdfURLOrBuffer - URL to a PDF file or buffer from fs.readFile
25
+ * @param options.addPageNumbers default=false - Adds `[n]` markers at each page boundary
26
+ * @param options.addCitation default=true - Populates `title`/`author` from PDF metadata
27
+ * @param options.liteParseOptions - Passed through to the `LiteParse` constructor;
28
+ * defaults to `{ ocrEnabled: false, ocrFailureFatal: false }` (use detectPdfNeedsOcr
29
+ * to decide when a document is worth re-parsing with `ocrEnabled: true`)
30
+ * @returns `{ html, title, author, format: "pdf" }`, or `{ error }` on failure
31
+ * @category Extract
32
+ */
33
+ export async function convertPDFToHTMLWithLiteParse(
34
+ pdfURLOrBuffer: any,
35
+ options: LiteParseHTMLOptions = {},
36
+ ) {
37
+ const { addPageNumbers = false, addCitation = true, liteParseOptions = {} } = options;
38
+
39
+ const buffer =
40
+ typeof pdfURLOrBuffer === "string"
41
+ ? await grab(pdfURLOrBuffer, { responseType: "arraybuffer", timeout: 10 })
42
+ : pdfURLOrBuffer;
43
+
44
+ let LiteParse: typeof import("@llamaindex/liteparse").LiteParse;
45
+ try {
46
+ ({ LiteParse } = await import("@llamaindex/liteparse"));
47
+ } catch (e: any) {
48
+ return {
49
+ error:
50
+ "method: 'liteparse' requires the optional @llamaindex/liteparse dependency " +
51
+ `(Node.js only): ${e.message}`,
52
+ };
53
+ }
54
+
55
+ // Default to no OCR: this method exists for the fast/local path (see
56
+ // detectPdfNeedsOcr for routing scanned/complex pages elsewhere), and OCR
57
+ // requires downloading tessdata on first use, which is slow and can fail
58
+ // offline. `ocrFailureFatal: false` keeps already-recovered native text
59
+ // instead of discarding the whole parse if a caller opts OCR back in and it
60
+ // fails on some pages.
61
+ const parser = new LiteParse({
62
+ quiet: true,
63
+ ocrEnabled: false,
64
+ ocrFailureFatal: false,
65
+ ...liteParseOptions,
66
+ });
67
+
68
+ let result;
69
+ try {
70
+ result = await parser.parse(new Uint8Array(buffer));
71
+ } catch (e: any) {
72
+ return { error: e.message };
73
+ }
74
+
75
+ const html = result.pages.reduce((acc, page) => {
76
+ const body = page.text
77
+ .split(/\n{2,}/)
78
+ .filter((paragraph) => paragraph.trim().length > 0)
79
+ .map((paragraph) => escapeHtml(paragraph).replace(/\n/g, "<br/>"))
80
+ .join("</p><p>");
81
+ return (
82
+ acc +
83
+ `<p id="page-${page.pageNum}">${addPageNumbers ? ` [${page.pageNum}] ` : ""}${body}</p>`
84
+ );
85
+ }, "");
86
+
87
+ let title: string | undefined;
88
+ let author: string | undefined;
89
+ if (addCitation) {
90
+ author = result.creator || result.producer;
91
+ title = result.pages[0]?.text.split("\n").find((line) => line.trim().length > 0);
92
+ }
93
+
94
+ return { author, title, html, format: "pdf" };
95
+ }
@@ -9,34 +9,7 @@ import {
9
9
  } from "./utils/page-number-functions";
10
10
  import TextItem from "./models/text-item";
11
11
  import Page from "./models/page";
12
-
13
- /**
14
- * Fetch wrapper for grabbing binary content
15
- */
16
- async function grab(url: string, options: { responseType?: string; timeout?: number } = {}) {
17
- const timeout = options.timeout ? options.timeout * 1000 : 10000;
18
- const controller = new AbortController();
19
- const timeoutId = setTimeout(() => controller.abort(), timeout);
20
-
21
- try {
22
- const response = await fetch(url, {
23
- signal: controller.signal,
24
- });
25
- clearTimeout(timeoutId);
26
-
27
- if (!response.ok) {
28
- throw new Error(`HTTP ${response.status}`);
29
- }
30
-
31
- if (options.responseType === "arraybuffer") {
32
- return await response.arrayBuffer();
33
- }
34
- return await response.text();
35
- } catch (error) {
36
- clearTimeout(timeoutId);
37
- throw error;
38
- }
39
- }
12
+ import { grab } from "./utils/grab";
40
13
 
41
14
  import CalculateGlobalStats from "./transforms/calculate-global-stats";
42
15
  import CompactLines from "./transforms/line-item/compact-lines";
@@ -52,6 +25,21 @@ import DetectListLevels from "./transforms/block/detect-list-levels";
52
25
  import ToTextBlocks from "./transforms/to-text-blocks";
53
26
  import ToHTML from "./transforms/to-html";
54
27
  import ParseResult from "./models/parse-result";
28
+ import {
29
+ convertPDFToHTMLWithLiteParse,
30
+ type LiteParseHTMLOptions,
31
+ } from "./liteparse-to-html";
32
+
33
+ /**
34
+ * Which parsing engine {@link convertPDFToHTML} runs.
35
+ * - `"ts-block-algorithm"` (default) — the pure-TS pipeline in this file: groups
36
+ * pdfjs text spans into lines/blocks and renders headings/lists/code blocks.
37
+ * Works in Node.js, Cloudflare Workers, and browsers.
38
+ * - `"liteparse"` — delegates to [LiteParse](https://github.com/run-llama/liteparse),
39
+ * a native/OCR-capable parser. Node.js only (ships a napi addon), and requires
40
+ * the optional `@llamaindex/liteparse` dependency to be installed.
41
+ */
42
+ export type ParseMethod = "ts-block-algorithm" | "liteparse";
55
43
 
56
44
  /**
57
45
  * Extracts formatted text from PDF with parsing of linebreaks ,
@@ -67,6 +55,8 @@ import ParseResult from "./models/parse-result";
67
55
  * @param {Object} [options]
68
56
  * @param {boolean} options.addPageNumbers default=false - Adds # to end of each page
69
57
  * @param {boolean} options.removePageHeaders default=true - Removes repeated headers found on each page
58
+ * @param {ParseMethod} options.method default="ts-block-algorithm" - Parsing engine to use;
59
+ * `"liteparse"` delegates to LiteParse (Node.js only, see {@link ParseMethod})
70
60
  * @returns {string|Object} HTML formatted text
71
61
  * @category Extract
72
62
  * @author [vtempest (2025)](https://github.com/vtempest),
@@ -75,8 +65,17 @@ import ParseResult from "./models/parse-result";
75
65
  */
76
66
  export async function convertPDFToHTML(
77
67
  pdfURLOrBuffer: any,
78
- options: { addPageNumbers?: boolean; addCitation?: boolean } = {},
68
+ options: {
69
+ addPageNumbers?: boolean;
70
+ addCitation?: boolean;
71
+ method?: ParseMethod;
72
+ } & Pick<LiteParseHTMLOptions, "liteParseOptions"> = {},
79
73
  ) {
74
+ if (options.method === "liteparse") {
75
+ const { method: _method, ...liteParseOptions } = options;
76
+ return convertPDFToHTMLWithLiteParse(pdfURLOrBuffer, liteParseOptions);
77
+ }
78
+
80
79
  // try {
81
80
  var { addPageNumbers = false, addCitation = true } = options;
82
81
 
@@ -222,4 +221,11 @@ export async function convertPDFToHTML(
222
221
  return { author, title, html, format: "pdf" };
223
222
  }
224
223
 
224
+ export { convertPDFToHTMLWithLiteParse };
225
+ export type { LiteParseHTMLOptions } from "./liteparse-to-html";
226
+ export { detectPdfNeedsOcr } from "./detect-needs-ocr";
227
+ export type {
228
+ DetectPdfNeedsOcrOptions,
229
+ PdfOcrAssessment,
230
+ } from "./detect-needs-ocr";
225
231
 
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Minimal fetch wrapper for grabbing binary/text content, shared by every
3
+ * parse method (ts-block-algorithm, liteparse) and the OCR-need detector.
4
+ */
5
+ export async function grab(
6
+ url: string,
7
+ options: { responseType?: string; timeout?: number } = {},
8
+ ) {
9
+ const timeout = options.timeout ? options.timeout * 1000 : 10000;
10
+ const controller = new AbortController();
11
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
12
+
13
+ try {
14
+ const response = await fetch(url, {
15
+ signal: controller.signal,
16
+ });
17
+ clearTimeout(timeoutId);
18
+
19
+ if (!response.ok) {
20
+ throw new Error(`HTTP ${response.status}`);
21
+ }
22
+
23
+ if (options.responseType === "arraybuffer") {
24
+ return await response.arrayBuffer();
25
+ }
26
+ return await response.text();
27
+ } catch (error) {
28
+ clearTimeout(timeoutId);
29
+ throw error;
30
+ }
31
+ }