extract-pdf 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # pdf-to-html
2
+
3
+ Converts a PDF (URL or `ArrayBuffer`) into clean HTML with structural tagging — headings, lists, footnotes, code blocks, bold/italic, and Table of Contents entries. Works in Node.js, Cloudflare Workers, and browser environments via [pdfjs-serverless](https://github.com/johannschopplich/pdfjs-serverless).
4
+
5
+ ## Usage
6
+
7
+ ```ts
8
+ import { convertPDFToHTML } from "./pdf-to-html";
9
+
10
+ const { html, title, author } = await convertPDFToHTML(
11
+ "https://example.com/paper.pdf",
12
+ );
13
+ // or pass an ArrayBuffer from fs.readFile / fetch
14
+ const { html } = await convertPDFToHTML(buffer, { addPageNumbers: true });
15
+ ```
16
+
17
+ ### Options
18
+
19
+ | Option | Default | Description |
20
+ | ---------------- | ------- | ------------------------------------------------------------------------------------------ |
21
+ | `addPageNumbers` | `false` | Inserts `[n]` markers at each page boundary |
22
+ | `addCitation` | `true` | Reads PDF metadata and first-page heading to populate `title`/`author` in the return value |
23
+
24
+ ### Return value
25
+
26
+ ```ts
27
+ { html: string, title?: string, author?: string, format: "pdf" }
28
+ ```
29
+
30
+ ## Pipeline
31
+
32
+ The conversion runs a sequential chain of transformations on a `ParseResult` (pages → items):
33
+
34
+ ```
35
+ Raw pdfjs text spans
36
+ → CalculateGlobalStats — font heights, distances, format map
37
+ → CompactLines — merge spans on the same y-line into LineItems
38
+ → RemoveRepetitiveElements — strip recurring page headers/footers
39
+ → VerticalToHorizontal — rotate vertical character runs
40
+ → DetectTOC — identify Table of Contents pages, link headings
41
+ → DetectHeaders — classify items as H1–H6 by font height
42
+ → DetectListItems — detect bullet/numbered list items
43
+ → GatherBlocks — group adjacent same-type lines into blocks
44
+ → DetectCodeQuoteBlocks — mark indented blocks as CODE
45
+ → DetectListLevels — add indentation for nested list levels
46
+ → ToTextBlocks — flatten blocks to { category, text } pairs
47
+ → ToHTML — render pairs as <p>, <h1>–<h6>, <ul>, <code>
48
+ ```
49
+
50
+ ## Folder structure
51
+
52
+ ```
53
+ pdf-to-html/
54
+ pdf-to-html.ts — main entry point (convertPDFToHTML)
55
+ models/ — data classes: Page, ParseResult, TextItem,
56
+ │ LineItem, LineItemBlock, Word, BlockType, …
57
+ transformations/
58
+ │ Transformation.ts — abstract base
59
+ │ CalculateGlobalStats.ts
60
+ │ ToTextBlocks.ts
61
+ │ ToHTML.ts
62
+ │ line-item/ — per-line-item transformations
63
+ │ line-item-block/ — per-block transformations
64
+ util/
65
+ string-functions.ts
66
+ page-item-functions.ts
67
+ page-number-functions.ts
68
+ ```
@@ -0,0 +1 @@
1
+ "use strict";Object.create,Object.defineProperty,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.prototype.hasOwnProperty;Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("grab-url");function e(t){for(;32===t.charCodeAt(0);)t=t.substring(1,t.length);return t}function s(t){for(;32===t.charCodeAt(t.length-1);)t=t.substring(0,t.length-1);return t}function n(t){return t>=48&&t<=57}function o(t){for(var e=0;e<t.length;e++){if(!n(t.charCodeAt(e)))return!1}return!0}function r(t,e){const s=e.charCodeAt(0);for(var n=0;n<t.length;n++){if(t.charCodeAt(n)!==s)return!1}return!0}function a(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 i(t){if(t.length>1)return!1;const e=t.charAt(0);return"-"===e||"•"===e||"–"===e}function h(t){return/^[\s]*[-•–][\s].*$/g.test(t)}function l(t,e){const s=new Set(t.toUpperCase().split(" ")),n=new Set(e.toUpperCase().split(" "));return new Set([...s].filter(t=>n.has(t))).size/Math.max(s.size,n.size)}const c=(t,e,s)=>Math.floor(t/e*s),m=(t,n,r)=>{for(const{str:a}of t){const t=s(e(a));o(t)&&(n[r]||(n[r]=[]),n[r].push(Number(t)))}return n},u=(t,e,s)=>{const n=c(1,6,s.length),o=c(5,6,s.length),r=m(s.slice(0,n),t,e);return m(s.slice(o),r,e)},f=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=c(1,6,s.items.length),o=c(5,6,s.items.length);return s.items=s.items.filter((t,r)=>{const a=r>0&&r<n,i=r>o&&r<s.items.length;return a||i?Number(t.str)!==Number(e):t}),s};class d{type;annotation;parsedElements;constructor(t){if(this.constructor===d)throw new TypeError("Can not construct abstract class.");this.type=t.type??null,this.annotation=t.annotation??null,this.parsedElements=t.parsedElements??null}}class g extends d{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}}class y{index;items;constructor(t){this.index=t.index,this.items=t.items||[]}}class x{name;itemType;constructor(t,e){if(this.constructor===x)throw new TypeError("Can not construct abstract class.");if(this.transform===x.prototype.transform)throw new TypeError("Please implement abstract method 'transform()'.");this.name=t,this.itemType=e}transform(t){throw new TypeError("Do not call abstract method foo from child.")}completeTransform(t){return t.messages=[],t}}class w{category;color;constructor(t){this.category=t.category,this.color=t.color}}const E=new w({category:"Added",color:"green"}),T=new w({category:"Removed",color:"red"}),v=new w({category:"Unchanged",color:"brown"}),b=new w({category:"Detected",color:"green"}),O=new w({category:"Modified",color:"green"});class L extends x{constructor(t){if(super(t,g.name),this.constructor===L)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 I{pages;globals;messages;constructor(t){this.pages=t.pages||[],this.globals=t.globals||{},this.messages=t.messages||[]}}class S{string;type;format;constructor(t){this.string=t.string,this.type=t.type??null,this.format=t.format??null}}class k extends d{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)}}class F{results;stash;constructor(){if(this.constructor===F)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)}}class C{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)}const D={BOLD:{name:"BOLD",startSymbol:"<strong>",endSymbol:"</strong>"},OBLIQUE:{name:"OBLIQUE",startSymbol:"<em>",endSymbol:"</em>"},BOLD_OBLIQUE:{name:"BOLD_OBLIQUE",startSymbol:"<strong><em>",endSymbol:"</em></strong>"}},M={LINK:{name:"LINK",toText:t=>`<a href="${t}">${t}</a>`},FOOTNOTE_LINK:{name:"FOOTNOTE_LINK",attachWithoutWhitespace:!0,plainTextFormat:!0,toText:t=>`<sup><a href="#${t}">${t}</a></sup>`},FOOTNOTE:{name:"FOOTNOTE",toText:t=>`<p id="${t}">^${t}</p>`}};class H{fontToFormats;constructor(t){this.fontToFormats=t}compact(t){N(t);const e=new B(this.fontToFormats);e.consumeAll(t.map(t=>new g({...t})));const s=e.complete();var n=0,o=0;return t.forEach(t=>{n=Math.max(n,t.height),o+=t.width}),new k({x:t[0].x,y:t[0].y,height:n,width:o,words:s,parsedElements:new C({footnoteLinks:e.footnoteLinks,footnotes:e.footnotes,containLinks:e.containLinks,formattedWords:e.formattedWords})})}}class B 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=o(t.text.trim())}doMatchesStash(t,e){if(this.fontToFormats.get(t.font)!==this.fontToFormats.get(e.font))return!1;const s=o(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:M.FOOTNOTE_LINK})),this.footnoteLinks.push(parseInt(s))):this.currentItem&&this.currentItem.y<t[0].y?(e.push(new S({string:`${s}`,type:M.FOOTNOTE})),this.footnotes.push(s)):this.copyStashItemsAsText(t,e)}else this.copyStashItemsAsText(t,e)}copyStashItemsAsText(t,e){const s=this.fontToFormats.get(t[0].font);e.push(...this.itemsToWords(t,s))}itemsToWords(t,e){var s,n;const o=(s="",n=null,t.forEach(t=>{var e=t.text;s.endsWith(" ")||e.startsWith(" ")||(n?t.x-n.x-n.width>5&&(s+=" "):i(t.text)&&(e+=" ")),s+=e,n=t}),s).split(" "),r=e?D[e]:null;return o.filter(t=>t.trim().length>0).map(t=>{var e=null;return t.startsWith("http:")?(this.containLinks=!0,e=M.LINK):t.startsWith("www.")&&(this.containLinks=!0,t=`http://${t}`,e=M.LINK),r&&this.formattedWords++,new S({string:t,type:e,format:r})})}}class U 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(A(e)),a=A(s),i={};t.pages.forEach(t=>{var e;t.items.forEach(t=>{if(t.height===r&&t.text.trim().length>0){if(e&&t.y!==e.y){const s=e.y-t.y;s>0&&(i[s]=i[s]?i[s]+1:1)}e=t}else e=null})});const h=parseInt(A(i)),l=[],c=new Map;this.fontMap?.forEach(function(t,e){l.push(e+" = "+t.name);const s=t.name.toLowerCase();var o=null;e===a?o=null:s.includes("bold")&&(s.includes("oblique")||s.includes("italic"))?o=D.BOLD_OBLIQUE:s.includes("bold")?o=D.BOLD:s.includes("oblique")||s.includes("italic")?o=D.OBLIQUE:s===n&&(o=D.BOLD),o&&c.set(e,o.name)}),l.sort();const m=t.pages.map(t=>({...t,items:t.items.map(t=>({...t}))}));return new 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}class P extends x{constructor(t){if(super(t,k.name),this.constructor===P)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 W{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}}const j={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}j.enumValueOf=function(t){return j[t]},j.isHeadline=function(t){return t&&2===t.name.length&&"H"===t.name[0]},j.blockToText=function(t){return t.type?t.type.toText(t):$(t.items,!1)},j.headlineByLevel=function(t){return 1===t?j.H1:2===t?j.H2:3===t?j.H3:4===t?j.H4:5===t?j.H5:(t>6&&console.warn("Unsupported headline level: "+t+" (supported are 1-6), defaulting to level 6"),j.H6)};class q 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=j.FOOTNOTES;const e=i.parsedElements.footnotes.map(e=>({footnote:e,page:t.index+1}));n.push.apply(n,e)}}),t.items=e}}),new I({...t,messages:["Detected "+a+" formatted words","Found "+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 s=0;s<t.length;s++){const o=t.charCodeAt(s);n(o)||32===o||160===o||(e=(e<<5)-e+o,e|=0)}return e}class R 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]})}}class V 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"]})}}class K 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)}}class J{headlineCharCodes;stackedLineItems;stackedChars;constructor(t){this.headlineCharCodes=a(t.headline),this.stackedLineItems=[],this.stackedChars=0}consume(t){const e=a(t.text());if(this.matchAll(e)){if(this.stackedLineItems.push(t),this.stackedChars+=e.length,this.stackedChars===this.headlineCharCodes.length)return this.stackedLineItems}else this.stackedChars>0&&(this.stackedChars=0,this.stackedLineItems=[],this.consume(t));return null}matchAll(t){for(var e=0;e<t.length;e++){const s=this.headlineCharCodes[this.stackedChars+e];if(t[e]!==s)return!1}return!0}}class Q extends P{constructor(){super("Detect TOC")}transform(t){const e=[],s=Math.min(20,t.pages.length),a=new G;var i,h,c=[];t.pages.slice(0,s).forEach(t=>{var s=0;const l=new Set,m=[];var u,f;if(t.items.filter(t=>t.words.includes(t=>r(t.string,"."))).forEach(t=>{var e=t.words.filter(t=>!r(t.string,"."));const a=[];for(;e.length>0&&o(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(;n(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})})),s++):h?(u&&l.add(f),u=e,f=t):h=t}),100*s/t.items.length>75){e.push(t.index+1),i=t,a.levelPageItems(m),c.push(...m);const s=[];t.items.forEach(t=>{l.has(t)||(t.annotation=T),s.push(t),t===h&&s.push(new k({...t,type:j.H2,annotation:E}))}),t.items=s}else h=null});var m=c.length;const u=[],f=[],p={};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:j.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=j.headlineByLevel(e.level+2),r=s.headlineItems.reduce((t,e)=>Math.max(t,e.height),0);t.items.splice(s.lineIndex+1,0,new k({...s.headlineItems[0],words:e.lineItem.words,height:r,type:o,annotation: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,p):u.push(s)});var d=i.index+2,g=[];const s=e=>{g.length>0&&(g.forEach(s=>{const n=j.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=>{if(!t.type&&!t.annotation&&t.height>=s.min&&t.height<=s.max){return l(r,t.text())>=.5}return!1});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})),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 _(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}class G{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},[])}}class X{lineItem;pageNumber;level;constructor(t){this.lineItem=t.lineItem,this.pageNumber=t.pageNumber,this.level=0}}class Z 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(i(t.words[0].string))if(e++,"-"===t.words[0].string)t.annotation=b,t.type=j.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:j.LIST}))}else/^[\s]*[\d]*[.][\s].*$/g.test(o)&&(s++,t.annotation=b,t.type=j.LIST)}}),t.items=n}),new I({...t,messages:["Detected "+e+" plain list items.","Detected "+s+" numbered list items."]})}}class tt extends P{constructor(){super("Detect Headers")}transform(t){const{tocPages:e,headlineTypeToHeightRange:s,mostUsedHeight:n,mostUsedDistance:o,mostUsedFont:r,maxHeight:a}=t.globals,i=e.length>0;var l=0;const c=function(t,e){const s=new Set;return t.forEach(t=>{t.items.forEach(n=>{n.type||n.height!==e||s.add(t)})}),s}(t.pages,a),m=n+(a-n)/4;if(c.forEach(t=>{t.items.forEach(t=>{const e=t.height;!t.type&&e>m&&(t.type=e===a?j.H1:j.H2,t.annotation=b,l++)})}),i){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=j.enumValueOf(e),l++)})})})}else{const e=[];t.pages.forEach(t=>{t.items.forEach(t=>{!t.type&&t.height>n&&!h(t.text())&&(e.includes(t.height)||e.push(t.height))})}),e.sort((t,e)=>e-t),e.forEach((e,s)=>{if(s+2<=6){const n=j.headlineByLevel(2+s);t.pages.forEach(t=>{t.items.forEach(t=>{t.type||t.height!==e||h(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=j.headlineByLevel(u+1);t.pages.forEach(t=>{var s;t.items.forEach(t=>{!t.type&&t.height===n&&t.font!==r&&(!s||s.y<t.y||s.type&&s.type.headline||s.y-t.y>2*o)&&t.text()===t.text().toUpperCase()&&(l++,t.annotation=b,t.type=e),s=t})})}return new I({...t,messages:["Detected "+l+" headlines."]})}}class et extends d{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)}}class st extends x{constructor(t){if(super(t,et.name),this.constructor===st)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 nt 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);if(o>r)return!0;return!1}(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"]})}}class ot 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=j.CODE,s++)})}),new I({...t,messages:["Detected "+s+" code/quote items."]})}}class rt extends st{constructor(){super("Level Lists")}transform(t){var e=0,s=0;return t.pages.forEach(t=>{t.items.filter(t=>t.type===j.LIST).forEach(t=>{var n,o=0;const r={};var a=!1;t.items.forEach(t=>{n?t.x>n?(o++,r[t.x]=o):t.x<n&&(o=r[t.x]):r[t.x]=0,o>0&&(t.words=[new S({string:" ".repeat(3*o)})].concat(t.words),a=!0),n=t.x}),e++,a?(s++,t.annotation=O):t.annotation=v})}),new I({...t,messages:["Modified "+s+" / "+e+" list blocks."]})}}class at extends x{constructor(){super("To Text Blocks","TextBlock")}transform(t){return t.pages.forEach(t=>{const e=[];t.items.forEach(t=>{const s=t.type?t.type.name:"Unknown";e.push({category:s,text:j.blockToText(t)})}),t.items=e}),new I({...t})}}class it extends x{constructor(){super("To HTML","String")}transform(t){return t.pages.forEach(t=>{var e="";t.items.forEach(t=>{let s;s="TOC"===t.category?t.text:t.text.replace(/(\r\n|\n|\r)/gm,"\n"),"LIST"!==t.category&&(s=s.split("- ").join("")),"CODE"===t.category&&(s=s.split("<code>").join("").split("</code>").join("")),e+=`<p>${s}</p>\n\n`}),t.items=[e]}),new I({...t})}}exports.convertPDFToHTML=async function(e,s={}){var{addPageNumbers:n=!1,addCitation:o=!0}=s,r="string"==typeof e?await t(e,{responseType:"arraybuffer",timeout:10}):e;let a;try{let{resolvePDFJS:t}=await import("https://cdn.jsdelivr.net/npm/pdfjs-serverless@1.1.0/+esm");const{getDocument:e}=await t();a=await e({data:new Uint8Array(r),useSystemFonts:!0,verbosity:0}).promise}catch(b){return{error:b.message}}const i=[...Array(a.numPages).keys()].map(t=>new y({index:t}));let h,l={};for(let t=1;t<=a.numPages;t++){const e=await a.getPage(t),s=await e.getTextContent();if(!(Object.keys(l).length<10)){h=f(l);break}l=u(l,e.pageNumber-1,s.items)}let c=h?h.pageNum:0;for(let t=1;t<=a.numPages;t++){const e=await a.getPage(t);await e.getOperatorList();const s=1,n=e.getViewport({scale:s});let o=await e.getTextContent();h&&e.pageIndex>=h.pageIndex&&(o=p(o,c),c++);const r=o.items.map(t=>{const e=[1,0,0,1,0,0];for(let r=0;r<6;r++)e[r]+=t.transform[r]*n.transform[r%2?3:0],r%2&&(e[r+1]+=t.transform[r]*n.transform[1]);const s=Math.sqrt(e[2]*e[2]+e[3]*e[3]),o=t.height/s;return new g({x:Math.round(t.transform[4]),y:Math.round(t.transform[5]),width:Math.round(t.width),height:Math.round(o<=1?t.height:o),text:t.str,font:t.fontName})});i[e.pageNumber-1].items=r}var m=new I({pages:i});let d,x=[new U,new q,new R,new V,new Q,new tt,new Z,new nt,new ot,new rt,new at,new it];x?.forEach(t=>{d&&(m=d.completeTransform(m)),m=t.transform(m),d=t});var w=m.pages.reduce((t,e,s)=>t+`<p id="page-${s+1}">${n?` [${s+1}] `:""}${e.items.join('</p><p id="page-'+s+'">')}</p>`,"");if(o){var E=await a.getMetadata(),{Author:T,Title:v}=E.info;v=w.slice(0,400).match(/<h[0-9]>(.*?)<\/h[0-9]>/)?.[1]||v}return{author:T,title:v,html:w,format:"pdf"}};
@@ -0,0 +1,41 @@
1
+ /**
2
+ * ### Convert PDF to HTML
3
+ * <img src="https://i.imgur.com/6IdNDLP.png" width="350px" />
4
+ *
5
+ * Extracts formatted text from PDF with parsing of linebreaks ,
6
+ * page headers, footnotes, and section headings. Supports fonts, links, bold,
7
+ * italics, lists, headings, headers, footnotes, and Table of Contents,
8
+ * Quotes, and Code Blocks, . Removes repeated headers, links footnote anchors to the footnote,
9
+ * and preserves number of the PDF page with invisible I element.
10
+ *
11
+ * This function uses [pdfjs-serverless](https://github.com/johannschopplich/pdfjs-serverless)
12
+ * to work in more environments than PDF.js-based tools:
13
+ * Cloudflare workers, serverless, node.js, and front-end only.
14
+ * @param {string} pdfURLOrBuffer - URL to a PDF file or buffer from fs.readFile
15
+ * @param {Object} [options]
16
+ * @param {boolean} options.addPageNumbers default=false - Adds # to end of each page
17
+ * @param {boolean} options.removePageHeaders default=true - Removes repeated headers found on each page
18
+ * @returns {string|Object} HTML formatted text
19
+ * @category Extract
20
+ * @author [vtempest (2025)](https://github.com/vtempest),
21
+ * [pdf-to-markdown (2017)](https://github.com/jzillmann/pdf-to-markdown/tree/master),
22
+ * [pdf.js (2012-)](https://github.com/mozilla/pdf.js/releases),
23
+ */
24
+ export declare function convertPDFToHTML(pdfURLOrBuffer: any, options?: {
25
+ addPageNumbers?: boolean;
26
+ addCitation?: boolean;
27
+ }): Promise<{
28
+ error: any;
29
+ author?: undefined;
30
+ title?: undefined;
31
+ html?: undefined;
32
+ format?: undefined;
33
+ } | {
34
+ author: any;
35
+ title: any;
36
+ html: string;
37
+ format: string;
38
+ error?: undefined;
39
+ }>;
40
+
41
+ export { }
@@ -0,0 +1 @@
1
+ import t from"grab-url";function e(t){for(;32===t.charCodeAt(0);)t=t.substring(1,t.length);return t}function s(t){for(;32===t.charCodeAt(t.length-1);)t=t.substring(0,t.length-1);return t}function n(t){return t>=48&&t<=57}function o(t){for(var e=0;e<t.length;e++){if(!n(t.charCodeAt(e)))return!1}return!0}function r(t,e){const s=e.charCodeAt(0);for(var n=0;n<t.length;n++){if(t.charCodeAt(n)!==s)return!1}return!0}function a(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 i(t){if(t.length>1)return!1;const e=t.charAt(0);return"-"===e||"•"===e||"–"===e}function h(t){return/^[\s]*[-•–][\s].*$/g.test(t)}function l(t,e){const s=new Set(t.toUpperCase().split(" ")),n=new Set(e.toUpperCase().split(" "));return new Set([...s].filter(t=>n.has(t))).size/Math.max(s.size,n.size)}const c=(t,e,s)=>Math.floor(t/e*s),m=(t,n,r)=>{for(const{str:a}of t){const t=s(e(a));o(t)&&(n[r]||(n[r]=[]),n[r].push(Number(t)))}return n},u=(t,e,s)=>{const n=c(1,6,s.length),o=c(5,6,s.length),r=m(s.slice(0,n),t,e);return m(s.slice(o),r,e)},f=t=>{let e=0;const s=Object.keys(t);if(0!==s.length&&1!==s.length)for(let n=0;n<s.length-1;n++){const o=t[s[n]],r=t[s[n+1]],a=e;for(let t=0;t<o.length&&e<2;t++)for(let a=0;a<r.length&&e<2;a++){const i=Number(s[n+1])-Number(s[n]);(o[t]+1===r[a]||i>1&&o[t]+i===r[a])&&e++}let i=n>0?Object.entries(t)[n-1]:Object.entries(t)[n];if(a===e)e=0,i=Object.entries(t)[n];else if(e>=2)return{pageIndex:Number(i[0]),pageNum:i[1][0]}}},d=(t,e)=>{const s={items:[...t.items]},n=c(1,6,s.items.length),o=c(5,6,s.items.length);return s.items=s.items.filter((t,r)=>{const a=r>0&&r<n,i=r>o&&r<s.items.length;return a||i?Number(t.str)!==Number(e):t}),s};class p{type;annotation;parsedElements;constructor(t){if(this.constructor===p)throw new TypeError("Can not construct abstract class.");this.type=t.type??null,this.annotation=t.annotation??null,this.parsedElements=t.parsedElements??null}}class g 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}}class y{index;items;constructor(t){this.index=t.index,this.items=t.items||[]}}class x{name;itemType;constructor(t,e){if(this.constructor===x)throw new TypeError("Can not construct abstract class.");if(this.transform===x.prototype.transform)throw new TypeError("Please implement abstract method 'transform()'.");this.name=t,this.itemType=e}transform(t){throw new TypeError("Do not call abstract method foo from child.")}completeTransform(t){return t.messages=[],t}}class w{category;color;constructor(t){this.category=t.category,this.color=t.color}}const E=new w({category:"Added",color:"green"}),T=new w({category:"Removed",color:"red"}),v=new w({category:"Unchanged",color:"brown"}),b=new w({category:"Detected",color:"green"}),L=new w({category:"Modified",color:"green"});class I extends x{constructor(t){if(super(t,g.name),this.constructor===I)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 O{pages;globals;messages;constructor(t){this.pages=t.pages||[],this.globals=t.globals||{},this.messages=t.messages||[]}}class S{string;type;format;constructor(t){this.string=t.string,this.type=t.type??null,this.format=t.format??null}}class k 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 S({string:t})))}text(){return this.wordStrings().join(" ")}wordStrings(){return this.words.map(t=>t.string)}}class C{results;stash;constructor(){if(this.constructor===C)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)}}class F{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)}const 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>`}};class M{fontToFormats;constructor(t){this.fontToFormats=t}compact(t){N(t);const e=new B(this.fontToFormats);e.consumeAll(t.map(t=>new g({...t})));const s=e.complete();var n=0,o=0;return t.forEach(t=>{n=Math.max(n,t.height),o+=t.width}),new k({x:t[0].x,y:t[0].y,height:n,width:o,words:s,parsedElements:new F({footnoteLinks:e.footnoteLinks,footnotes:e.footnotes,containLinks:e.containLinks,formattedWords:e.formattedWords})})}}class B 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=o(t.text.trim())}doMatchesStash(t,e){if(this.fontToFormats.get(t.font)!==this.fontToFormats.get(e.font))return!1;const s=o(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){var s,n;const o=(s="",n=null,t.forEach(t=>{var e=t.text;s.endsWith(" ")||e.startsWith(" ")||(n?t.x-n.x-n.width>5&&(s+=" "):i(t.text)&&(e+=" ")),s+=e,n=t}),s).split(" "),r=e?D[e]:null;return o.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),r&&this.formattedWords++,new S({string:t,type:e,format:r})})}}class U extends I{fontMap;constructor(t){super("Calculate Global Stats"),this.fontMap=t}transform(t){const e={},s={};var n,o=0;t.pages.forEach(t=>{t.items.forEach(t=>{t.height&&(e[t.height]=e[t.height]?e[t.height]+1:1,s[t.font]=s[t.font]?s[t.font]+1:1,t.height>o&&(o=t.height,n=t.font))})});const r=parseInt(A(e)),a=A(s),i={};t.pages.forEach(t=>{var e;t.items.forEach(t=>{if(t.height===r&&t.text.trim().length>0){if(e&&t.y!==e.y){const s=e.y-t.y;s>0&&(i[s]=i[s]?i[s]+1:1)}e=t}else e=null})});const h=parseInt(A(i)),l=[],c=/* @__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 O({...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}class W extends x{constructor(t){if(super(t,k.name),this.constructor===W)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 P{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}}const $={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)};class q extends W{constructor(){super("Compact To Lines")}transform(t){const{mostUsedDistance:e,fontToFormats:s}=t.globals,n=[],o=[];var r=0,a=0;const i=new P({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=$.FOOTNOTES;const e=i.parsedElements.footnotes.map(e=>({footnote:e,page:t.index+1}));n.push.apply(n,e)}}),t.items=e}}),new O({...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 s=0;s<t.length;s++){const o=t.charCodeAt(s);n(o)||32===o||160===o||(e=(e<<5)-e+o,e|=0)}return e}class R extends W{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 O({...t,messages:["Removed Header: "+o,"Removed Footers: "+r]})}}class V extends W{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 O({...t,messages:["Converted "+e+" verticals"]})}}class K 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)}}class J{headlineCharCodes;stackedLineItems;stackedChars;constructor(t){this.headlineCharCodes=a(t.headline),this.stackedLineItems=[],this.stackedChars=0}consume(t){const e=a(t.text());if(this.matchAll(e)){if(this.stackedLineItems.push(t),this.stackedChars+=e.length,this.stackedChars===this.headlineCharCodes.length)return this.stackedLineItems}else this.stackedChars>0&&(this.stackedChars=0,this.stackedLineItems=[],this.consume(t));return null}matchAll(t){for(var e=0;e<t.length;e++){const s=this.headlineCharCodes[this.stackedChars+e];if(t[e]!==s)return!1}return!0}}class Q extends W{constructor(){super("Detect TOC")}transform(t){const e=[],s=Math.min(20,t.pages.length),a=new G;var i,h,c=[];t.pages.slice(0,s).forEach(t=>{var s=0;const l=/* @__PURE__ */new Set,m=[];var u,f;if(t.items.filter(t=>t.words.includes(t=>r(t.string,"."))).forEach(t=>{var e=t.words.filter(t=>!r(t.string,"."));const a=[];for(;e.length>0&&o(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(;n(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})})),s++):h?(u&&l.add(f),u=e,f=t):h=t}),100*s/t.items.length>75){e.push(t.index+1),i=t,a.levelPageItems(m),c.push(...m);const s=[];t.items.forEach(t=>{l.has(t)||(t.annotation=T),s.push(t),t===h&&s.push(new k({...t,type:$.H2,annotation:E}))}),t.items=s}else h=null});var m=c.length;const u=[],f=[],d={};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,d):u.push(s)});var p=i.index+2,g=[];const s=e=>{g.length>0&&(g.forEach(s=>{const n=$.headlineByLevel(s.level+2),o=d[n.name];if(o){const[r,a]=function(t,e,s,n,o){const r=e.lineItem.text().toUpperCase();for(var a=n;a<=o;a++){const e=t[a-1];if(e){const t=e.items.findIndex(t=>{if(!t.type&&!t.annotation&&t.height>=s.min&&t.height<=s.max){return l(r,t.text())>=.5}return!1});if(t>-1)return[a-1,t]}}return[-1,-1]}(t.pages,s,o,p,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})),f.push(s)}}}),g=[])};u.length>0&&(c.forEach(t=>{u.includes(t)?g.push(t):(s(t.pageNumber),p=t.pageNumber)}),g.length>0&&s(t.pages.length))}const y=[];return y.push("Detected "+e.length+" table of content pages"),e.length>0&&(y.push("TOC headline heights: "+JSON.stringify(d)),y.push("Found TOC headlines: "+(m-u.length+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 O({...t,globals:{...t.globals,tocPages:e,headlineTypeToHeightRange:d},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}class G{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},[])}}class X{lineItem;pageNumber;level;constructor(t){this.lineItem=t.lineItem,this.pageNumber=t.pageNumber,this.level=0}}class Z extends W{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(i(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 O({...t,messages:["Detected "+e+" plain list items.","Detected "+s+" numbered list items."]})}}class tt extends W{constructor(){super("Detect Headers")}transform(t){const{tocPages:e,headlineTypeToHeightRange:s,mostUsedHeight:n,mostUsedDistance:o,mostUsedFont:r,maxHeight:a}=t.globals,i=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?$.H1:$.H2,t.annotation=b,l++)})}),i){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&&!h(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||h(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 O({...t,messages:["Detected "+l+" headlines."]})}}class et 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)}}class st extends x{constructor(t){if(super(t,et.name),this.constructor===st)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 nt 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);if(o>r)return!0;return!1}(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 O({...t,messages:["Gathered "+s+" blocks out of "+n+" line items"]})}}class ot 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 O({...t,messages:["Detected "+s+" code/quote items."]})}}class rt 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 O({...t,messages:["Modified "+s+" / "+e+" list blocks."]})}}class at extends x{constructor(){super("To Text Blocks","TextBlock")}transform(t){return t.pages.forEach(t=>{const e=[];t.items.forEach(t=>{const s=t.type?t.type.name:"Unknown";e.push({category:s,text:$.blockToText(t)})}),t.items=e}),new O({...t})}}class it extends x{constructor(){super("To HTML","String")}transform(t){return t.pages.forEach(t=>{var e="";t.items.forEach(t=>{let s;s="TOC"===t.category?t.text:t.text.replace(/(\r\n|\n|\r)/gm,"\n"),"LIST"!==t.category&&(s=s.split("- ").join("")),"CODE"===t.category&&(s=s.split("<code>").join("").split("</code>").join("")),e+=`<p>${s}</p>\n\n`}),t.items=[e]}),new O({...t})}}async function ht(e,s={}){var{addPageNumbers:n=!1,addCitation:o=!0}=s,r="string"==typeof e?await t(e,{responseType:"arraybuffer",timeout:10}):e;let a;try{let{resolvePDFJS:t}=await import("https://cdn.jsdelivr.net/npm/pdfjs-serverless@1.1.0/+esm");const{getDocument:e}=await t();a=await e({data:new Uint8Array(r),useSystemFonts:!0,verbosity:0}).promise}catch(b){return{error:b.message}}const i=[...Array(a.numPages).keys()].map(t=>new y({index:t}));let h,l={};for(let t=1;t<=a.numPages;t++){const e=await a.getPage(t),s=await e.getTextContent();if(!(Object.keys(l).length<10)){h=f(l);break}l=u(l,e.pageNumber-1,s.items)}let c=h?h.pageNum:0;for(let t=1;t<=a.numPages;t++){const e=await a.getPage(t);await e.getOperatorList();const s=1,n=e.getViewport({scale:s});let o=await e.getTextContent();h&&e.pageIndex>=h.pageIndex&&(o=d(o,c),c++);const r=o.items.map(t=>{const e=[1,0,0,1,0,0];for(let r=0;r<6;r++)e[r]+=t.transform[r]*n.transform[r%2?3:0],r%2&&(e[r+1]+=t.transform[r]*n.transform[1]);const s=Math.sqrt(e[2]*e[2]+e[3]*e[3]),o=t.height/s;return new g({x:Math.round(t.transform[4]),y:Math.round(t.transform[5]),width:Math.round(t.width),height:Math.round(o<=1?t.height:o),text:t.str,font:t.fontName})});i[e.pageNumber-1].items=r}var m=new O({pages:i});let p,x=[new U,new q,new R,new V,new Q,new tt,new Z,new nt,new ot,new rt,new at,new it];x?.forEach(t=>{p&&(m=p.completeTransform(m)),m=t.transform(m),p=t});var w=m.pages.reduce((t,e,s)=>t+`<p id="page-${s+1}">${n?` [${s+1}] `:""}${e.items.join('</p><p id="page-'+s+'">')}</p>`,"");if(o){var E=await a.getMetadata(),{Author:T,Title:v}=E.info;v=w.slice(0,400).match(/<h[0-9]>(.*?)<\/h[0-9]>/)?.[1]||v}return{author:T,title:v,html:w,format:"pdf"}}export{ht as convertPDFToHTML};
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "extract-pdf",
3
+ "version": "0.1.0",
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
+ "author": "vtempest <grokthiscontact@gmail.com>",
6
+ "license": "rights.institute/PROSPER",
7
+ "type": "module",
8
+ "main": "./dist/pdf-to-html.cjs.js",
9
+ "module": "./dist/pdf-to-html.es.js",
10
+ "types": "./dist/pdf-to-html.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/pdf-to-html.d.ts",
14
+ "import": "./dist/pdf-to-html.es.js",
15
+ "require": "./dist/pdf-to-html.cjs.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "pdf-to-html.ts",
21
+ "models",
22
+ "transforms",
23
+ "utils"
24
+ ],
25
+ "scripts": {
26
+ "build": "vite build",
27
+ "ship": "npm run build && npx standard-version --release-as patch; rm -f CHANGELOG.md; npm publish"
28
+ },
29
+ "dependencies": {
30
+ "grab-url": "^1.6.19"
31
+ },
32
+ "devDependencies": {
33
+ "terser": "^5.46.0",
34
+ "typescript": "^5.9.3",
35
+ "vite": "^7.3.1",
36
+ "vite-plugin-dts": "^4.5.4"
37
+ },
38
+ "keywords": [
39
+ "pdf",
40
+ "html",
41
+ "pdf-parser",
42
+ "pdfjs",
43
+ "pdf-to-text",
44
+ "document-extraction"
45
+ ],
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/vtempest/qwksearch-research-agent"
49
+ }
50
+ }