extract-pdf 0.1.0 → 0.1.2
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 +16 -10
- package/dist/models/annotation.d.ts +20 -0
- package/dist/models/block-type.d.ts +10 -0
- package/dist/models/headline-finder.d.ts +11 -0
- package/dist/models/line-converter.d.ts +10 -0
- package/dist/models/line-item-block.d.ts +14 -0
- package/dist/models/line-item.d.ts +25 -0
- package/dist/models/metadata.d.ts +23 -0
- package/dist/models/page-item.d.ts +21 -0
- package/dist/models/page.d.ts +14 -0
- package/dist/models/parse-result.d.ts +24 -0
- package/dist/models/parsed-elements.d.ts +20 -0
- package/dist/models/stashing-stream.d.ts +23 -0
- package/dist/models/text-item-line-grouper.d.ts +8 -0
- package/dist/models/text-item.d.ts +29 -0
- package/dist/models/word.d.ts +27 -0
- package/dist/pdf-to-html.cjs.js +1 -1
- package/dist/pdf-to-html.d.ts +36 -41
- package/dist/pdf-to-html.es.js +1 -1
- package/dist/transforms/base/to-line-item-block-transform.d.ts +6 -0
- package/dist/transforms/base/to-line-item-transform.d.ts +6 -0
- package/dist/transforms/base/to-text-item-transform.d.ts +6 -0
- package/dist/transforms/base/transformation.d.ts +8 -0
- package/dist/transforms/block/detect-code-quote-blocks.d.ts +6 -0
- package/dist/transforms/block/detect-list-levels.d.ts +6 -0
- package/dist/transforms/block/gather-blocks.d.ts +6 -0
- package/dist/transforms/calculate-global-stats.d.ts +11 -0
- package/dist/transforms/line-item/compact-lines.d.ts +6 -0
- package/dist/transforms/line-item/detect-headers.d.ts +6 -0
- package/dist/transforms/line-item/detect-list-items.d.ts +6 -0
- package/dist/transforms/line-item/detect-toc.d.ts +6 -0
- package/dist/transforms/line-item/remove-repetitive-elements.d.ts +6 -0
- package/dist/transforms/line-item/vertical-to-horizontal.d.ts +6 -0
- package/dist/transforms/to-html.d.ts +6 -0
- package/dist/transforms/to-text-blocks.d.ts +6 -0
- package/dist/utils/is-url-pdf.d.ts +1 -0
- package/dist/utils/page-item-functions.d.ts +8 -0
- package/dist/utils/page-number-functions.d.ts +14 -0
- package/dist/utils/string-functions.d.ts +14 -0
- package/package.json +8 -11
- package/src/models/annotation.ts +41 -0
- package/src/models/block-type.ts +203 -0
- package/src/models/headline-finder.ts +53 -0
- package/src/models/line-converter.ts +224 -0
- package/src/models/line-item-block.ts +51 -0
- package/src/models/line-item.ts +59 -0
- package/src/models/metadata.ts +29 -0
- package/src/models/page-item.ts +36 -0
- package/src/models/page.ts +16 -0
- package/src/models/parse-result.ts +32 -0
- package/src/models/parsed-elements.ts +29 -0
- package/src/models/stashing-stream.ts +86 -0
- package/src/models/text-item-line-grouper.ts +41 -0
- package/src/models/text-item.ts +50 -0
- package/src/models/word.ts +31 -0
- package/src/pdf-to-html.ts +225 -0
- package/src/transforms/base/to-line-item-block-transform.ts +29 -0
- package/src/transforms/base/to-line-item-transform.ts +29 -0
- package/src/transforms/base/to-text-item-transform.ts +28 -0
- package/src/transforms/base/transformation.ts +35 -0
- package/src/transforms/block/detect-code-quote-blocks.ts +57 -0
- package/src/transforms/block/detect-list-levels.ts +64 -0
- package/src/transforms/block/gather-blocks.ts +113 -0
- package/src/transforms/calculate-global-stats.ts +132 -0
- package/src/transforms/line-item/compact-lines.ts +92 -0
- package/src/transforms/line-item/detect-headers.ts +173 -0
- package/src/transforms/line-item/detect-list-items.ts +68 -0
- package/src/transforms/line-item/detect-toc.ts +459 -0
- package/src/transforms/line-item/remove-repetitive-elements.ts +101 -0
- package/src/transforms/line-item/vertical-to-horizontal.ts +90 -0
- package/src/transforms/to-html.ts +46 -0
- package/src/transforms/to-text-blocks.ts +38 -0
- package/src/utils/is-url-pdf.ts +33 -0
- package/src/utils/page-item-functions.ts +35 -0
- package/src/utils/page-number-functions.ts +109 -0
- package/src/utils/string-functions.ts +124 -0
package/dist/pdf-to-html.es.js
CHANGED
|
@@ -1 +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};
|
|
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};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as Transformation } from './transformation';
|
|
2
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
3
|
+
export default class ToLineItemBlockTransformation extends Transformation {
|
|
4
|
+
constructor(name: string);
|
|
5
|
+
completeTransform(parseResult: ParseResult): ParseResult;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as Transformation } from './transformation';
|
|
2
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
3
|
+
export default class ToLineItemTransformation extends Transformation {
|
|
4
|
+
constructor(name: string);
|
|
5
|
+
completeTransform(parseResult: ParseResult): ParseResult;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as Transformation } from './transformation';
|
|
2
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
3
|
+
export default class ToTextItemTransformation extends Transformation {
|
|
4
|
+
constructor(name: string);
|
|
5
|
+
completeTransform(parseResult: ParseResult): ParseResult;
|
|
6
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
2
|
+
export default class Transformation {
|
|
3
|
+
name: string;
|
|
4
|
+
itemType: string;
|
|
5
|
+
constructor(name: string, itemType: string);
|
|
6
|
+
transform(parseResult: ParseResult): ParseResult;
|
|
7
|
+
completeTransform(parseResult: ParseResult): ParseResult;
|
|
8
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as ToLineItemBlockTransformation } from '../base/to-line-item-block-transform';
|
|
2
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
3
|
+
export default class DetectCodeQuoteBlocks extends ToLineItemBlockTransformation {
|
|
4
|
+
constructor();
|
|
5
|
+
transform(parseResult: ParseResult): ParseResult;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as ToLineItemBlockTransformation } from '../base/to-line-item-block-transform';
|
|
2
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
3
|
+
export default class DetectListLevels extends ToLineItemBlockTransformation {
|
|
4
|
+
constructor();
|
|
5
|
+
transform(parseResult: ParseResult): ParseResult;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as ToLineItemBlockTransformation } from '../base/to-line-item-block-transform';
|
|
2
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
3
|
+
export default class GatherBlocks extends ToLineItemBlockTransformation {
|
|
4
|
+
constructor();
|
|
5
|
+
transform(parseResult: ParseResult): ParseResult;
|
|
6
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { default as ToTextItemTransformation } from './base/to-text-item-transform';
|
|
2
|
+
import { default as ParseResult } from '../models/parse-result';
|
|
3
|
+
export default class CalculateGlobalStats extends ToTextItemTransformation {
|
|
4
|
+
fontMap: Map<string, {
|
|
5
|
+
name: string;
|
|
6
|
+
}> | undefined;
|
|
7
|
+
constructor(fontMap?: Map<string, {
|
|
8
|
+
name: string;
|
|
9
|
+
}>);
|
|
10
|
+
transform(parseResult: ParseResult): ParseResult;
|
|
11
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as ToLineItemTransformation } from '../base/to-line-item-transform';
|
|
2
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
3
|
+
export default class CompactLines extends ToLineItemTransformation {
|
|
4
|
+
constructor();
|
|
5
|
+
transform(parseResult: ParseResult): ParseResult;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as ToLineItemTransformation } from '../base/to-line-item-transform';
|
|
2
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
3
|
+
export default class DetectHeaders extends ToLineItemTransformation {
|
|
4
|
+
constructor();
|
|
5
|
+
transform(parseResult: ParseResult): ParseResult;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as ToLineItemTransformation } from '../base/to-line-item-transform';
|
|
2
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
3
|
+
export default class DetectListItems extends ToLineItemTransformation {
|
|
4
|
+
constructor();
|
|
5
|
+
transform(parseResult: ParseResult): ParseResult;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as ToLineItemTransformation } from '../base/to-line-item-transform';
|
|
2
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
3
|
+
export default class DetectTOC extends ToLineItemTransformation {
|
|
4
|
+
constructor();
|
|
5
|
+
transform(parseResult: ParseResult): ParseResult;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as ToLineItemTransformation } from '../base/to-line-item-transform';
|
|
2
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
3
|
+
export default class RemoveRepetitiveElements extends ToLineItemTransformation {
|
|
4
|
+
constructor();
|
|
5
|
+
transform(parseResult: ParseResult): ParseResult;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { default as ToLineItemTransformation } from '../base/to-line-item-transform';
|
|
2
|
+
import { default as ParseResult } from '../../models/parse-result';
|
|
3
|
+
export default class VerticalToHorizontal extends ToLineItemTransformation {
|
|
4
|
+
constructor();
|
|
5
|
+
transform(parseResult: ParseResult): ParseResult;
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isUrlPDF(url: string): Promise<boolean>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { default as LineItemBlock } from '../models/line-item-block';
|
|
2
|
+
export declare function minXFromBlocks(blocks: LineItemBlock[]): number | null;
|
|
3
|
+
export declare function minXFromPageItems(items: Array<{
|
|
4
|
+
x: number;
|
|
5
|
+
}>): number | null;
|
|
6
|
+
export declare function sortByX(items: Array<{
|
|
7
|
+
x: number;
|
|
8
|
+
}>): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface TextContentItem {
|
|
2
|
+
str: string;
|
|
3
|
+
}
|
|
4
|
+
interface TextContent {
|
|
5
|
+
items: TextContentItem[];
|
|
6
|
+
}
|
|
7
|
+
type PageIndexNumMap = Record<string, number[]>;
|
|
8
|
+
declare const findPageNumbers: (pageIndexNumMap: PageIndexNumMap, pageIndex: number, items: TextContentItem[]) => PageIndexNumMap;
|
|
9
|
+
declare const findFirstPage: (pageIndexNumMap: PageIndexNumMap) => {
|
|
10
|
+
pageIndex: number;
|
|
11
|
+
pageNum: number;
|
|
12
|
+
} | undefined;
|
|
13
|
+
declare const removePageNumber: (textContent: TextContent, pageNum: number) => TextContent;
|
|
14
|
+
export { findPageNumbers, findFirstPage, removePageNumber };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function removeLeadingWhitespaces(string: string): string;
|
|
2
|
+
export declare function removeTrailingWhitespaces(string: string): string;
|
|
3
|
+
export declare function isDigit(charCode: number): boolean;
|
|
4
|
+
export declare function isNumber(string: string): boolean;
|
|
5
|
+
export declare function hasOnly(string: string, char: string): boolean;
|
|
6
|
+
export declare function hasUpperCaseCharacterInMiddleOfWord(text: string): boolean;
|
|
7
|
+
export declare function normalizedCharCodeArray(string: string): number[];
|
|
8
|
+
export declare function charCodeArray(string: string): number[];
|
|
9
|
+
export declare function prefixAfterWhitespace(prefix: string, string: string): string;
|
|
10
|
+
export declare function suffixBeforeWhitespace(string: string, suffix: string): string;
|
|
11
|
+
export declare function isListItemCharacter(string: string): boolean;
|
|
12
|
+
export declare function isListItem(string: string): boolean;
|
|
13
|
+
export declare function isNumberedListItem(string: string): boolean;
|
|
14
|
+
export declare function wordMatch(string1: string, string2: string): number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "extract-pdf",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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",
|
|
@@ -17,23 +17,20 @@
|
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"dist",
|
|
20
|
-
"
|
|
21
|
-
"models",
|
|
22
|
-
"transforms",
|
|
23
|
-
"utils"
|
|
20
|
+
"src"
|
|
24
21
|
],
|
|
25
22
|
"scripts": {
|
|
26
23
|
"build": "vite build",
|
|
24
|
+
"prepare": "vite build",
|
|
25
|
+
"test": "bun test",
|
|
27
26
|
"ship": "npm run build && npx standard-version --release-as patch; rm -f CHANGELOG.md; npm publish"
|
|
28
27
|
},
|
|
29
|
-
"dependencies": {
|
|
30
|
-
"grab-url": "^1.6.19"
|
|
31
|
-
},
|
|
28
|
+
"dependencies": {},
|
|
32
29
|
"devDependencies": {
|
|
33
30
|
"terser": "^5.46.0",
|
|
34
31
|
"typescript": "^5.9.3",
|
|
35
|
-
"vite": "^
|
|
36
|
-
"vite-plugin-dts": "^
|
|
32
|
+
"vite": "^8.1.3",
|
|
33
|
+
"vite-plugin-dts": "^5.0.3"
|
|
37
34
|
},
|
|
38
35
|
"keywords": [
|
|
39
36
|
"pdf",
|
|
@@ -45,6 +42,6 @@
|
|
|
45
42
|
],
|
|
46
43
|
"repository": {
|
|
47
44
|
"type": "git",
|
|
48
|
-
"url": "https://github.com/
|
|
45
|
+
"url": "git+https://github.com/OpenSourceAGI/qwksearch-research-agent.git"
|
|
49
46
|
}
|
|
50
47
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description Diff-annotation markers used to tag document items during pipeline
|
|
3
|
+
* transformations. Each `Annotation` carries a human-readable `category` and a
|
|
4
|
+
* display `color`. Pre-built singletons (`ADDED_ANNOTATION`, `REMOVED_ANNOTATION`,
|
|
5
|
+
* `UNCHANGED_ANNOTATION`, `DETECTED_ANNOTATION`, `MODIFIED_ANNOTATION`) are
|
|
6
|
+
* attached to page items so debug views can highlight what each stage changed.
|
|
7
|
+
*/
|
|
8
|
+
export default class Annotation {
|
|
9
|
+
category: string;
|
|
10
|
+
color: string;
|
|
11
|
+
|
|
12
|
+
constructor(options: { category: string; color: string }) {
|
|
13
|
+
this.category = options.category;
|
|
14
|
+
this.color = options.color;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ADDED_ANNOTATION = new Annotation({
|
|
19
|
+
category: "Added",
|
|
20
|
+
color: "green",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const REMOVED_ANNOTATION = new Annotation({
|
|
24
|
+
category: "Removed",
|
|
25
|
+
color: "red",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const UNCHANGED_ANNOTATION = new Annotation({
|
|
29
|
+
category: "Unchanged",
|
|
30
|
+
color: "brown",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const DETECTED_ANNOTATION = new Annotation({
|
|
34
|
+
category: "Detected",
|
|
35
|
+
color: "green",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const MODIFIED_ANNOTATION = new Annotation({
|
|
39
|
+
category: "Modified",
|
|
40
|
+
color: "green",
|
|
41
|
+
});
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description Enum-like registry of semantic document block types (H1–H6, TOC,
|
|
3
|
+
* FOOTNOTES, CODE, LIST, PARAGRAPH). Each entry carries a `toText()` serializer
|
|
4
|
+
* that converts a `LineItemBlock` to HTML. Helper methods `isHeadline`,
|
|
5
|
+
* `blockToText`, and `headlineByLevel` are mixed in on the registry object.
|
|
6
|
+
* The inner `linesToText` function handles inline bold/italic/link/footnote
|
|
7
|
+
* formatting by tracking open `WordFormat` spans across words and lines.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const BlockTypes: Record<string, any> = {
|
|
11
|
+
H1: {
|
|
12
|
+
name: "H1",
|
|
13
|
+
headline: true,
|
|
14
|
+
headlineLevel: 1,
|
|
15
|
+
toText(block /*: LineItemBlock */) /*: string */ {
|
|
16
|
+
return "<h1>" + linesToText(block.items, true) + "</h1>";
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
H2: {
|
|
20
|
+
name: "H2",
|
|
21
|
+
headline: true,
|
|
22
|
+
headlineLevel: 2,
|
|
23
|
+
toText(block /*: LineItemBlock */) /*: string */ {
|
|
24
|
+
return "<h2>" + linesToText(block.items, true) + "</h2>";
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
H3: {
|
|
28
|
+
name: "H3",
|
|
29
|
+
headline: true,
|
|
30
|
+
headlineLevel: 3,
|
|
31
|
+
toText(block /*: LineItemBlock */) /*: string */ {
|
|
32
|
+
return "<h3>" + linesToText(block.items, true) + "</h3>";
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
H4: {
|
|
36
|
+
name: "H4",
|
|
37
|
+
headline: true,
|
|
38
|
+
headlineLevel: 4,
|
|
39
|
+
toText(block /*: LineItemBlock */) /*: string */ {
|
|
40
|
+
return "<h4>" + linesToText(block.items, true) + "</h4>";
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
H5: {
|
|
44
|
+
name: "H5",
|
|
45
|
+
headline: true,
|
|
46
|
+
headlineLevel: 5,
|
|
47
|
+
toText(block /*: LineItemBlock */) /*: string */ {
|
|
48
|
+
return "<h5>" + linesToText(block.items, true) + "</h5>";
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
H6: {
|
|
52
|
+
name: "H6",
|
|
53
|
+
headline: true,
|
|
54
|
+
headlineLevel: 6,
|
|
55
|
+
toText(block /*: LineItemBlock */) /*: string */ {
|
|
56
|
+
return "<h6>" + linesToText(block.items, true) + "</h6>";
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
TOC: {
|
|
60
|
+
name: "TOC",
|
|
61
|
+
mergeToBlock: true,
|
|
62
|
+
toText(block /*: LineItemBlock */) /*: string */ {
|
|
63
|
+
return linesToText(block.items, true);
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
FOOTNOTES: {
|
|
67
|
+
name: "FOOTNOTES",
|
|
68
|
+
mergeToBlock: true,
|
|
69
|
+
mergeFollowingNonTypedItems: true,
|
|
70
|
+
toText(block /*: LineItemBlock */) /*: string */ {
|
|
71
|
+
return "<p>" + linesToText(block.items, false) + "</p>";
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
CODE: {
|
|
75
|
+
name: "CODE",
|
|
76
|
+
mergeToBlock: true,
|
|
77
|
+
toText(block /*: LineItemBlock */) /*: string */ {
|
|
78
|
+
return "<code>" + linesToText(block.items, true) + "</code>";
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
LIST: {
|
|
82
|
+
name: "LIST",
|
|
83
|
+
mergeToBlock: false,
|
|
84
|
+
mergeFollowingNonTypedItemsWithSmallDistance: true,
|
|
85
|
+
toText(block /*: LineItemBlock */) /*: string */ {
|
|
86
|
+
return "<ul>\n" + linesToText(block.items, false) + "</ul>";
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
PARAGRAPH: {
|
|
90
|
+
name: "PARAGRAPH",
|
|
91
|
+
toText(block /*: LineItemBlock */) /*: string */ {
|
|
92
|
+
return "<p>" + linesToText(block.items, false) + "</p>";
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
function firstFormat(lineItem: { words: Array<{ format: any }> }): any {
|
|
98
|
+
if (lineItem.words.length === 0) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return lineItem.words[0].format;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isPunctationCharacter(string: string): boolean {
|
|
105
|
+
if (string.length !== 1) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return string[0] === "." || string[0] === "!" || string[0] === "?";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function linesToText(lineItems: any[], disableInlineFormats: boolean): string {
|
|
112
|
+
var text = "";
|
|
113
|
+
var openFormat;
|
|
114
|
+
|
|
115
|
+
const closeFormat = () => {
|
|
116
|
+
text += openFormat.endSymbol;
|
|
117
|
+
openFormat = null;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
lineItems.forEach((line, lineIndex) => {
|
|
121
|
+
if (!line) return;
|
|
122
|
+
|
|
123
|
+
line.words.forEach((word, i) => {
|
|
124
|
+
const wordType = word.type;
|
|
125
|
+
const wordFormat = word.format;
|
|
126
|
+
if (openFormat && (!wordFormat || wordFormat !== openFormat)) {
|
|
127
|
+
closeFormat();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (
|
|
131
|
+
i > 0 &&
|
|
132
|
+
!(wordType && wordType.attachWithoutWhitespace) &&
|
|
133
|
+
!isPunctationCharacter(word.string)
|
|
134
|
+
) {
|
|
135
|
+
text += " ";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (wordFormat && !openFormat && !disableInlineFormats) {
|
|
139
|
+
openFormat = wordFormat;
|
|
140
|
+
text += openFormat.startSymbol;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (wordType && (!disableInlineFormats || wordType.plainTextFormat)) {
|
|
144
|
+
text += wordType.toText(word.string);
|
|
145
|
+
} else {
|
|
146
|
+
text += word.string;
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
if (
|
|
150
|
+
openFormat &&
|
|
151
|
+
(lineIndex === lineItems.length - 1 ||
|
|
152
|
+
firstFormat(lineItems[lineIndex + 1]) !== openFormat)
|
|
153
|
+
) {
|
|
154
|
+
closeFormat();
|
|
155
|
+
}
|
|
156
|
+
text += "\n";
|
|
157
|
+
});
|
|
158
|
+
return text;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Make the object immutable
|
|
162
|
+
// Object.freeze(BlockTypes);
|
|
163
|
+
// Object.values(BlockTypes).forEach(value => Object.freeze(value));
|
|
164
|
+
|
|
165
|
+
BlockTypes.enumValueOf = function enumValueOf(name: string) {
|
|
166
|
+
return BlockTypes[name];
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
BlockTypes.isHeadline = function isHeadline(
|
|
170
|
+
type /*: typeof BlockTypes[keyof typeof BlockTypes] */,
|
|
171
|
+
) /*: boolean */ {
|
|
172
|
+
return type && type.name.length === 2 && type.name[0] === "H";
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
BlockTypes.blockToText = function blockToText(
|
|
176
|
+
block /*: LineItemBlock */,
|
|
177
|
+
) /*: string */ {
|
|
178
|
+
if (!block.type) {
|
|
179
|
+
return linesToText(block.items, false);
|
|
180
|
+
}
|
|
181
|
+
return block.type.toText(block);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
BlockTypes.headlineByLevel = function headlineByLevel(level) {
|
|
185
|
+
if (level === 1) return BlockTypes.H1;
|
|
186
|
+
if (level === 2) return BlockTypes.H2;
|
|
187
|
+
if (level === 3) return BlockTypes.H3;
|
|
188
|
+
if (level === 4) return BlockTypes.H4;
|
|
189
|
+
if (level === 5) return BlockTypes.H5;
|
|
190
|
+
|
|
191
|
+
// if level is >= 6, just use BlockType H6
|
|
192
|
+
if (level > 6) {
|
|
193
|
+
// eslint-disable-next-line no-console
|
|
194
|
+
console.warn(
|
|
195
|
+
"Unsupported headline level: " +
|
|
196
|
+
level +
|
|
197
|
+
" (supported are 1-6), defaulting to level 6",
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
return BlockTypes.H6;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export default BlockTypes;
|